Package net.xoetrope.builder

Source Code of net.xoetrope.builder.XuiBuilder

package net.xoetrope.builder;

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

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
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.WidgetAdapter;
import net.xoetrope.xui.XAppender;
import net.xoetrope.xui.XAttributedComponent;
import net.xoetrope.xui.XContentPane;
import net.xoetrope.xui.XPage;
import net.xoetrope.xui.PageSupport;
import net.xoetrope.xui.XPageDisplay;
import net.xoetrope.xui.XPageLoader;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.xui.XRadioButtonGroup;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.XTextHolder;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.xui.data.XDataBinding;
import net.xoetrope.xui.data.XDataBindingFactory;
import net.xoetrope.xui.evaluator.XAttributeEvaluator;
import net.xoetrope.xui.style.XStyleFactory;
import net.xoetrope.xui.validation.XValidationFactory;
import net.xoetrope.xui.XContentHolder;
import net.xoetrope.registry.ComponentAdapter;
import net.xoetrope.registry.ComponentCustomizer;
import net.xoetrope.xui.XAttributedComponentEx;
import net.xoetrope.xui.XMultiAttributedComponent;
import net.xoetrope.xui.data.XBasicDataBindingFactory;
import net.xoetrope.xui.data.XRegisteredDataBindingFactory;
import net.xoetrope.xui.evaluator.XDefaultAttributeEvaluator;
import net.xoetrope.xui.events.XuiEventHandler;
import net.xoetrope.xui.helper.ReflectionHelper;
import net.xoetrope.xui.helper.XLayoutHelper;
import net.xoetrope.xui.helper.XAttributedComponentHelper;
import net.xoetrope.xui.style.XStyle;

/**
* 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-2007</p>
* <p>License: see license.txt</p>
* $Revision: 2.59 $
*/
public class XuiBuilder implements XPageLoader
{
  protected String packageName;
  protected XStyleFactory componentFactory;
  protected Object checkBoxGroup;
  protected PageSupport page;
  protected PageSupport rootPage;
  protected XAppender menuBar;
  protected Hashtable validationFactories;
  protected Hashtable attributeSets;
  protected static Hashtable currentAttributes;
  protected XLayoutHelper layoutHelper;
  private static int nextChildId;

  protected XProject currentProject;
  protected WidgetAdapter adapter;
  protected ComponentCustomizer customizer;

  /** The default attribute evaluator. The evaluator can be replaced to support
   * various ways of evaluating and interpreting dynamic attributes */
  protected XAttributeEvaluator evaluator;

  protected XuiEventHandler eventHandler;
 
  /**
   * A secondary classloader for attempts to load classes referenced by a page
   */
  protected ClassLoader secondaryClassloader;

  protected String includeFileName;
 
  /**
   * The default class used to instantiate a page. As of XUI 2.0 the implementation
   * of the page can be customized to suit different widget libraries.
   */
  public static final String DEFAULT_PAGE_CLASS = "net.xoetrope.xui.XPage";
 
  /**
   * Construct an instance of the builder with the default package set to net.xoetrope.awt
   * @param project the current project
   */
  public XuiBuilder( XProject project )
  {
    currentProject = project;
    init( XPage.XUI_AWT_PACKAGE );
  }

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

  /**
   * Initialize the builder
   * @param packageName the default componnet package or use AWT if none is specified
   */
  protected void init( String packageName )
  {
    Object ref = currentProject.getObject( "Builder" );
    if ( ref == null )
      currentProject.setObject( "Builder", this );
   
    adapter = WidgetAdapter.getInstance();
    if ( packageName == null )
      packageName = XPage.XUI_AWT_PACKAGE;
   
    validationFactories = new Hashtable( 2 );
    componentFactory = new XStyleFactory( currentProject, packageName );
    layoutHelper = (XLayoutHelper)currentProject.getObject( "LayoutHelper" );
    XBasicDataBindingFactory.register( currentProject );
    XRegisteredDataBindingFactory.register( currentProject );
    evaluator = (XDefaultAttributeEvaluator)currentProject.getObject( "DefaultAttributeEvaluator" );
   
    if ( evaluator == null ) {
      evaluator = new XDefaultAttributeEvaluator( currentProject );
      evaluator.setCurrentProject( currentProject );
      currentProject.setObject( "DefaultAttributeEvaluator", evaluator );
    }
   
    customizer = (ComponentCustomizer)currentProject.getObject( "ComponentCustomizer" );
    if ( customizer == null ) {
      customizer = new ComponentCustomizer( currentProject );
      currentProject.setObject( "ComponentCustomizer", customizer );
      try {
        Reader r = currentProject.getBufferedReader( "customizations.xml" );
        if ( r != null )
          customizer.read( r );   
      }
      catch ( Exception ex )
      {
        ex.printStackTrace();
      }   
    }
  }

  /**
   * Set a secondary classloader for loading the classes referenced by pages
   * @param cl the class loader
   */
  public void setClassLoader( ClassLoader cl )
  {
    secondaryClassloader = cl;
  }

  /**
   * 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( currentProject, packageName );
  }
 
  /**
   * Set the current page
   * @param p the page object
   * @since 3.0
   */
  public void setPage( PageSupport p )
  {
    page = p;
  }

