/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@ricochet.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001-2004 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.ui.dlg;

import java.lang.reflect.*;
import java.util.*;
import org.apache.log4j.*;
import org.w3c.dom.*;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.ui.RenderDirect;

public class DialogElementLoader
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Logger logger = Logger.getLogger(DialogElementLoader.class);

  private static DialogElementLoader self = null;

  private static final Class[] init_classes = {
    CategoryHeader.class, CheckBoxField.class, CommunityLogoField.class, CountryListField.class,
    DateField.class, EMailAddressField.class, EnumPickListField.class, HiddenField.class,
    ImageButton.class, IntegerField.class, IPAddressField.class, LanguageListField.class,
    LocaleListField.class, PasswordField.class, RoleListField.class, StaticPickListField.class,
    TextField.class, TimeZoneListField.class, UserPhotoField.class, VeniceIDField.class
  };

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private Map factories;         // mapping from tag name to factories

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  private DialogElementLoader()
  {
    final Class[] ctor_params = { Element.class };
    HashMap tmp_factories = new HashMap();
    for (int i=0; i<init_classes.length; i++)
    { // do a quick sanity check
      int xmod = init_classes[i].getModifiers();
      if (Modifier.isAbstract(xmod) || Modifier.isInterface(xmod))
	continue;
      if (!(RenderDirect.class.isAssignableFrom(init_classes[i])))
	continue;

      // retrieve the tag name
      String tagname = null;
      try
      { // retrieve the TAGNAME static field value from the class
	Field tname_fld = init_classes[i].getField("TAGNAME");
	if (!(Modifier.isStatic(tname_fld.getModifiers())))
	  continue;
	tagname = tname_fld.get(null).toString();

      } // end try
      catch (NoSuchFieldException nsfe)
      { // the TAGNAME field doesn't exist
	continue;

      } // end catch
      catch (SecurityException se)
      { // cannot get the field
	continue;

      } // end catch
      catch (IllegalAccessException iae)
      { // cannot get the field
	continue;

      } // end catch

      Constructor ctor = null;
      try
      { // pull the specified constructor out of the class
	ctor = init_classes[i].getConstructor(ctor_params);

      } // end try
      catch (NoSuchMethodException nsme)
      { // the constructor doesn't exist
	continue;

      } // end catch
      catch (SecurityException se)
      { // cannot get the constructor
	continue;

      } // end catch

      // save off the retrieved information
      if (logger.isDebugEnabled())
	logger.debug("Initialized tag <" + tagname + "/> to ctor " + ctor.toString());
      tmp_factories.put(tagname,ctor);
      
    } // end for

    if (logger.isDebugEnabled())
      logger.debug("Defined " + tmp_factories.size() + " out of " + init_classes.length + " dialog classes");
    if (tmp_factories.isEmpty())
      factories = Collections.EMPTY_MAP;
    else
      factories = Collections.unmodifiableMap(tmp_factories);

  } // end constructor

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  public RenderDirect createElement(Element elt) throws ConfigException
  {
    Constructor ctor = (Constructor)(factories.get(elt.getTagName()));
    if (ctor==null)
    { // don't know this tag name
      logger.fatal("DialogElementLoader: unknown element type \"" + elt.getTagName() + "\"");
      throw new ConfigException("unknown dialog element <" + elt.getTagName() + "/>");

    } // end if

    RenderDirect rc = null;
    try
    { // create the resulting element
      Object[] parms = new Object[1];
      parms[0] = elt;
      rc = (RenderDirect)(ctor.newInstance(parms));

    } // end try
    catch (InstantiationException ie)
    { // this shouldn't happen
      throw new InternalStateError("class " + ctor.getDeclaringClass().getName()
				   + " is abstract, shouldn't be");

    } // end catch
    catch (IllegalAccessException iae)
    { // shouldn't happen
      throw new InternalStateError("constructor of class " + ctor.getDeclaringClass().getName()
				   + " is not accessible");

    } // end catch
    catch (IllegalArgumentException ae)
    { // this shouldn't happen
      throw new InternalStateError("args for constructor of class " + ctor.getDeclaringClass().getName()
				   + " aren't what they should be");

    } // end catch
    catch (InvocationTargetException ite)
    { // this is where we catch ConfigExceptions
      Throwable t = ite.getTargetException();
      if (t instanceof ConfigException)
	throw (ConfigException)t;
      throw new InternalStateError("unknown exception thrown by constructor of class "
				   + ctor.getDeclaringClass().getName() + ": " + t.getClass().getName());

    } // end catch
    catch (ClassCastException cce)
    { // this shouldn't happen
      throw new InternalStateError("class " + ctor.getDeclaringClass().getName()
				   + " is not a RenderDirect, should be");

    } // end catch

    return rc;

  } // end createElement

  /*--------------------------------------------------------------------------------
   * External static operations
   *--------------------------------------------------------------------------------
   */

  public static final DialogElementLoader get()
  {
    synchronized (DialogElementLoader.class)
    { // initialize the dialog element loader
      if (self==null)
	self = new DialogElementLoader();

    } // end synchronized block

    return self;

  } // end get

} // end class DialogElementLoader
