XuiBuilder.java :  » XML-UI » XUI » net » xoetrope » builder » Java Open Source

Java Open Source » XML UI » XUI 
XUI » net » xoetrope » builder » XuiBuilder.java
package net.xoetrope.builder;

import java.io.Reader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.ResourceBundle;
import java.util.Vector;

import java.awt.Component;
import java.awt.Container;
import java.awt.TextComponent;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;

import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XAppender;
import net.xoetrope.xui.XAttributedComponent;
import net.xoetrope.xui.XContentPane;
import net.xoetrope.xui.XLayoutHelper;
import net.xoetrope.xui.XModelHolder;
import net.xoetrope.xui.XPage;
import net.xoetrope.xui.XPageDisplay;
import net.xoetrope.xui.XPageLoader;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.xui.XRadioButtonGroup;
import net.xoetrope.xui.XResourceManager;
import net.xoetrope.xui.XTarget;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.xui.data.XDataBinding;
import net.xoetrope.xui.data.XDataBindingFactory;
import net.xoetrope.xui.data.XListBinding;
import net.xoetrope.xui.data.XModel;
import net.xoetrope.xui.data.XModelAdapter;
import net.xoetrope.xui.data.XRadioBinding;
import net.xoetrope.xui.data.XStateBinding;
import net.xoetrope.xui.data.XTextBinding;
import net.xoetrope.xui.style.XStyleFactory;
import net.xoetrope.xui.validation.XValidationFactory;
import java.util.PropertyResourceBundle;
import java.awt.Cursor;
import net.xoetrope.xui.events.XListenerHelper;
import net.xoetrope.xui.data.XCustomDataBinding;
import net.xoetrope.xui.XComponentFactory;
import net.xoetrope.xui.XTextHolder;

/**
 * A builder of XUI pages from an XML source. An instance of the class is setup
 * by XApplet as a secondary page loader so that if a class file for the
 * page is not found then an attempt is made to load the page from an XML file
 * with the same base name.
 * <br>
 * Components, Menus, Events, Validations and Data Binding can be configured via the
 * XML declarations.
 * <br>
 * The loading process itself relies heavily on the component factories to do
 * the bulk of the work involved in constructing and adding components. The XPage
 * class also performs much of the work involved in adding events and binding data.
 * <p>Copyright (c) Xoetrope Ltd., 2002-2003</p>
 * <p>License: see license.txt</p>
 * $Revision: 1.29 $
 */
public class XuiBuilder implements XPageLoader
{
  protected String packageName;
  protected XStyleFactory componentFactory;
  protected Object checkBoxGroup;
  protected XPage page;
  protected XPage rootPage;
  protected XAppender menuBar;
  protected Hashtable validationFactories;
  protected static Hashtable currentAttributes;
  protected XLayoutHelper layoutHelper;
  private static int nextChildId;

  /**
   * Construct an instance of the builder with the default package set to net.xoetrope.awt
   */
  public XuiBuilder()
  {
    init( XPage.XUI_AWT_PACKAGE );
  }

  /**
   * Construct a new builder and set the default package
   * @param packageName the name of the default package e.g. net.xoetrope.awt
   */
  public XuiBuilder( String packageName )
  {
    init( packageName );
  }

  /**
   * Initialize the builder
   * @param packageName the default componnet package or use AWT if none is specified
   */
  protected void init( String packageName )
  {
    if ( packageName == null )
      packageName = XPage.XUI_AWT_PACKAGE;
    validationFactories = new Hashtable( 2 );
    componentFactory = new XStyleFactory( packageName );
    layoutHelper = componentFactory.getLayoutHelper();
  }

  /**
   * Set the default package name. The default package name is used when
   * creating widgets such that a button class like XButton is instatiated as
   * <defPackageName>.XButton.class. By default this expands to
   * net.xoetrope.awt.XButton.clas
   * @param defPackageName the default package name
   */
  public void setPackageName( String defPackageName )
  {
    if ( defPackageName == null )
      packageName = XPage.XUI_AWT_PACKAGE;
    else
      packageName = defPackageName;
    componentFactory = new XStyleFactory( packageName );
  }