  /**
   * Loads an XPage via a reader obtained from the XProject (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 PageSupport loadPage( String defPackageName, String pageName, boolean include )
  {
    packageName = defPackageName;

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

    try {
      if (( r == null ) || !r.ready())
        return null;

      return readPage( r, pageName, ".xml", include );
    }
    catch ( Exception e ) {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "BUILDER", "File NOT found: " + pageName + ". Error: " + e.getMessage() );
    }
    finally {
      if ( !include )
        rootPage = null;
    }
    return null;
  }

  /**
   * 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
   * @param useFrames true to setup the frameset or false to add just a single target area
   * @return true if a frameset is loaded
   */
  public boolean loadFrames( String defPackageName, String frameSetName, XPageDisplay pageDisplay, boolean useFrames )
  {
    if ( !useFrames ) {
      loadSingleTarget( pageDisplay );
      return true;
    }
   
    // Only add frames once
    /** @todo check this beahvior for multi-project applications, secondary projects may be allowed to add targets? */
    boolean addFrames = ( pageDisplay.getNumTargets() == 0 );

    packageName = defPackageName;
    Reader r = null;
    try {
        r = currentProject.getBufferedReader( frameSetName + ".xml", null );
    }
    catch ( Exception ex ) {
    }
   
    if ( r == null ) {
      loadSingleTarget( pageDisplay );
      return true;
    }

    XmlElement model = XmlSource.read( r );
    Vector componentNodes = model.getChildren();
    int numChildren = componentNodes.size();
    if ( numChildren == 0 ) {
      if ( !"NONE".equals( currentProject.getStartupParam( "DefaultTarget" )))
        loadSingleTarget( pageDisplay );
     
      return true;
    }
   
    Hashtable framesetParams = new Hashtable();
    Enumeration framesetAttribNames = model.enumerateAttributeNames();
    while ( framesetAttribNames.hasMoreElements()) {
      String attribName = (String)framesetAttribNames.nextElement();
      framesetParams.put( attribName, (String)model.getAttribute( attribName ));
    }
    pageDisplay.setupFrameset( framesetParams );
   
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )componentNodes.elementAt( i );
      String typeStr = childNode.getName();
      String targetName = childNode.getAttribute( "name" );
      String pageName = childNode.getAttribute( "content" );
      Hashtable params = new Hashtable();
      Enumeration attribNames = childNode.enumerateAttributeNames();
      while ( attribNames.hasMoreElements()) {
        String attribName = (String)attribNames.nextElement();
        params.put( attribName, (String)childNode.getAttribute( attribName ));
      }
     
      if ( typeStr.equals( "Frame" )) {
        try {
          Object constraint = layoutHelper.getConstraint( childNode.getAttribute( "constraint" ));
          int preferredWidth = new Integer( childNode.getAttribute( "width" ) ).intValue();
          int preferredHeight = new Integer( childNode.getAttribute( "height" ) ).intValue();
          XContentHolder target;
          if ( addFrames )
            target = pageDisplay.addTarget( targetName, constraint,
                                            preferredWidth,
                                            preferredHeight,
                                            params );
          else {/** @todo set the target parameters for this new frame */
            target = (XContentHolder)pageDisplay.findTarget( targetName );
            target.setup( targetName, preferredWidth, preferredHeight, params );
          }
         
          if ( pageName != null ) {
            target.setContent( pageName );
            PageSupport targetPage = currentProject.getPageManager().loadPage( pageName );
            pageDisplay.displayPage( targetPage, targetName );
          }
        }
        catch ( NumberFormatException ex1 ) {
          ex1.printStackTrace();
        }
      }
      else if ( typeStr.equals( "Toolbar" )) {
        String constraint = childNode.getAttribute( "constraint" );
        PageSupport toolbar = currentProject.getPageManager().loadPage( pageName );
        pageDisplay.displayDecoration( toolbar, constraint );
      }
    }
    pageDisplay.doLayout();
   
    return true;
  }

  /**
   * Load a single target, used in case a frameset is not loaded
   * @param pageDisplay the object that will display the pages and frameset
   */
  protected void loadSingleTarget( XPageDisplay pageDisplay )
  {
    int clientWidth = 640;
    int clientHeight = 480;
    String temp = currentProject.getStartupParam( "ClientWidth" );
    if ( temp != null )
      clientWidth = Integer.parseInt( temp );
   
    temp = currentProject.getStartupParam( "ClientHeight" );
    if ( temp != null )
      clientHeight = Integer.parseInt( temp );
   
    pageDisplay.addTarget( BuildProperties.CONTENT_TARGET, BorderLayout.CENTER, clientWidth, clientHeight, null );
  }

  /**
   * 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 ext the file extension
   * @param include the page to be loaded is being included in another page
   * @return the page
   */
  public PageSupport readPage( Reader reader, String pageName, String ext, boolean include )
  {
    XmlElement model = XmlSource.read( reader );
    if ( BuildProperties.DEBUG && ( model == null ))
      DebugLogger.logError( "BUILDER", "The file - " + pageName + " - could not be parsed!" );
    setupPage( model, pageName, ext, include );
    eventHandler = page.getEventHandler();
   
    XmlElement componentsNode = null;
    XmlElement eventsNode = null;
    XmlElement dataNode = null;
    XmlElement menuNode = null;
    XmlElement validationsNode = null;
    XmlElement scriptsNode = 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.equals( "components" ))
        componentsNode = childNode;
      else if ( typeStr.equals( "events" ))
        eventsNode = childNode;
      else if ( typeStr.equals( "data" ))
        dataNode = childNode;
      else if ( typeStr.equals( "validations" ))
        validationsNode = childNode;
      else if ( typeStr.equals( "menubar" ))
        menuNode = childNode;
      else if ( typeStr.equals( "attributes" ))
        loadAttributeSet( childNode );
      else if ( typeStr.equals( "include" )) {
        loadPage( packageName, childNode.getAttribute( "file" ), true );
        componentFactory.setParentComponent( page );
      }
      else if ( typeStr.equals( "scripts" ))
        scriptsNode = childNode;
      else
        loadOtherElement( page, childNode );
    }

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

