package org.apache.struts.action;

import java.io.Serializable;
import java.util.HashMap;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaClass;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.struts.config.FormBeanConfig;
import org.apache.struts.config.FormPropertyConfig;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.util.RequestUtils;

public class DynaActionFormClass implements DynaClass, Serializable {
  protected transient Class beanClass;
  
  protected FormBeanConfig config;
  
  protected String name;
  
  protected DynaProperty[] properties;
  
  protected HashMap propertiesMap;
  
  private DynaActionFormClass(FormBeanConfig config) {
    this.beanClass = null;
    this.config = null;
    this.name = null;
    this.properties = null;
    this.propertiesMap = new HashMap();
    introspect(config);
  }
  
  protected static transient HashMap dynaClasses = new HashMap();
  
  protected static String lock = "";
  
  public String getName() {
    return this.name;
  }
  
  public DynaProperty getDynaProperty(String name) {
    if (name == null)
      throw new IllegalArgumentException("No property name specified"); 
    return (DynaProperty)this.propertiesMap.get(name);
  }
  
  public DynaProperty[] getDynaProperties() {
    return this.properties;
  }
  
  public DynaBean newInstance() throws IllegalAccessException, InstantiationException {
    DynaActionForm dynaBean = getBeanClass().newInstance();
    dynaBean.setDynaActionFormClass(this);
    FormPropertyConfig[] props = this.config.findFormPropertyConfigs();
    for (int i = 0; i < props.length; i++)
      dynaBean.set(props[i].getName(), props[i].initial()); 
    return dynaBean;
  }
  
  public String toString() {
    StringBuffer sb = new StringBuffer("DynaActionFormBean[name=");
    sb.append(this.name);
    DynaProperty[] props = getDynaProperties();
    if (props == null)
      props = new DynaProperty[0]; 
    for (int i = 0; i < props.length; i++) {
      sb.append(',');
      sb.append(props[i].getName());
      sb.append('/');
      sb.append(props[i].getType());
    } 
    sb.append("]");
    return sb.toString();
  }
  
  public static void clear() {
    synchronized (lock) {
      if (dynaClasses == null)
        dynaClasses = new HashMap(); 
      dynaClasses.clear();
    } 
  }
  
  public static DynaActionFormClass createDynaActionFormClass(FormBeanConfig config) {
    synchronized (lock) {
      if (dynaClasses == null)
        dynaClasses = new HashMap(); 
      ModuleConfig moduleConfig = config.getModuleConfig();
      String key = config.getName();
      if (moduleConfig != null)
        key = key + moduleConfig.getPrefix(); 
      DynaActionFormClass dynaClass = (DynaActionFormClass)dynaClasses.get(key);
      if (dynaClass == null) {
        dynaClass = new DynaActionFormClass(config);
        dynaClasses.put(key, dynaClass);
      } 
      return dynaClass;
    } 
  }
  
  protected Class getBeanClass() {
    if (this.beanClass == null)
      introspect(this.config); 
    return this.beanClass;
  }
  
  protected void introspect(FormBeanConfig config) {
    this.config = config;
    try {
      this.beanClass = RequestUtils.applicationClass(config.getType());
    } catch (Throwable t) {
      throw new IllegalArgumentException("Cannot instantiate ActionFormBean class '" + config.getType() + "': " + t);
    } 
    if (!DynaActionForm.class.isAssignableFrom(this.beanClass))
      throw new IllegalArgumentException("Class '" + config.getType() + "' is not a subclass of " + "'org.apache.struts.action.DynaActionForm'"); 
    this.name = config.getName();
    FormPropertyConfig[] descriptors = config.findFormPropertyConfigs();
    if (descriptors == null)
      descriptors = new FormPropertyConfig[0]; 
    this.properties = new DynaProperty[descriptors.length];
    for (int i = 0; i < descriptors.length; i++) {
      this.properties[i] = new DynaProperty(descriptors[i].getName(), descriptors[i].getTypeClass());
      this.propertiesMap.put(this.properties[i].getName(), this.properties[i]);
    } 
  }
}