  /**
   * Loads an XPage via a reader obtained from the XResourceManager (searches
   * the classpath). The pageName is assumed to be the name of an XML file. For
   * example if the pageName is 'welcome' then the 'welcome.xml' file is read as
   * a UTF8 encoded XML file (by default).
   * @param defPackageName the package or path to the page
   * @param pageName the page name or the name of the class implementing the page
   * @param include true if the page to be loaded is being included in another
   * page in which case any class attribute of the included page is ignored
   * @return the page
   */
  public XPage loadPage( String defPackageName, String pageName, boolean include )
  {
    packageName = defPackageName;

    Reader r = null;
    try {
      r = XResourceManager.getBufferedReader( pageName + ".xml", null );
    }
    catch ( Exception ex ) {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "File NOT found: " + pageName );
    }
    if ( r == null )
      return null;

    return readPage( r, pageName, include );
  }

  /**
   * Loads a frameset. If a frameset is used then the calls to the XPageDisplay
   * interface should use the form that specifies the target areas. By default
   * frames are specified in a file (if found) called frames.xml. The frames
   * are added to a container laid out with a BorderLayout.
   * <br>
   * Frames can be used to control how parts of the screen are updated. The
   * default page updated by the page transition/swapping methods is the 'content'
   * frame. Other areas can be used as non swapping areas for toolbars,
   * navigation controls, sidebars or status areas. Frames may also be hidden
   * and shown as needed.
   * @param defPackageName the package or path to the page
   * @param frameSetName the page name or the name of the class implementing the page
   * @param pageDisplay the object that will display the pages and frameset
   */
  public void loadFrames( String defPackageName, String frameSetName, XPageDisplay pageDisplay )
  {
    packageName = defPackageName;
    Reader r = null;
    try {
        r = XResourceManager.getBufferedReader( frameSetName + ".xml", null );
    }
    catch ( Exception ex ) {
    }
    if ( r == null )
      return;

    XmlElement model = XmlSource.read( r );
    Vector componentNodes = model.getChildren();
    int numChildren = componentNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )componentNodes.elementAt( i );
      String typeStr = childNode.getName();
      if ( typeStr.compareTo( "Frame" ) == 0 ) {
        try {
          String targetName = childNode.getAttribute( "name" );
          XTarget target = new XTarget( targetName,
                                        new Integer( childNode.getAttribute( "width" ) ).intValue(),
                                        new Integer( childNode.getAttribute( "height" ) ).intValue() );
          Object constraint = layoutHelper.getConstraint( childNode.getAttribute( "constraint" ));
          target.setName( targetName );
          pageDisplay.addTarget( target, constraint );
          XPage targetPage = XProjectManager.getPageManager().loadPage( childNode.getAttribute( "content" ));
          pageDisplay.displayPage( targetPage, targetName );
        }
        catch ( NumberFormatException ex1 ) {
        }
      }
    }
  }

  /**
   * Read an XML description of the page and construct a new XPage. An instance
   * of the class specified by the class attribute is constructed or else an
   * instance of XPage if no class attribute is specified. The new page is
   * populated but is not yet added to its parent.
   * <br>
   * The startup file parameter 'DefaultClass' is used to obtain a default for
   * each page's class if a class parameter is not specified in the page's XML
   * <br>
   * The startup file parameter 'Validations' is used to obtain a default for
   * each page's set of validation rules
   *
   * @param reader a input stream from which to read the page
   * @param pageName the name of the page
   * @param include the page to be loaded is being included in another page
   * @return the page
   */
  public XPage readPage( Reader reader, String pageName, boolean include )
  {
    XmlElement model = XmlSource.read( reader );
    setupPage( model, pageName, include );
    XmlElement componentsNode = null;
    XmlElement eventsNode = null;
    XmlElement dataNode = null;
    XmlElement menuNode = null;
    XmlElement validationsNode = null;

    Vector componentNodes = model.getChildren();
    int numChildren = componentNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )componentNodes.elementAt( i );
      String typeStr = childNode.getName().toLowerCase();
      if ( typeStr.compareTo( "components" ) == 0 )
        componentsNode = childNode;
      else if ( typeStr.compareTo( "events" ) == 0 )
        eventsNode = childNode;
      else if ( typeStr.compareTo( "data" ) == 0 )
        dataNode = childNode;
      else if ( typeStr.compareTo( "validations" ) == 0 )
        validationsNode = childNode;
      else if ( typeStr.compareTo( "menubar" ) == 0 )
        menuNode = childNode;
      else if ( typeStr.compareTo( "include" ) == 0 ) {
        loadPage( packageName, childNode.getAttribute( "file" ), true );
        componentFactory.setParentComponent( (Container)page );
      }
    }

    if ( menuNode != null )
      addMenu( page, menuNode );
    if ( componentsNode != null )
      addComponents( (Container)( include ? componentFactory.getParentComponent() : page ), componentsNode );
    if ( dataNode != null )
      addBindings( page, dataNode );
    if ( eventsNode != null )
      addEvents( page, eventsNode );
    if ( validationsNode != null )
      addValidations( page, validationsNode );

