/**
   * Create Definition factory from provided classname. If a factory class name is provided, a
   * factory of this class is created. Otherwise, a default factory is created. Factory must have a
   * constructor taking ServletContext and Map as parameter.
   *
   * @param classname Class name of the factory to create.
   * @param servletContext Servlet Context passed to newly created factory.
   * @param properties Map of name/property passed to newly created factory.
   * @return newly created factory.
   * @throws DefinitionsFactoryException If an error occur while initializing factory
   */
  public ComponentDefinitionsFactory createFactoryFromClassname(
      ServletContext servletContext, Map properties, String classname)
      throws DefinitionsFactoryException {

    if (classname == null) {
      return createFactory(servletContext, properties);
    }

    // Try to create from classname
    try {
      Class factoryClass = RequestUtils.applicationClass(classname);
      ComponentDefinitionsFactory factory =
          (ComponentDefinitionsFactory) factoryClass.newInstance();
      factory.initFactory(servletContext, properties);
      return factory;

    } catch (ClassCastException ex) { // Bad classname
      throw new DefinitionsFactoryException(
          "Error - createDefinitionsFactory : Factory class '"
              + classname
              + " must implements 'ComponentDefinitionsFactory'.",
          ex);

    } catch (ClassNotFoundException ex) { // Bad classname
      throw new DefinitionsFactoryException(
          "Error - createDefinitionsFactory : Bad class name '" + classname + "'.", ex);

    } catch (InstantiationException ex) { // Bad constructor or error
      throw new DefinitionsFactoryException(ex);

    } catch (IllegalAccessException ex) {
      throw new DefinitionsFactoryException(ex);
    }
  }
  /**
   * Create a controller from specified classname
   *
   * @param classname Controller classname.
   * @return org.apache.struts.tiles.Controller
   * @throws InstantiationException if an error occur while instanciating Controller : (classname
   *     can't be instanciated, Illegal access with instanciated class, Error while instanciating
   *     class, classname can't be instanciated.
   */
  public static Controller createControllerFromClassname(String classname)
      throws InstantiationException {

    try {
      Class requestedClass = RequestUtils.applicationClass(classname);
      Object instance = requestedClass.newInstance();

      if (log.isDebugEnabled()) {
        log.debug("Controller created : " + instance);
      }
      return (Controller) instance;

    } catch (java.lang.ClassNotFoundException ex) {
      throw new InstantiationException("Error - Class not found :" + ex.getMessage());

    } catch (java.lang.IllegalAccessException ex) {
      throw new InstantiationException("Error - Illegal class access :" + ex.getMessage());

    } catch (java.lang.InstantiationException ex) {
      throw ex;

    } catch (java.lang.ClassCastException ex) {
      throw new InstantiationException(
          "Controller of class '"
              + classname
              + "' should implements 'Controller' or extends 'Action'");
    }
  }
  /**
   * Introspect our form bean configuration to identify the supported properties.
   *
   * @param config The FormBeanConfig instance describing the properties of the bean to be created
   * @exception IllegalArgumentException if the bean implementation class specified in the
   *     configuration is not DynaActionForm (or a subclass of DynaActionForm)
   */
  protected void introspect(FormBeanConfig config) {

    this.config = config;

    // Validate the ActionFormBean implementation class
    try {
      beanClass = RequestUtils.applicationClass(config.getType());
    } catch (Throwable t) {
      throw new IllegalArgumentException(
          "Cannot instantiate ActionFormBean class '" + config.getType() + "': " + t);
    }
    if (!DynaActionForm.class.isAssignableFrom(beanClass)) {
      throw new IllegalArgumentException(
          "Class '"
              + config.getType()
              + "' is not a subclass of "
              + "'org.apache.struts.action.DynaActionForm'");
    }

    // Set the name we will know ourselves by from the form bean name
    this.name = config.getName();

    // Look up the property descriptors for this bean class
    FormPropertyConfig descriptors[] = config.findFormPropertyConfigs();
    if (descriptors == null) {
      descriptors = new FormPropertyConfig[0];
    }

    // Create corresponding dynamic property definitions
    properties = new DynaProperty[descriptors.length];
    for (int i = 0; i < descriptors.length; i++) {
      properties[i] = new DynaProperty(descriptors[i].getName(), descriptors[i].getTypeClass());
      propertiesMap.put(properties[i].getName(), properties[i]);
    }
  }
  /**
   * Make sure that the specified <code>className</code> identfies a class which can be found and
   * which implements the <code>ActionContext</code> interface.
   *
   * @param className Fully qualified name of
   * @throws ServletException If an error occurs during initialization
   * @throws UnavailableException if class does not implement ActionContext or is not found
   */
  private void setActionContextClassName(String className) throws ServletException {
    if ((className != null) && (className.trim().length() > 0)) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("setActionContextClassName: requested context class: " + className);
      }

      try {
        Class actionContextClass = RequestUtils.applicationClass(className);

        if (!ActionContext.class.isAssignableFrom(actionContextClass)) {
          throw new UnavailableException(
              "ActionContextClass "
                  + "["
                  + className
                  + "]"
                  + " must implement ActionContext interface.");
        }

        this.setActionContextClass(actionContextClass);
      } catch (ClassNotFoundException e) {
        throw new UnavailableException("ActionContextClass " + className + " not found.");
      }
    } else {
      if (LOG.isDebugEnabled()) {
        LOG.debug("setActionContextClassName: no className specified");
      }

      this.setActionContextClass(null);
    }
  }
  /**
   * Create an appropriate form bean in the appropriate scope, if one does not already exist.
   *
   * @param context FacesContext for the current request
   * @exception IllegalArgumentException if no ActionConfig for the specified action attribute can
   *     be located
   * @exception IllegalArgumentException if no FormBeanConfig for the specified form bean can be
   *     located
   * @exception IllegalArgumentException if no ModuleConfig can be located for this application
   *     module
   */
  public void createActionForm(FacesContext context) {

    // Look up the application module configuration information we need
    ModuleConfig moduleConfig = lookupModuleConfig(context);

    // Look up the ActionConfig we are processing
    String action = getAction();
    ActionConfig actionConfig = moduleConfig.findActionConfig(action);
    if (actionConfig == null) {
      throw new IllegalArgumentException("Cannot find action '" + action + "' configuration");
    }

    // Does this ActionConfig specify a form bean?
    String name = actionConfig.getName();
    if (name == null) {
      return;
    }

    // Look up the FormBeanConfig we are processing
    FormBeanConfig fbConfig = moduleConfig.findFormBeanConfig(name);
    if (fbConfig == null) {
      throw new IllegalArgumentException("Cannot find form bean '" + name + "' configuration");
    }

    // Does a usable form bean attribute already exist?
    String attribute = actionConfig.getAttribute();
    String scope = actionConfig.getScope();
    ActionForm instance = null;
    if ("request".equals(scope)) {
      instance = (ActionForm) context.getExternalContext().getRequestMap().get(attribute);
    } else if ("session".equals(scope)) {
      HttpSession session = (HttpSession) context.getExternalContext().getSession(true);
      instance = (ActionForm) context.getExternalContext().getSessionMap().get(attribute);
    }
    if (instance != null) {
      if (fbConfig.getDynamic()) {
        String className = ((DynaBean) instance).getDynaClass().getName();
        if (className.equals(fbConfig.getName())) {
          if (log.isDebugEnabled()) {
            log.debug(
                " Recycling existing DynaActionForm instance " + "of type '" + className + "'");
          }
          return;
        }
      } else {
        try {
          Class configClass = RequestUtils.applicationClass(fbConfig.getType());
          if (configClass.isAssignableFrom(instance.getClass())) {
            if (log.isDebugEnabled()) {
              log.debug(
                  " Recycling existing ActionForm instance "
                      + "of class '"
                      + instance.getClass().getName()
                      + "'");
            }
            return;
          }
        } catch (Throwable t) {
          throw new IllegalArgumentException(
              "Cannot load form bean class '" + fbConfig.getType() + "'");
        }
      }
    }

    // Create a new form bean instance
    if (fbConfig.getDynamic()) {
      try {
        DynaActionFormClass dynaClass = DynaActionFormClass.createDynaActionFormClass(fbConfig);
        instance = (ActionForm) dynaClass.newInstance();
        if (log.isDebugEnabled()) {
          log.debug(
              " Creating new DynaActionForm instance " + "of type '" + fbConfig.getType() + "'");
          log.trace(" --> " + instance);
        }
      } catch (Throwable t) {
        throw new IllegalArgumentException(
            "Cannot create form bean of type '" + fbConfig.getType() + "'");
      }
    } else {
      try {
        instance = (ActionForm) RequestUtils.applicationInstance(fbConfig.getType());
        if (log.isDebugEnabled()) {
          log.debug(" Creating new ActionForm instance " + "of type '" + fbConfig.getType() + "'");
          log.trace(" --> " + instance);
        }
      } catch (Throwable t) {
        throw new IllegalArgumentException(
            "Cannot create form bean of class '" + fbConfig.getType() + "'");
      }
    }

    // Configure and cache the form bean instance in the correct scope
    ActionServlet servlet =
        (ActionServlet)
            context.getExternalContext().getApplicationMap().get(Globals.ACTION_SERVLET_KEY);
    instance.setServlet(servlet);
    if ("request".equals(scope)) {
      context.getExternalContext().getRequestMap().put(attribute, instance);
    } else if ("session".equals(scope)) {
      context.getExternalContext().getSessionMap().put(attribute, instance);
    }
  }