//    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 PageSupport loadClass( String className ) throws ClassNotFoundException, IllegalAccessException, InstantiationException
  {
    XProjectManager.setCurrentProject( currentProject );
    if ( secondaryClassloader != null ) {
      try {
        return (PageSupport)ReflectionHelper.constructViaReflection( secondaryClassloader, className );
      }
      catch ( Exception e ) {}
    }
   
    return (PageSupport)ReflectionHelper.constructViaReflection( null, className );
  }

  /**
   * Load any element other than the predefined node types e.g. Component, Bindings etc...
   * Allows an editor to insert extra data in the page, e.g. guides
   * @param page the page currently being loaded
   * @param childNode the xml element to process
   */
  protected void loadOtherElement( PageSupport page, XmlElement childNode )
  {
    if ( childNode.getName().toLowerCase().equals( "guides" )) {
      try {
        Object gs = ReflectionHelper.constructViaReflection( "net.xoetrope.optional.layout.GuideSupport", XProject.class, currentProject );
        BuilderSupport bs = (BuilderSupport)gs;
        String guideFile = childNode.getAttribute( "include" );
        if (( guideFile != null ) && ( guideFile.length() > 0 ))
          bs.read( guideFile );
        else
          bs.read( childNode );
        page.setAttribute( "guides", "guidesupport", gs );
      }
      catch ( Exception e )
      {
        if ( BuildProperties.DEBUG
          DebugLogger.logWarning( "BUILDER", "Unable to read the guides" );
      }
    }
  }

  /**
   * Loads the page based on the contents of the page tag or by using default
   * values.
   *
   * @param pageName the name of the page
   * @param ext the file extension
   * @param include the page to be loaded is being included in another page
   */
  protected void setupPage( XmlElement model, String pageName, String ext, boolean include )
  {
    XProjectManager.setCurrentProject( currentProject );
    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 = currentProject.getStartupParam( "DefaultClass" );
      }
      catch ( Exception ex ) {
        if ( BuildProperties.DEBUG )
          DebugLogger.trace( "BUILDER", "No class specified and cannot find the DefaultClass startup property" );
      }
      if ( className == null )
        className = DEFAULT_PAGE_CLASS;
    }

    if ( !include ) {
      if ( ( className.indexOf( '.' ) <= 0 ) && ( packageName.length() > 1 ) )          
        className = packageName + ( packageName.endsWith( "." ) ? "" : "." ) + className;     
      try {
        page = loadClass( className );
      }
      catch ( Exception e ) {
        if ( BuildProperties.DEBUG ) {
          // The class may not have been created yet
          if ( e instanceof ClassNotFoundException )
            DebugLogger.trace( "BUILDER", "Unable to load the named class: " + className );
          else {
            // There is a problem in the constructor
            DebugLogger.logError( "BUILDER", "Unable to load the named class: " + className );
            e.printStackTrace();
          }
        }
        try {
          page = (PageSupport)ReflectionHelper.constructViaReflection( null, DEFAULT_PAGE_CLASS );
        }
        catch ( Exception e2 ) {
          e.printStackTrace();
        }
      }

      // 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 = currentProject.getStartupParam( "Validations" );
       
        if ( validationName != null ) {
          if ( BuildProperties.DEBUG )
            DebugLogger.trace( "BUILDER", "Reading validations: " + validationName );
          page.setValidationFactory( getValidationFactory( validationName ) );
        }
      }
      catch ( Exception ex ) {
        ex.printStackTrace();
      }

      if ( page instanceof XContentPane )
        componentFactory.setParentComponent( (( XContentPane )page).getContentPane() );
      else
        componentFactory.setParentComponent( page );
      String styleName = model.getAttribute( "style" );
      if ( styleName != null )
        componentFactory.applyStyle( page, styleName );
    }
    if ( !include ) {
      setPageName( pageName );
      setPageExtension( ext );
    }