//    String styleValue = ( String )model.getAttribute( "style" );
//    if ( styleValue != null )
//      currentResource.setStyleName( page, styleValue );

    page.validate();
    return page;
  }

  /**
   * Loads a class as the basis for a page
   * @param className the full class name
   * @return an instance of the XPage class or a derivative
   * @throws ClassNotFoundException
   * @throws IllegalAccessException
   * @throws InstantiationException
   */
  protected XPage loadClass( String className ) throws ClassNotFoundException, IllegalAccessException, InstantiationException
  {
    return ( XPage )Class.forName( className ).newInstance();
  }

  /**
   * Loads the page based on the contents of the page tag or by using default
   * values.
   *
   * @param pageName the name of the page
   * @param include the page to be loaded is being included in another page
   * @return the page
   */
  protected void setupPage( XmlElement model, String pageName, boolean include )
  {
    String className = model.getAttribute( "class" );

    if (( className == null ) || ( className.length() == 0 )) {
      // Try to get a default startup class name if none has been specified in the XML
      try {
        className = XProjectManager.getCurrentProject().getStartupParam( "DefaultClass" );
      }
      catch ( Exception ex ) {
      }
      if ( className == null )
        className = "net.xoetrope.xui.XPage";
    }

    if ( !include ) {
      if ( ( className.indexOf( '.' ) <= 0 ) && ( packageName.length() > 1 ) )
        className = packageName + className;
      try {
        page = loadClass( className );
      }
      catch ( Exception e ) {
        if ( BuildProperties.DEBUG )
          DebugLogger.trace( "Unable to load the named class: " + className );
        page = new XPage();
      }
      setPageName( pageName );

      // Perhaps use classname by default
      // or check for existance of the file within XPage ... might add delay in constructing non validated pages
      try {
        String validationName = model.getAttribute( "Validations" );
        if ( validationName == null || validationName.length() == 0 )
          validationName = XProjectManager.getCurrentProject().getStartupParam( "Validations" );
        if ( validationName != null ) {
          if ( BuildProperties.DEBUG )
            DebugLogger.trace( "Reading validations: " + validationName );
          page.setValidationFactory( getValidationFactory( validationName ) );
        }
      }
      catch ( Exception ex ) {
        ex.printStackTrace();
      }

      if ( page instanceof XContentPane )
        componentFactory.setParentComponent( (( XContentPane )page).getContentPane() );
      else
        componentFactory.setParentComponent( ( Container )page );
      String styleName = model.getAttribute( "style" );
      if ( styleName != null )
        componentFactory.applyStyle( page, styleName );
    }

    int width = 0, height = 0;
    String layoutMgr = null;
    Enumeration attribNamesEnum = model.enumerateAttributeNames();
    while ( attribNamesEnum.hasMoreElements()) {
      String attribName = ( String )attribNamesEnum.nextElement();
      String attribValue = ( String )model.getAttribute( attribName );
      if ( attribName.compareTo( "class" ) == 0 )
        ;
      else if ( attribName.compareTo( "resource" ) == 0 ) {
        String resName;
        if (( attribValue != null ) && ( attribValue.length() > 0 ))
          resName = page.evaluatePath( attribValue );
        else
          resName = XResourceManager.getStartupParam( "Language" );
        componentFactory.setResourceBundle( resName );
        page.getComponentFactory().setResourceBundle( resName );
      }
      else if ( attribName.compareTo( "layout" ) == 0 ) {
        if ( attribValue != null )
          layoutMgr = attribValue;
      }
      else
        page.setAttribute( attribName, null, evaluateAttribute( page, attribValue ));

      setPageAttribute( page, attribName, attribValue );
    }

    if ( layoutMgr != null )
      componentFactory.addLayout( page, layoutHelper.getLayoutType( layoutMgr ), currentAttributes );
    rootPage = (XPage)page;
  }

  /**
   * Get a page attribute (this version does nothing)
   * @param c
   * @param name
   * @param value
   */
  public void setPageAttribute( Component c, String name, String value )
  {
  }

  /**
   * Get the value of an attribute.
   * @param page the page being loaded
   * @param attributeValue the raw value of the attribute
   * @return the evaluated value of the attribute
   */
  public Object evaluateAttribute( XPage page, String attributeValue )
  {
    return page.evaluateAttribute( attributeValue );
  }

  /**
   * Adds the elements specified by the Components element and its children
   * @param page the new page object
   * @param model the Components XML element (and implicitly its children)
   */
  protected void addComponents( Component page, XmlElement model )
  {
//    componentFactory.setParentComponent( (Container)page );

    Vector componentNodes = model.getChildren();
    int numChildren = componentNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )componentNodes.elementAt( i );
      addComponent( childNode );
    }
  }

  /**
   * Adds the event handlers. Events are specified in the XML as having 'type'
   * (e.g. MouseHandler, ActionHandler etc...), 'name' (the method name to be
   * invoked) and 'target' (the name of the component to which the handler is
   * attached) attributes
   * @param page The page that contains the response methods
   * @param model the 'events' XML element
   */
  protected void addEvents( XPage page, XmlElement model )
  {
    String typeStr, methodStr, targetStr, cursorStr;
    cursorStr = "";

    Vector eventNodes = model.getChildren();
    int numChildren = eventNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      typeStr = methodStr = targetStr = null;

      XmlElement childNode = ( XmlElement )eventNodes.elementAt( i );
      try {
        Enumeration attribNamesEnum = childNode.enumerateAttributeNames();
        while ( attribNamesEnum.hasMoreElements()) {
          String attribName = (String)attribNamesEnum.nextElement();
          String attribValue = childNode.getAttribute( attribName );
          if ( attribName.compareTo( "type" ) == 0 )
            typeStr = attribValue;
          else if  ( attribName.compareTo( "method" ) == 0 )
            methodStr = attribValue;
          else if  ( attribName.compareTo( "target" ) == 0 )
            targetStr = attribValue;
          else if  ( attribName.compareTo( "cursor" ) == 0 )
            cursorStr = attribValue;
          else
            page.setAttribute( attribName, targetStr, attribValue );
        }

        if ( typeStr.compareTo( "MenuHandler" ) == 0 )
          page.addMenuHandler( getMenuItem( targetStr ), methodStr );
        else {
          Component targetComp = page.findComponent( targetStr );
          if ( BuildProperties.DEBUG ) {
            if ( targetComp == null )
              DebugLogger.logError( "Unable to find the component '" + targetStr + "' named in the event element " + methodStr );
          }
          addHandler( page, targetComp, typeStr, methodStr );
          if ( cursorStr.equals( "true" ))
            targetComp.setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) );
        }
      }
      catch ( Exception e )
      {
        if ( BuildProperties.DEBUG )
          DebugLogger.logError( "While adding the event element: " + methodStr );

        e.printStackTrace();
      }
    }
  }

  /**
   * Adds an event handler.
   * @param xpage The page that contains the response methods
   * @param targetComp the component to which the event handler is added
   * @param typeStr the type of handler
   * @param name the name of the response method
   */
  protected void addHandler( XPage xpage, Component targetComp, String typeStr, String name )
  {
    if ( typeStr.compareTo( "MouseHandler" ) == 0 )
      page.addMouseHandler( targetComp, name );
    else if ( typeStr.compareTo( "MouseMotionHandler" ) == 0 )
      page.addMouseMotionHandler( targetComp, name );
    else if ( typeStr.compareTo( "ActionHandler" ) == 0 )
      page.addActionHandler( targetComp, name );
    else if ( typeStr.compareTo( "FocusHandler" ) == 0 )
      page.addFocusHandler( targetComp, name );
    else if ( typeStr.compareTo( "ItemHandler" ) == 0 )
      page.addItemHandler( targetComp, name );
    else if ( typeStr.compareTo( "KeyHandler" ) == 0 )
      page.addKeyHandler( targetComp, name );
    else if ( typeStr.compareTo( "TextHandler" ) == 0 )
      page.addTextHandler( targetComp, name );
    else {
      try {
        ( ( XListenerHelper )targetComp ).addHandler( page, typeStr, name );
      }
      catch ( NoSuchMethodException ex ) {
        if ( BuildProperties.DEBUG )
          DebugLogger.logError( "Unable to add the event handler method: " + name );
      }
    }
  }

  /**
   * Register a data binding factory. An add-on library or plug-in may wish to
   * customize the data bindings, particularly if it provides extended model
   * node types. Such extended nodes may need adapters to be useful for some or
   * all widgets and other binding contexts. Registration of the binding factory
   * allows the necessary adapters and bindings to be created on request.
   * @param fact the new binding factory
   */
  public static void registerBindingFactory( XDataBindingFactory fact )
  {
    XProjectManager.getCurrentProject().getBindingsFactories().addElement( fact );
  }

  /**
   * Adds data bindings to the page.
   * @param page the page to which the component/data bindings are added
   * @param model the data model
   */
  protected void addBindings( XPage page, XmlElement model )
  {
    if ( model == null )
      return;

    String nameStr = null, srcStr, adapterStr, typeStr, attribStr;
    XModel rootNode = XProjectManager.getModel();

    Vector eventNodes = model.getChildren();
    int numChildren = eventNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )eventNodes.elementAt( i );
      try {
        nameStr = childNode.getAttribute( "target" );
        srcStr = childNode.getAttribute( "source" );
        typeStr = childNode.getAttribute( "type" );
        attribStr = childNode.getAttribute( "attrib" );
        XModel srcModel = ( XModel )rootNode.get( page.evaluatePath( srcStr ));

        Component targetComp = page.findComponent( nameStr );
        XDataBinding factoryBinding = getFactoryBinding( targetComp, srcModel, childNode );
        if ( factoryBinding != null )
          page.addBinding( factoryBinding );
        else {
          if ( typeStr != null && typeStr.compareTo( "custom" ) == 0 ) {
            String className = childNode.getAttribute( "class" );
            XDataBinding binding = ( XDataBinding )Class.forName( className ).newInstance();
            ((XCustomDataBinding)binding).setup( targetComp, childNode );
            page.addBinding( binding );
          }
          else {
            String className = targetComp.getClass().getName();
            if ( className.indexOf( "XComboBox" ) >= 0 ) {
              XListBinding binding;

              // Check for a specific binding
              adapterStr = childNode.getAttribute( "adapter" );
              if ( adapterStr != null ) {
                // Assume a table is bound to the node
                XModelAdapter adapter = ( XModelAdapter ) Class.forName( adapterStr ).newInstance();
                XModel tableModel = ( XModel )rootNode.get( srcStr );
                adapter.setModel( tableModel );
                binding = new XListBinding( targetComp, adapter );
              }
              else { // Otherwise static data is being bound
                String destStr = childNode.getAttribute( "output" );
                String saveStr = childNode.getAttribute( "saveToSource" );
                if ( destStr == null )
                  destStr = srcStr;

                XModel dstModel = (XModel)rootNode.get( page.evaluatePath( destStr ) );
                binding = new XListBinding( targetComp, srcStr, destStr, srcModel, dstModel, ( saveStr != null ? saveStr.equals( "true" ) : false ));
              }

              // Check and set the unique rows property
              String attrib = childNode.getAttribute( "unique" );
              if ( ( attrib != null ) && ( attrib.compareTo( "true" ) == 0 ) )
                binding.setUseUnique(true);
              page.addBinding(binding);
            }
            else if ( className.indexOf( "XEdit" ) >= 0 )
              page.addBinding( new XTextBinding( targetComp, srcStr, srcModel, attribStr ) );
            else if ( className.indexOf( "XTextArea" ) >= 0 )
              page.addBinding( new XTextBinding( targetComp, srcStr, srcModel, attribStr ) );
            else if ( className.indexOf( "XButton" ) >= 0 )
              page.addBinding( new XTextBinding( targetComp, srcStr, srcModel ) );
            else if ( className.indexOf( "XLabel" ) >= 0 )
              page.addBinding( new XTextBinding( targetComp, srcStr, srcModel, attribStr ) );
            else if ( className.indexOf( "XRadioButton" ) >= 0 ) {
              if ( typeStr != null ) {
                if ( typeStr.equals( "text" ) )
                  page.addBinding( new XTextBinding( targetComp, srcStr, srcModel, attribStr ) );
                else if ( typeStr.equals( "state" ) ) {
                  XStateBinding sb = new XStateBinding( targetComp, srcStr, srcModel, childNode.getAttribute( "leaf" ) );
                  String destStr = childNode.getAttribute( "output" );
                  if ( destStr != null )
                    sb.setOutputPath( destStr );
                  page.addBinding( sb );
                }
              }
              else
                page.addBinding( new XRadioBinding( targetComp, srcStr, srcModel ) );
            }
            else if ( className.indexOf( "XCheckbox" ) >= 0 ) {
              if ( ( typeStr != null ) && ( typeStr.equals( "text" ) ) )
                page.addBinding( new XTextBinding( targetComp, srcStr, srcModel, attribStr ) );
              else {
                XStateBinding sb = new XStateBinding( targetComp, srcStr, srcModel, childNode.getAttribute( "leaf" ) );
                String destStr = childNode.getAttribute( "output" );
                if ( destStr != null )
                  sb.setOutputPath( destStr );
                page.addBinding( sb );
              }
            }
            else if ( className.indexOf( "XTable" ) >= 0 ) {
              XModel tableModel = ( XModel )rootNode.get( srcStr );
              ( ( XModelHolder )targetComp ).setModel( tableModel );
            }
            else if ( targetComp instanceof TextComponent )
              page.addBinding( new XTextBinding( targetComp, srcStr, srcModel, attribStr ) );
          }
        }
      }
      catch ( Exception e )
      {
        if ( BuildProperties.DEBUG )
          DebugLogger.logError( "While adding the data binding element: " + nameStr );
        e.printStackTrace();
      }
    }
  }

  /**
   * Try to get a binding factory to construct the binding
   * @param compType the component type
   * @param model the source data model
   * @param bindingNode the XML element defining the binding
   * @return the new binding if one could be constructed
   */
  protected XDataBinding getFactoryBinding( Component compType, XModel model, XmlElement bindingNode )
  {
    XDataBinding binding = null;
    Vector bindingFactories = XProjectManager.getCurrentProject().getBindingsFactories();
    for ( int i = 0; i < bindingFactories.size(); i++ ) {
      binding = ( ( XDataBindingFactory )bindingFactories.elementAt( i ) ).getBinding( compType, model, bindingNode );
      if ( binding != null )
        return binding;
    }

    return null;
  }

  /**
   * Set the name of the page
   * @param pageName the new page name.
   */
  protected void setPageName( String pageName )
  {
    page.setName( pageName );
  }

  /**
   * Adds an individual component element to the page (this method may be called
   * recursively for nested elements). Several methods will be attempted until a
   * component is successfully created. Firstly the built-in component types are
   * checked, then any additional registered component constructors. The types
   * can be specified by type ID, type name or class name.
   * @param childName the name of the child element
   * @param childNode the XML element containing the component specification.
   * @return the new component
   */
  protected Component addComponent( XmlElement childNode )
  {
    String nameStr = null, typeStr, xStr, yStr, wStr, hStr, contentStr, styleStr, classStr,
                     layoutMgr, constraintStr, opaqueStr;

    Component comp = null;
    String childName = childNode.getName();

    try {
      int x = 0, y = 0, w = 30, h = 20;
      nameStr = typeStr = wStr = hStr = contentStr = styleStr = classStr = layoutMgr =
          constraintStr = opaqueStr = null;
      xStr = yStr = "0";
      wStr = hStr = "-1";
      Hashtable componentAttributes = new Hashtable(); // Local copy of the component attributes
      currentAttributes = componentAttributes;
      Enumeration attribNamesEnum = childNode.enumerateAttributeNames();
      while ( attribNamesEnum.hasMoreElements() ) {
        String attribName = ( String ) attribNamesEnum.nextElement();
        String attribValue = ( String ) childNode.getAttribute( attribName );
        if ( attribName.compareTo( "type" ) == 0 )
          typeStr = attribValue;
        else if ( attribName.compareTo( "x" ) == 0 )
          xStr = attribValue;
        else if ( attribName.compareTo( "y" ) == 0 )
          yStr = attribValue;
        else if ( attribName.compareTo( "w" ) == 0 )
          wStr = attribValue;
        else if ( attribName.compareTo( "h" ) == 0 )
          hStr = attribValue;
        else {
          Object retObj = evaluateAttribute( rootPage, attribValue );
          attribValue = retObj.toString();
          if ( attribName.compareTo( "name" ) == 0 )
            nameStr = attribValue;
          else if ( attribName.compareTo( "class" ) == 0 )
            classStr = attribValue;
          else if ( attribName.compareTo( "layout" ) == 0 )
            layoutMgr = attribValue;
          else if ( attribName.compareTo( "constraint" ) == 0 )
            constraintStr = attribValue;
          else {
            if ( attribName.compareTo( "content" ) == 0 )
              contentStr = attribValue;
            else if ( attribName.compareTo( "key" ) == 0 )
              contentStr = attribValue;
            else if ( attribName.compareTo( "style" ) == 0 )
              styleStr = attribValue;
            else if ( attribName.compareTo( "opaque" ) == 0 )
              opaqueStr = attribValue;
            else {
              if ( nameStr == null ) {
                nameStr = childNode.getAttribute( "name" );
                // Synthesise a name to distinguish this component
                if ( nameStr == null )
                  nameStr = "child" + new Integer( ++nextChildId ).toString();
              }

              // Save a copy of the attributes in the page for post creation usage
              ( ( XPage )rootPage ).setAttribute( attribName, nameStr, attribValue );
            }

            // Save a local copy for use during the construction process
            componentAttributes.put( attribName, attribValue );
          }
        }
      }

      x = y = w = h = 0;
      x = getInt( xStr, x );
      y = getInt( yStr, y );
      w = getInt( wStr, w );
      h = getInt( hStr, h );

      if ( typeStr != null )  // Load an enumerated type
        childName = typeStr;

      if ( classStr != null ) // Load a specified type class
        comp = componentFactory.addClass( classStr,
                                              x, y, w, h,
                                              styleStr
                                              );
      else { // Load by name
        if ( childName.compareTo( "Include" ) == 0 ) {
          Component parentObject = componentFactory.getParentComponent();
          loadPage( packageName, childNode.getAttribute( "file" ), true );
          componentFactory.setParentComponent( parentObject );
          return null;
        }

        // Load with a name like 'Button'
        try {
          if ( constraintStr == null )
            comp = componentFactory.addComponent( childName,
                x, y, w, h,
                contentStr,
                styleStr
                );
          else {
            comp = componentFactory.addComponent( childName,
                layoutHelper.getConstraint( constraintStr ),
                contentStr,
                styleStr
                );
            if ( w > 0 )
              comp.setSize( w, h );
          }
        }
        catch ( Exception e ) {
          comp = null;
        }
        finally {
          if ( comp == null ) {
            componentAttributes.put( "x", xStr );
            componentAttributes.put( "y", yStr );
            componentAttributes.put( "w", wStr );
            componentAttributes.put( "h", hStr );
            Object obj = componentFactory.addElement( childName, nameStr, contentStr, componentAttributes );
            if ( obj == null ) {
              if ( BuildProperties.DEBUG )
                DebugLogger.logError( "Unable to add component: " + nameStr );

                // Construct a dummy component
              comp = componentFactory.addComponent( XPage.UNKNOWN, x, y, w, h, contentStr, styleStr );
              componentAttributes.put( "type", childName );
            }
          }
        }
      }

      if ( comp == null )
        return null;

      // Add the layout manager if one has been specified
      if (( comp instanceof Container ) && ( layoutMgr != null ))
          componentFactory.addLayout( ( Container )comp, layoutHelper.getLayoutType( layoutMgr ), componentAttributes );

      // Set the component name
      comp.setName( nameStr );

      // If a panel is found set it to the parent and recursively add its children
      if ( comp instanceof Container ) {
        Component parentObject = componentFactory.getParentComponent();
        componentFactory.setParentComponent( comp );
        addComponents( comp, childNode );
        currentAttributes = componentAttributes;
        componentFactory.setParentComponent( parentObject );
        comp.doLayout();
      }

      // Special handling for radio buttons
      // For the first rb create a group
      // For subsequent rbs add them to the group
      // If another type of component is found reset the groups
      if ( comp instanceof XRadioButtonGroup ) {
        XRadioButtonGroup rb = ( ( XRadioButtonGroup ) comp );
        if ( checkBoxGroup == null ) {
          // The radio button may already have a group
          if ( rb.getRadioButtonGroup() == null )
            checkBoxGroup = rb.createGroup();
          else
            checkBoxGroup = rb.getRadioButtonGroup();
        }
        else
          rb.setRadioButtonGroup( checkBoxGroup );
      }
      else
        checkBoxGroup = null;

      // Setup any extra attributes specified in the XML
      setComponentAttributes( comp, componentAttributes );
    }
    catch ( Exception e ) {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "While adding the component element: " + nameStr );
      e.printStackTrace();
    }

    return comp;
  }

  /**
   * Adds a menu to the application. Although specified in the XML as part of the
   * page the menu is actually added to the application frame.
   * @param page the page
   * @param model the Menu XML element
   */
  protected void addMenu( XPage page, XmlElement model )
  {
    Component parent = componentFactory.getParentComponent();
    componentFactory.setParentComponent( (Container)page );
    String basePackageName = XResourceManager.getPackageName() + ".X";
    try {
      menuBar = ( XAppender )Class.forName( basePackageName + XPage.MENUBAR ).newInstance();

      Vector menuNodes = model.getChildren();
      int numMenus = menuNodes.size();
      for ( int i = 0; i < numMenus; i++ ) {
        XmlElement childNode = ( XmlElement )menuNodes.elementAt( i );
        XAppender menu = ( XAppender )Class.forName( basePackageName + XPage.MENU ).newInstance();
        ((XTextHolder)menu).setText( componentFactory.translate( childNode.getAttribute( "content" ) ));

        int numMenuItems = childNode.getChildren().size();
        for ( int j = 0; j < numMenuItems; j++ ) {
          XmlElement itemNode = ( XmlElement )childNode.elementAt( j );
          Object menuItem = Class.forName( basePackageName + XPage.MENUITEM ).newInstance();
          ((XTextHolder)menuItem).setText( componentFactory.translate( itemNode.getAttribute( "content" ) ));
          menu.append( menuItem, itemNode.getAttribute( "name" ) );
        }
        menuBar.append( menu, childNode.getAttribute( "name" ) );
      }
      menuBar.setup();
    }
    catch ( Exception ex ) {
    }
    componentFactory.setParentComponent( (Container)parent );
  }

  /**
   * Adds validation rules to the components
   * @param page the page
   * @param model the Validations XML element
   */
  protected void addValidations( XPage page, XmlElement model )
  {
    Vector validations = model.getChildren();
    int numValidations = validations.size();
    for ( int i = 0; i < numValidations; i++ ) {
      XmlElement childNode = ( XmlElement )validations.elementAt( i );
      Component target = page.findComponent( childNode.getAttribute( "target" ));
      String whenAttrib = childNode.getAttribute( "when" );
      int whenAttribValue = FocusEvent.FOCUS_LOST;
      if ( whenAttrib != null ) {
        if ( whenAttrib.toLowerCase().compareTo( "mouseclicked" ) == 0 )
          whenAttribValue = MouseEvent.MOUSE_CLICKED;
      }
      page.addValidation( target, childNode.getAttribute( "rule" ), childNode.getAttribute( "method" ), whenAttribValue );
    }
  }



  /**
   * Iterate through the attributes and set the attributes for a component
   * @param comp the component
   * @param attribs the attributes.
   */
  protected void setComponentAttributes( Component comp, Hashtable attribs )
  {
    String compName = page.getComponentName( comp );

    Enumeration enum = attribs.keys();
    while ( enum.hasMoreElements() ) {
      try {
        String key = ( String )enum.nextElement();
        String value = ( String )attribs.get( key );
        page.setAttribute( key, compName, value );

        // Set a couple of key attributes
        if ( key.compareTo( "visible" ) == 0 )
          comp.setVisible( value.equals( "true" ) );
        else if ( key.compareTo( "enabled" ) == 0 )
          comp.setEnabled( value.equals( "true" ) );
        else if ( comp instanceof XAttributedComponent )
          ( ( XAttributedComponent )comp ).setAttribute( key, value );
      }
      catch ( Exception ex ) {
      }
    }
  }

  /**
   * Gets a reference to a menu object
   * @param name the name of the menu item
   * @return
   */
  protected Object getMenuItem( String name )
  {
    return menuBar.getObject( name );
  }

  /**
   * Construct a validation factory appropriate to this builder
   * @param validationFileName the validations file to read
   * @return the validation factory
   */
  protected XValidationFactory getValidationFactory( String validationFileName )
  {
    if (( validationFileName != null ) && ( validationFileName.length() > 0 )) {
      try {
        XValidationFactory vf = ( XValidationFactory )validationFactories.get( validationFileName );
        if ( vf == null ) {
          String validationFactName = XProjectManager.getCurrentProject().getStartupParam( "ValidationFactory" );
          if ( validationFactName != null ) {
            vf = ( XValidationFactory ) Class.forName(validationFactName).newInstance();
            vf.read( XResourceManager.getBufferedReader( validationFileName, null ) );
          }
          else {
            vf = new XValidationFactory( XResourceManager.getBufferedReader( validationFileName, null ) );
          }
          validationFactories.put( validationFileName, vf );
        }
        return vf;
      }
      catch ( Exception ex ) {
        if ( BuildProperties.DEBUG )
          DebugLogger.logError( "Unable to read the validations file: " + ex.getMessage() + ", " + ex.getMessage());
      }
    }
    return null;
  }

  /**
   * Convert a string to an int or else return the default value
   * @param s the number as a string
   * @param def the default value
   * @return the converted value
   */
  protected int getInt( String s, int def )
  {
    try {
      return new Integer( s ).intValue();
    }
    catch ( NumberFormatException ex ) {
    }
    return def;
  }

  /**
   * Get the table of component attributes
   * @return the attributes hash table.
   */
  public static Hashtable getCurrentAttributes()
  {
    return currentAttributes;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.