//    int width = 0;//, height = 0;
    String layoutMgr = null;
    Hashtable layoutAttributes = null;
    Enumeration attribNamesEnum = model.enumerateAttributeNames();
    while ( attribNamesEnum.hasMoreElements()) {
      String attribName = (String)attribNamesEnum.nextElement();
      String attribValue = (String)model.getAttribute( attribName );
      String attribNameLwr = attribName.toLowerCase();
      if ( attribNameLwr.equals( "class" ))
        ;
      else if ( attribNameLwr.equals( "resource" ))
        ;//setResourceBundle( page, attribValue );
      else if ( attribNameLwr.equals( "layout" )) {
        if ( attribValue != null )
          layoutMgr = attribValue;

        XmlElement layoutElement = model.getFirstChildNamed( "Layout" );
        if ( layoutElement != null ) {
          layoutAttributes = new Hashtable();
          Enumeration layoutAttribNamesEnum = layoutElement.enumerateAttributeNames();
          while ( layoutAttribNamesEnum.hasMoreElements()) {
            String layoutAttribName = (String)layoutAttribNamesEnum.nextElement();
            String layoutAttribValue = (String)layoutElement.getAttribute( layoutAttribName );
            layoutAttributes.put( layoutAttribName, layoutAttribValue );
          }
        }
        else
          layoutAttributes = currentAttributes;
      }
      else
        page.setAttribute( attribName, null, evaluateAttribute( page, attribValue ));

      setPageAttribute( page, attribName, attribValue );
    }
   
    setResourceBundle( page, model.getAttribute( "resource" ));

    /** @todo this method attempts to set all the attributes of the layout and it's parent as the
     * attributes are embedded in the parent element. Need to separate the two and provide a separate
     * element for the layout.
     */
    if ( layoutMgr != null )
      componentFactory.addLayout( include ? componentFactory.getParentComponent() : page, layoutHelper.getLayoutType( layoutMgr ), layoutAttributes );
    rootPage = (PageSupport)page;
  }
 
  /**
   * Set the name of the page's resource bundle
   * @param page the page that owns the resource
   * @param the resource bundle name, minus any extension
   */
  protected void setResourceBundle( PageSupport page, String attribValue )
  {
    String resName;
    if (( attribValue != null ) && ( attribValue.length() > 0 ))
      resName = page.evaluatePath( attribValue );
    else
      resName = currentProject.getStartupParam( "Language" );
   
    componentFactory.setResourceBundle( resName );
    page.getComponentFactory().setResourceBundle( resName );
  }

  /**
   * Get a page attribute (this version does nothing)
   * @param c the component associated with an attribute
   * @param name the attribute name
   * @param value the attribute value
   */
  public void setPageAttribute( Object c, String name, String value )
  {
  }
 
  /**
   * Process an attribute element
   * @param attrElement the attribute element
   */
  public void loadAttributeSet( XmlElement childNode )
  {
    if ( attributeSets == null )
      attributeSets = new Hashtable();
   
    String attribSetName = childNode.getAttribute( "name" );
    Vector attribs = new Vector();
    Enumeration attribNames = childNode.enumerateAttributeNames();
    while ( attribNames.hasMoreElements()) {
      String attribName = (String)attribNames.nextElement();
      if ( attribName.equals( "name" ))
        attribSetName = childNode.getAttribute( "name" );
      else {
        Object[] attribPair = new Object[ 2 ];
        attribPair[ 0 ] = attribName;
        attribPair[ 1 ] = childNode.getAttribute( attribName );
        attribs.add( attribPair );
      }
    }
   
    if (( attribSetName != null ) && ( attribSetName.length() > 0 ))
      attributeSets.put( attribSetName, attribs );
  }
 
  /**
   * Insert the attributes of a named attribute set into the node
   * @param node the node to be modified if it has an atttributes attribute
   */
  public void insertAttributes( XmlElement node )
  {
    String attribSetName = node.getAttribute( "attributes" );
    if (( attribSetName != null ) && ( attribSetName.length() > 0 )) {
      Vector attribs = (Vector)attributeSets.get( attribSetName );
      int numAttribPairs = attribs.size();
      for ( int i = 0; i < numAttribPairs; i ++ ) {
        Object[] attribPair = (Object[])attribs.elementAt( i );
        node.setAttribute( (String)attribPair[ 0 ], (String)attribPair[ 1 ] );
      }
    }
  }

  /**
   * Set the attribute evaluator object.
   * @param e the new evaluator
   */
  public void setAttributeEvaluator( XAttributeEvaluator e )
  {
    evaluator = e;
  }

  /**
   * 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( PageSupport page, String attributeValue )
  {
    if ( attributeValue == null )
      return null;
   
    if ( evaluator != null )
      return evaluator.evaluateAttribute( page, attributeValue );
   
    return page.evaluateAttribute( attributeValue );
  }

  /**
   * 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 String evaluateAttributeAsString( PageSupport page, String attributeValue )
  {
    Object rc = evaluateAttribute( page, attributeValue );
    if ( rc != null )
      return rc.toString();
   
    return null;
  }

  /**
   * 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( Object 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 );
      String childName = childNode.getName();
      if ( childName == null )
        continue;
      else if ( !"Repeat".equalsIgnoreCase( childName ))
        addComponent( childNode );
      else {
        String repeatReference = childNode.getAttribute( "while" );
        pushRepeatReference( repeatReference );
        // Repeat the child addition.
        boolean whileClauseResult = ((Boolean)rootPage.evaluateAttribute( repeatReference )).booleanValue();
        if ( whileClauseResult ) {
          i--;
          addComponents( page, childNode );
        }
        popRepeatReference( repeatReference );
      }
    }
  }

  /**
   * 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( PageSupport page, XmlElement model )
  {
//    String typeStr, methodStr, targetStr, cursorStr;
//    cursorStr = "";

    Vector eventNodes = model.getChildren();
    int numChildren = eventNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )eventNodes.elementAt( i );
      if ( childNode.getName().compareTo( "Repeat" ) != 0 )
        addEvent( childNode );
      else {
        String repeatReference = childNode.getAttribute( "while" );
        pushRepeatReference( repeatReference );
        // Repeat the child addition.      
        boolean whileClauseResult = ((Boolean)rootPage.evaluateAttribute( repeatReference )).booleanValue();
        if ( whileClauseResult ) {
          i--;
          addEvents( page, childNode );
        }
        popRepeatReference( repeatReference );
      }
    }
  }
 
  /**
   * 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 childNode the 'events' XML element
   */
  protected void addEvent( XmlElement childNode )
  {
    String typeStr, methodStr, targetStr, cursorStr;
    cursorStr = "";

    typeStr = methodStr = targetStr = null;

    try {
      Enumeration attribNamesEnum = childNode.enumerateAttributeNames();
      while ( attribNamesEnum.hasMoreElements()) {
        String attribName = (String)attribNamesEnum.nextElement();
        String attribValue = childNode.getAttribute( attribName );
        if ( "type".equals( attribName ))
          typeStr = evaluateAttributeAsString( rootPage, attribValue );
        else if ( "method".equals( attribName )) {
          // Do not evaluate this method... the actual event handler
          // is only meant to be invoked in response to an event.
          methodStr = attribValue;
        }
        else if  ( "target".equals( attribName ))
          targetStr = evaluateAttributeAsString( rootPage, attribValue );
        else if  ( "cursor".equals( attribName ))
          cursorStr = evaluateAttributeAsString( rootPage, attribValue );
        else
          page.setAttribute( attribName, targetStr, evaluateAttribute( page, attribValue ));
      }

      if ( "MenuHandler".equals( typeStr ))
        addHandler( page, getMenuItem( targetStr ), "MenuHandler", methodStr );
      else {
        Object targetComp = page.findComponent( targetStr );
        if ( BuildProperties.DEBUG ) {
          if ( targetComp == null )
            DebugLogger.logError( "BUILDER", "Unable to find the component '" + targetStr + "' named in the event element " + methodStr );
        }
        addHandler( page, targetComp, typeStr, methodStr );
        if ( cursorStr.equals( "true" ))
          adapter.setCursor( targetComp, Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) );
      }
    }
    catch ( Exception e )
    {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "BUILDER", "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 methodName the name of the response method
   */
  public void addHandler( PageSupport xpage, Object targetComp, String typeStr, String methodName )
  {
    eventHandler.addHandler( xpage, targetComp, typeStr, methodName );
  }

  /**
   * 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( PageSupport page, XmlElement model )
  {
    if ( model == null )
      return;

    Vector eventNodes = model.getChildren();
    int numChildren = eventNodes.size();
    for ( int i = 0; i < numChildren; i++ ) {
      XmlElement childNode = ( XmlElement )eventNodes.elementAt( i );
      if ( childNode.getName().compareTo( "Repeat" ) != 0 )
        addBinding( childNode );
      else {
        // Repeat the child addition.
        String repeatReference = childNode.getAttribute( "while" );
        pushRepeatReference( repeatReference );
        boolean whileClauseResult = ((Boolean)rootPage.evaluateAttribute( repeatReference )).booleanValue();
        if ( whileClauseResult ) {
          i--;
          addBindings( page, childNode );
        }
        popRepeatReference( repeatReference );
      }
    }
  }
 
  /**
   * Adds data bindings to the page.
   * @param model the data model
   */
  protected void addBinding( XmlElement childNode )
  {
    String nameStr = null;
    try {
      Hashtable instanceConfig = new Hashtable();
      Enumeration attribNames = childNode.enumerateAttributeNames();
      while ( attribNames.hasMoreElements()) {
        String attribName = (String)attribNames.nextElement();
        String attribValue = childNode.getAttribute( attribName );

        instanceConfig.put( attribName, evaluateAttribute( page, attribValue ));
      }
      instanceConfig.put( "sourcePath", childNode.getAttribute( "source" ));       
      String s = childNode.getAttribute( "output" );
      if ( s != null )
        instanceConfig.put( "outputPath", s );       

      nameStr = (String)instanceConfig.get( "target" );
      if ( nameStr == null ) {
        nameStr = childNode.getAttribute( "target" );
        nameStr = evaluateAttributeAsString( rootPage, nameStr );     
      }
     
      Object targetComp = page.findComponent( nameStr );
      if ( BuildProperties.DEBUG ) {
        if ( targetComp == null )
          DebugLogger.logError( "BUILDER", "While adding the data binding element for the target component: " + nameStr + " Please check that the component name is specified correctly and that the case matches." );
      }

      String typeStr = (String)instanceConfig.get( "type" );
      if (( typeStr!= null ) && typeStr.equals( "custom" )) { 
        if ( BuildProperties.DEBUG )
          DebugLogger.logWarning( "BUILDER", "Deprecated binding configuration, please use the bindings registry (bindings.xml) instead of \'custom\' attributes" );
       
        String customClass = (String)instanceConfig.get( "class" );
        try {
          XDataBinding customBinding = (XDataBinding)Class.forName( customClass.trim() ).newInstance();
          customBinding.setup( currentProject, targetComp, null, instanceConfig );
          page.addBinding( customBinding );
          return;
        }
        catch ( Exception ex ) {
          System.out.println( "could not load binding class - " + customClass );
        }
      }
     
      XDataBinding factoryBinding = getFactoryBinding( page, targetComp, instanceConfig );
      if ( factoryBinding != null )
        page.addBinding( factoryBinding );
      else if ( BuildProperties.DEBUG ) {
        DebugLogger.logError( "BUILDER", "Failed to create the binding for the target: " + nameStr + " and the data source:" + (String)instanceConfig.get( "source" ));
      }
    }
    catch ( Exception e )
    {
      if ( BuildProperties.DEBUG ) {
        DebugLogger.logError( "BUILDER", "Undetermined error while adding the data binding element for the target component: " + nameStr + ". Please check the binding specification and following stack trace." );
        e.printStackTrace();
      }
    }
  }

  /**
   * Try to get a binding factory to construct the binding
   * @param currentProject the current project
   * @param page the page that will own the binding
   * @param compType the component type
   * @param bindingNode the XML element defining the binding
   * @return the new binding if one could be constructed
   */
  public XDataBinding getFactoryBinding( PageSupport page, Object compType, Hashtable instanceConfig )
  {
    XDataBinding binding = null;   
    Vector bindingFactories = page.getProject().getBindingsFactories();
    for ( int i = 0; i < bindingFactories.size(); i++ ) {
      /**
       * @todo These factories need to be extended to determine the binding type
       * from both the component type and model node type. This probably
       * requires some sort of configuration or perhaps a voting mechanism
       * to allow a binding be composed of mulitple bindings and adapters so
       * that it is possible to go from any given node type to any (possibly
       * unknown to XUI) component type. - LO'C 2 May 2005
       */
      binding = ((XDataBindingFactory)bindingFactories.elementAt( i )).getBinding( page, compType, instanceConfig );
      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 );
  }

  /**
   * Set the file extension of the page
   * @param ext the new file extension.
   */
  protected void setPageExtension( String ext )
  {
    page.setExtension( ext );
  }
 
  /**
   * 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 childNode the XML element containing the component specification.
   * @return the new component
   */
  protected Object addComponent( XmlElement childNode )
  {
    String nameStr = null, xStr, yStr, wStr, hStr, contentStr, styleStr,
                     layoutMgr, constraintStr, eagerStr;

    Object comp = null;
    String childName = childNode.getName();
    if ( childName.equals( "Layout" ))
      return null;

    try {
      int x = 0, y = 0, w = 30, h = 20;
      nameStr = wStr = hStr = contentStr = styleStr = layoutMgr =
          constraintStr = eagerStr = null;
      xStr = yStr = "0";
      wStr = hStr = "-1";
      Hashtable layoutAttributes = null;
      Hashtable componentAttributes = new Hashtable(); // Local copy of the component attributes
      currentAttributes = componentAttributes;
     
      insertAttributes( childNode );
     
      Enumeration attribNamesEnum = childNode.enumerateAttributeNames();
      while ( attribNamesEnum.hasMoreElements() ) {
        String attribName = (String)attribNamesEnum.nextElement();
        String attribValue = (String)childNode.getAttribute( attribName );
       
        attribValue = evaluateAttributeAsString( rootPage, attribValue );
        if ( attribName.equals( "x" ))
          xStr = attribValue;
        else if ( attribName.equals( "y" ))
          yStr = attribValue;
        else if ( attribName.equals( "w" ))
          wStr = attribValue;
        else if ( attribName.equals( "h" ))
          hStr = attribValue;
        else if ( attribName.equals( "eager" ))
          eagerStr = attribValue;
        else if ( attribName.equals( "attributes" ))
          continue; // already processed
        else {
          if ( attribName.equals( "name" ))
            nameStr = attribValue;
          else if ( attribName.equals( "layout" )) {
            layoutMgr = attribValue;
            XmlElement layoutElement = childNode.getFirstChildNamed( "Layout" );
            if ( layoutElement != null ) {
              layoutAttributes = new Hashtable();
              Enumeration layoutAttribNamesEnum = layoutElement.enumerateAttributeNames();
              while ( layoutAttribNamesEnum.hasMoreElements()) {
                String layoutAttribName = (String)layoutAttribNamesEnum.nextElement();
                String layoutAttribValue = (String)layoutElement.getAttribute( layoutAttribName );
                layoutAttributes.put( layoutAttribName, layoutAttribValue );
              }
            }
            else
              layoutAttributes = componentAttributes;
          }
          else if ( attribName.equals( "constraint" ))
            constraintStr = attribValue;
          else {
            if ( attribName.equals( "content" ))
              contentStr = attribValue;
            else if ( attribName.equals( "key" ))
              contentStr = attribValue;
            else if ( attribName.equals( "style" )) {
              styleStr = attribValue;
              if ( componentFactory instanceof XStyleFactory )
                continue;
            }
            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
              rootPage.setAttribute( attribName, nameStr, evaluateAttribute( rootPage, attribValue ));
            }

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

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

      if ( includeFileName != null ) {
        if (( nameStr == null ) || ( nameStr.length() == 0 ))
          nameStr = includeFileName;
        page.setAttribute( "Include", nameStr, evaluateAttribute( page, includeFileName ));
        includeFileName = null;
      }
     
      if ( childName.equalsIgnoreCase( "include" )) {
        Object parentObject = componentFactory.getParentComponent();
        includeFileName = evaluateAttributeAsString( page, childNode.getAttribute( "file" ));
        pushIncludeReference( includeFileName );
        loadPage( packageName, includeFileName, true );
        popIncludeReference( includeFileName );
        componentFactory.setParentComponent( parentObject );
        return null;
      }
      else if ( childName.equalsIgnoreCase( "attributes" )) {
        loadAttributeSet( childNode );
        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 (( x >= 0 ) || ( y >= 0 ))
            adapter.setLocation( comp, x, y );
          if (( w > 0 ) || ( h > 0 ))
            adapter.setSize( comp, Math.max( 2, w ), Math.max( 2, h ));
        }
      }
      catch ( Exception e ) {
        if ( BuildProperties.DEBUG )
          e.printStackTrace();
        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( "BUILDER", "Unable to add component: " + nameStr );

            // Try to find the class.
            String clazz = (String)componentAttributes.get( "class" );
            if ( clazz != null )
              comp = componentFactory.addComponent( clazz, x, y, w, h, contentStr, styleStr );
           
            // Construct a dummy component
            if ( comp == null )
              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
      /** @todo this method attempts to set all the attributes of the layout and it's parent as the
       * attributes are embedded in the parent element. Need to separate the two and provide a separate
       * element for the layout.
       */
      if (( adapter.isContainer( comp )) && ( layoutMgr != null ))
        componentFactory.addLayout( comp, layoutHelper.getLayoutType( layoutMgr ), layoutAttributes );

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

      // If a panel is found set it to the parent and recursively add its children
      if ( adapter.isContainer( comp )) {
        Object parentObject = componentFactory.getParentComponent();
        componentFactory.setParentComponent( comp );       
        if ( "true".equals( eagerStr ))
          setComponentAttributes( childName, comp, componentAttributes );
        addComponents( comp, childNode );
        currentAttributes = componentAttributes;
        componentFactory.setParentComponent( parentObject );
        adapter.doLayout( comp );
      }

      // 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;
     
      // Now customize the component
      String customization = (String)childNode.getAttribute( "customizer" );
      if (( customization != null ) && ( customization.length() > 0 ))
        customizer.customize( comp, customization, ComponentCustomizer.POST_CREATE_TIME );


      // Setup any extra attributes specified in the XML
      if ( !"true".equals( eagerStr ))
        setComponentAttributes( childName, comp, componentAttributes );
    }
    catch ( Exception e ) {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "BUILDER", "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( PageSupport page, XmlElement model )
  {
    Object parent = componentFactory.getParentComponent();
    componentFactory.setParentComponent( page );
    String basePackageName = currentProject.getWidgetPackageName() + ".X";
    String attribStr;
    WidgetAdapter adapter = WidgetAdapter.getInstance();
    try {
//      if ( adapter.requiresParent()) {
//        Object[] args = new Object[ 1 ];
//        args[ 0 ] = parent;
//        menuBar = (XAppender)ReflectionHelper.constructViaReflection( basePackageName + XPage.MENUBAR, args );
//      }
//      else 
        menuBar = (XAppender)Class.forName( basePackageName + XPage.MENUBAR ).newInstance();
      menuBar.setName( page.getName());
     
      XStyle menuStyle = null;
      String menuStyleName = model.getAttribute( "style" );
      Color menuBkColor = null;
      Color menuTextColor = null;
      Font menuFont = null;
      if (( menuStyleName != null ) && ( menuStyleName.length() > 0 )) {
        menuStyle = currentProject.getStyleManager().getStyle( menuStyleName );
        menuBkColor = menuStyle.getStyleAsColor( XStyle.COLOR_BACK );
        menuTextColor = menuStyle.getStyleAsColor( XStyle.COLOR_FORE );
        menuFont = currentProject.getStyleManager().getFont( menuStyleName );
        adapter.setBackground( menuBar, menuBkColor );
        adapter.setForeground( menuBar, menuTextColor );
        adapter.setFont( menuBar, menuFont );
      }

      Vector menuNodes = model.getChildren();
      int numMenus = menuNodes.size();
      for ( int i = 0; i < numMenus; i++ ) {
        XmlElement childNode = ( XmlElement )menuNodes.elementAt( i );
        addMenu( menuBar, childNode, basePackageName,
          menuStyle, menuBkColor, menuTextColor, menuFont );
      }
      menuBar.setup();
    }
    catch ( Exception ex ) {
      ex.printStackTrace();
    }
   
    componentFactory.setParentComponent( parent );
  }

  private void addMenu( XAppender menuBar, XmlElement childNode,
    String basePackageName, XStyle menuStyle,
    Color menuBkColor, Color menuTextColor,
    Font menuFont ) throws ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    XAppender menu = null;
    if ( adapter.requiresParent()) {
      Object[] args = new Object[ 1 ];
      args[ 0 ] = menuBar;
      menu = (XAppender)ReflectionHelper.constructViaReflection( basePackageName + XPage.MENU, args );
    }
    else {
      menu = ( XAppender )Class.forName( basePackageName + XPage.MENU ).newInstance();
    }

    ((XTextHolder)menu).setText( componentFactory.translate( childNode.getAttribute( "content" ) ));
    if ( menuStyle != null ) {
      adapter.setBackground( menu, menuBkColor );
      adapter.setForeground( menu, menuTextColor );
      adapter.setFont( menu, menuFont );
    }

    addMenuItems( menu, childNode, basePackageName,
      menuStyle, menuBkColor, menuTextColor, menuFont );

    String attribStr = childNode.getAttribute( "enabled" );
    if (( attribStr != null ) && attribStr.equals( "false" ))
      adapter.setEnabled( menu, false );

    attribStr = childNode.getAttribute( "visible" );
    if (( attribStr != null ) && attribStr.equals( "false" ))
      adapter.setVisible( menu, false );

    menuBar.append( menu, childNode.getAttribute( "name" ) );
  }
 
  private void addMenuItems( XAppender menu, XmlElement childNode,
    String basePackageName, XStyle menuStyle,
    Color menuBkColor, Color menuTextColor,
    Font menuFont ) throws ClassNotFoundException, InstantiationException, IllegalAccessException
  {
    int numMenuItems = childNode.getChildren().size();
    for ( int j = 0; j < numMenuItems; j++ ) {
      XmlElement itemNode = ( XmlElement )childNode.elementAt( j );
      if ( itemNode.getName().equalsIgnoreCase( "Menu" )) {
        addMenu( menu, itemNode,
                basePackageName, menuStyle,
                menuBkColor, menuTextColor,
                menuFont );
      }
      else {
        String menuText = itemNode.getAttribute( "content" );
        if ( menuText != null ) {
          Object menuItem = null;
          if ( adapter.requiresParent()) {
            Object[] args = new Object[ 1 ];
            args[ 0 ] = menu;
            menuItem = ReflectionHelper.constructViaReflection( basePackageName + XPage.MENUITEM, args );
          }
          else {
            menuItem = Class.forName( basePackageName + XPage.MENUITEM ).newInstance();
          }

          if ( menuStyle != null ) {
            adapter.setBackground( menuItem, menuBkColor );
            adapter.setForeground( menuItem, menuTextColor );
            adapter.setFont( menuItem, menuFont );
          }

          ((XTextHolder)menuItem).setText( componentFactory.translate( menuText ));
          if ( menuItem instanceof XAttributedComponent ) {
            // Should these values be translated?
            ((XAttributedComponent)menuItem).setAttribute( "image", componentFactory.translate( itemNode.getAttribute( "image" )));
            ((XAttributedComponent)menuItem).setAttribute( "name", componentFactory.translate( itemNode.getAttribute( "name" )));
          }
          menu.append( menuItem, itemNode.getAttribute( "name" ) );

          String attribStr = itemNode.getAttribute( "enabled" );
          if (( attribStr != null ) && attribStr.equals( "false" ))
            adapter.setEnabled( menuItem, false );

          attribStr = itemNode.getAttribute( "visible" );
          if (( attribStr != null ) && attribStr.equals( "false" ))
            adapter.setVisible( menuItem, false );
        }
        else
          menu.addSeparator();         
      }
    }
  }

  /**
   * Adds validation rules to the components
   * @param page the page
   * @param model the Validations XML element
   */
  protected void addValidations( PageSupport page, XmlElement model )
  {
    Vector validations = model.getChildren();
    int numValidations = validations.size();
    for ( int i = 0; i < numValidations; i++ ) {
      XmlElement childNode = ( XmlElement )validations.elementAt( i );
      Object 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;
      }
     
      try {
        page.addValidation( target, childNode.getAttribute( "rule" ), childNode.getAttribute( "method" ), whenAttribValue, childNode );
      }
      catch ( Exception e ) {
        DebugLogger.logError( "BUILDER", "Could not create the validation - " + childNode.getAttribute( "rule" ) );
      }
    }
  }

  /**
   * Store the scripts in the page
   * @param page the current page
   * @param scriptsNode the scripts xml node
   */
  public void addScripts( PageSupport page, XmlElement scriptsNode )
  {
    page.setAttribute( "source", "scripts", scriptsNode.getContent().trim() );
  }

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

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

        // Set a couple of key attributes
        if ( key.compareTo( "visible" ) == 0 )
          adapter.setVisible( comp, value.equals( "true" ) );
        else if ( key.compareTo( "enabled" ) == 0 )
          adapter.setEnabled( comp, value.equals( "true" ) );
        else {
          int rc = -1;
          ComponentAdapter ca = componentFactory.getComponentAdapter( compType );
          if ( ca != null )
            rc = ca.setProperty( comp, key, value, null );
         
          if (( rc < 0 ) && ( comp instanceof XAttributedComponent )) {
            if ( comp instanceof XAttributedComponentEx )
              rc = ((XAttributedComponentEx)comp).setAttribute( currentProject, key, evaluatedAttribute );
            else
              rc = ((XAttributedComponent)comp).setAttribute( key, evaluatedAttribute );
           
            if ( rc < 0 )
              rc = XAttributedComponentHelper.setAttribute( currentProject, comp, key.toLowerCase(), evaluatedAttribute );          
          }
          /** @todo add a return type to the XAttributedComponent interface and check it
           * so that if the attribute is not set try to use the bean api/reflection to
           * set the attribute based on the attribute name */
          if ( rc != 0 ) {
            try {
              ReflectionHelper.setViaReflection( "set" + key.substring( 0, 1 ).toUpperCase() + key.substring( 1 ),
                comp, evaluatedAttribute );
            }
            catch ( Exception ex ) {
              ex.printStackTrace();
            }
          }
        }
      }
      catch ( Exception ex ) {
        ex.printStackTrace();
      }
    }
   
    if ( comp instanceof XMultiAttributedComponent )
      ((XMultiAttributedComponent)comp).allAttributesSet();   
  }

  /**
   * Gets a reference to a menu object
   * @param name the name of the menu item
   * @return the named manu item
   */
  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 = currentProject.getStartupParam( "ValidationFactory" );
          if (( validationFactName != null ) && ( validationFactName.length() > 0 )) {
            Object[] params = new Object[ 1 ];
            params[ 0 ] = currentProject;
            vf = (XValidationFactory)ReflectionHelper.constructViaReflection( validationFactName, params );
          }
          else
            vf = new XValidationFactory( currentProject );

          vf.addConfigFile( "Project", validationFileName, true );
          validationFactories.put( validationFileName, vf );
        }
        return vf;
      }
      catch ( Exception ex ) {
        if ( BuildProperties.DEBUG )
          DebugLogger.logError( "BUILDER", "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;
  }
 
  /**
   * A method that the editor can overload to keep track of includes
   */
  public void pushIncludeReference( String includeFileName )
  {   
  }

  /**
   * A method that the editor can overload to keep track of includes
   */
  public void popIncludeReference( String includeFileName )
  {   
  }
 
  /**
   * A method that the editor can overload to keep track of repeats
   */
  public void pushRepeatReference( String includeFileName )
  {   
  }

  /**
   * A method that the editor can overload to keep track of repeats
   */
  public void popRepeatReference( String includeFileName )
  {   
  }

  /**
   * Get the page loader type - a unique name identifying the loader
   * @return "xui"
   */
  public String getType()
  {
    return "xui";
  }
}
TOP

Related Classes of net.xoetrope.builder.XuiBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.