0001: package net.xoetrope.builder;
0002:
0003: import java.io.Reader;
0004: import java.util.Enumeration;
0005: import java.util.Hashtable;
0006: import java.util.ResourceBundle;
0007: import java.util.Vector;
0008:
0009: import java.awt.Component;
0010: import java.awt.Container;
0011: import java.awt.TextComponent;
0012: import java.awt.event.FocusEvent;
0013: import java.awt.event.MouseEvent;
0014:
0015: import net.xoetrope.debug.DebugLogger;
0016: import net.xoetrope.xml.XmlElement;
0017: import net.xoetrope.xml.XmlSource;
0018: import net.xoetrope.xui.XAppender;
0019: import net.xoetrope.xui.XAttributedComponent;
0020: import net.xoetrope.xui.XContentPane;
0021: import net.xoetrope.xui.XLayoutHelper;
0022: import net.xoetrope.xui.XModelHolder;
0023: import net.xoetrope.xui.XPage;
0024: import net.xoetrope.xui.XPageDisplay;
0025: import net.xoetrope.xui.XPageLoader;
0026: import net.xoetrope.xui.XProjectManager;
0027: import net.xoetrope.xui.XRadioButtonGroup;
0028: import net.xoetrope.xui.XResourceManager;
0029: import net.xoetrope.xui.XTarget;
0030: import net.xoetrope.xui.build.BuildProperties;
0031: import net.xoetrope.xui.data.XDataBinding;
0032: import net.xoetrope.xui.data.XDataBindingFactory;
0033: import net.xoetrope.xui.data.XListBinding;
0034: import net.xoetrope.xui.data.XModel;
0035: import net.xoetrope.xui.data.XModelAdapter;
0036: import net.xoetrope.xui.data.XRadioBinding;
0037: import net.xoetrope.xui.data.XStateBinding;
0038: import net.xoetrope.xui.data.XTextBinding;
0039: import net.xoetrope.xui.style.XStyleFactory;
0040: import net.xoetrope.xui.validation.XValidationFactory;
0041: import java.util.PropertyResourceBundle;
0042: import java.awt.Cursor;
0043: import net.xoetrope.xui.events.XListenerHelper;
0044: import net.xoetrope.xui.data.XCustomDataBinding;
0045: import net.xoetrope.xui.XComponentFactory;
0046: import net.xoetrope.xui.XTextHolder;
0047:
0048: /**
0049: * A builder of XUI pages from an XML source. An instance of the class is setup
0050: * by XApplet as a secondary page loader so that if a class file for the
0051: * page is not found then an attempt is made to load the page from an XML file
0052: * with the same base name.
0053: * <br>
0054: * Components, Menus, Events, Validations and Data Binding can be configured via the
0055: * XML declarations.
0056: * <br>
0057: * The loading process itself relies heavily on the component factories to do
0058: * the bulk of the work involved in constructing and adding components. The XPage
0059: * class also performs much of the work involved in adding events and binding data.
0060: * <p>Copyright (c) Xoetrope Ltd., 2002-2003</p>
0061: * <p>License: see license.txt</p>
0062: * $Revision: 1.29 $
0063: */
0064: public class XuiBuilder implements XPageLoader {
0065: protected String packageName;
0066: protected XStyleFactory componentFactory;
0067: protected Object checkBoxGroup;
0068: protected XPage page;
0069: protected XPage rootPage;
0070: protected XAppender menuBar;
0071: protected Hashtable validationFactories;
0072: protected static Hashtable currentAttributes;
0073: protected XLayoutHelper layoutHelper;
0074: private static int nextChildId;
0075:
0076: /**
0077: * Construct an instance of the builder with the default package set to net.xoetrope.awt
0078: */
0079: public XuiBuilder() {
0080: init(XPage.XUI_AWT_PACKAGE);
0081: }
0082:
0083: /**
0084: * Construct a new builder and set the default package
0085: * @param packageName the name of the default package e.g. net.xoetrope.awt
0086: */
0087: public XuiBuilder(String packageName) {
0088: init(packageName);
0089: }
0090:
0091: /**
0092: * Initialize the builder
0093: * @param packageName the default componnet package or use AWT if none is specified
0094: */
0095: protected void init(String packageName) {
0096: if (packageName == null)
0097: packageName = XPage.XUI_AWT_PACKAGE;
0098: validationFactories = new Hashtable(2);
0099: componentFactory = new XStyleFactory(packageName);
0100: layoutHelper = componentFactory.getLayoutHelper();
0101: }
0102:
0103: /**
0104: * Set the default package name. The default package name is used when
0105: * creating widgets such that a button class like XButton is instatiated as
0106: * <defPackageName>.XButton.class. By default this expands to
0107: * net.xoetrope.awt.XButton.clas
0108: * @param defPackageName the default package name
0109: */
0110: public void setPackageName(String defPackageName) {
0111: if (defPackageName == null)
0112: packageName = XPage.XUI_AWT_PACKAGE;
0113: else
0114: packageName = defPackageName;
0115: componentFactory = new XStyleFactory(packageName);
0116: }
0117:
0118: /**
0119: * Loads an XPage via a reader obtained from the XResourceManager (searches
0120: * the classpath). The pageName is assumed to be the name of an XML file. For
0121: * example if the pageName is 'welcome' then the 'welcome.xml' file is read as
0122: * a UTF8 encoded XML file (by default).
0123: * @param defPackageName the package or path to the page
0124: * @param pageName the page name or the name of the class implementing the page
0125: * @param include true if the page to be loaded is being included in another
0126: * page in which case any class attribute of the included page is ignored
0127: * @return the page
0128: */
0129: public XPage loadPage(String defPackageName, String pageName,
0130: boolean include) {
0131: packageName = defPackageName;
0132:
0133: Reader r = null;
0134: try {
0135: r = XResourceManager.getBufferedReader(pageName + ".xml",
0136: null);
0137: } catch (Exception ex) {
0138: if (BuildProperties.DEBUG)
0139: DebugLogger.logError("File NOT found: " + pageName);
0140: }
0141: if (r == null)
0142: return null;
0143:
0144: return readPage(r, pageName, include);
0145: }
0146:
0147: /**
0148: * Loads a frameset. If a frameset is used then the calls to the XPageDisplay
0149: * interface should use the form that specifies the target areas. By default
0150: * frames are specified in a file (if found) called frames.xml. The frames
0151: * are added to a container laid out with a BorderLayout.
0152: * <br>
0153: * Frames can be used to control how parts of the screen are updated. The
0154: * default page updated by the page transition/swapping methods is the 'content'
0155: * frame. Other areas can be used as non swapping areas for toolbars,
0156: * navigation controls, sidebars or status areas. Frames may also be hidden
0157: * and shown as needed.
0158: * @param defPackageName the package or path to the page
0159: * @param frameSetName the page name or the name of the class implementing the page
0160: * @param pageDisplay the object that will display the pages and frameset
0161: */
0162: public void loadFrames(String defPackageName, String frameSetName,
0163: XPageDisplay pageDisplay) {
0164: packageName = defPackageName;
0165: Reader r = null;
0166: try {
0167: r = XResourceManager.getBufferedReader(frameSetName
0168: + ".xml", null);
0169: } catch (Exception ex) {
0170: }
0171: if (r == null)
0172: return;
0173:
0174: XmlElement model = XmlSource.read(r);
0175: Vector componentNodes = model.getChildren();
0176: int numChildren = componentNodes.size();
0177: for (int i = 0; i < numChildren; i++) {
0178: XmlElement childNode = (XmlElement) componentNodes
0179: .elementAt(i);
0180: String typeStr = childNode.getName();
0181: if (typeStr.compareTo("Frame") == 0) {
0182: try {
0183: String targetName = childNode.getAttribute("name");
0184: XTarget target = new XTarget(
0185: targetName,
0186: new Integer(childNode.getAttribute("width"))
0187: .intValue(), new Integer(childNode
0188: .getAttribute("height")).intValue());
0189: Object constraint = layoutHelper
0190: .getConstraint(childNode
0191: .getAttribute("constraint"));
0192: target.setName(targetName);
0193: pageDisplay.addTarget(target, constraint);
0194: XPage targetPage = XProjectManager
0195: .getPageManager()
0196: .loadPage(childNode.getAttribute("content"));
0197: pageDisplay.displayPage(targetPage, targetName);
0198: } catch (NumberFormatException ex1) {
0199: }
0200: }
0201: }
0202: }
0203:
0204: /**
0205: * Read an XML description of the page and construct a new XPage. An instance
0206: * of the class specified by the class attribute is constructed or else an
0207: * instance of XPage if no class attribute is specified. The new page is
0208: * populated but is not yet added to its parent.
0209: * <br>
0210: * The startup file parameter 'DefaultClass' is used to obtain a default for
0211: * each page's class if a class parameter is not specified in the page's XML
0212: * <br>
0213: * The startup file parameter 'Validations' is used to obtain a default for
0214: * each page's set of validation rules
0215: *
0216: * @param reader a input stream from which to read the page
0217: * @param pageName the name of the page
0218: * @param include the page to be loaded is being included in another page
0219: * @return the page
0220: */
0221: public XPage readPage(Reader reader, String pageName,
0222: boolean include) {
0223: XmlElement model = XmlSource.read(reader);
0224: setupPage(model, pageName, include);
0225: XmlElement componentsNode = null;
0226: XmlElement eventsNode = null;
0227: XmlElement dataNode = null;
0228: XmlElement menuNode = null;
0229: XmlElement validationsNode = null;
0230:
0231: Vector componentNodes = model.getChildren();
0232: int numChildren = componentNodes.size();
0233: for (int i = 0; i < numChildren; i++) {
0234: XmlElement childNode = (XmlElement) componentNodes
0235: .elementAt(i);
0236: String typeStr = childNode.getName().toLowerCase();
0237: if (typeStr.compareTo("components") == 0)
0238: componentsNode = childNode;
0239: else if (typeStr.compareTo("events") == 0)
0240: eventsNode = childNode;
0241: else if (typeStr.compareTo("data") == 0)
0242: dataNode = childNode;
0243: else if (typeStr.compareTo("validations") == 0)
0244: validationsNode = childNode;
0245: else if (typeStr.compareTo("menubar") == 0)
0246: menuNode = childNode;
0247: else if (typeStr.compareTo("include") == 0) {
0248: loadPage(packageName, childNode.getAttribute("file"),
0249: true);
0250: componentFactory.setParentComponent((Container) page);
0251: }
0252: }
0253:
0254: if (menuNode != null)
0255: addMenu(page, menuNode);
0256: if (componentsNode != null)
0257: addComponents((Container) (include ? componentFactory
0258: .getParentComponent() : page), componentsNode);
0259: if (dataNode != null)
0260: addBindings(page, dataNode);
0261: if (eventsNode != null)
0262: addEvents(page, eventsNode);
0263: if (validationsNode != null)
0264: addValidations(page, validationsNode);
0265:
0266: // String styleValue = ( String )model.getAttribute( "style" );
0267: // if ( styleValue != null )
0268: // currentResource.setStyleName( page, styleValue );
0269:
0270: page.validate();
0271: return page;
0272: }
0273:
0274: /**
0275: * Loads a class as the basis for a page
0276: * @param className the full class name
0277: * @return an instance of the XPage class or a derivative
0278: * @throws ClassNotFoundException
0279: * @throws IllegalAccessException
0280: * @throws InstantiationException
0281: */
0282: protected XPage loadClass(String className)
0283: throws ClassNotFoundException, IllegalAccessException,
0284: InstantiationException {
0285: return (XPage) Class.forName(className).newInstance();
0286: }
0287:
0288: /**
0289: * Loads the page based on the contents of the page tag or by using default
0290: * values.
0291: *
0292: * @param pageName the name of the page
0293: * @param include the page to be loaded is being included in another page
0294: * @return the page
0295: */
0296: protected void setupPage(XmlElement model, String pageName,
0297: boolean include) {
0298: String className = model.getAttribute("class");
0299:
0300: if ((className == null) || (className.length() == 0)) {
0301: // Try to get a default startup class name if none has been specified in the XML
0302: try {
0303: className = XProjectManager.getCurrentProject()
0304: .getStartupParam("DefaultClass");
0305: } catch (Exception ex) {
0306: }
0307: if (className == null)
0308: className = "net.xoetrope.xui.XPage";
0309: }
0310:
0311: if (!include) {
0312: if ((className.indexOf('.') <= 0)
0313: && (packageName.length() > 1))
0314: className = packageName + className;
0315: try {
0316: page = loadClass(className);
0317: } catch (Exception e) {
0318: if (BuildProperties.DEBUG)
0319: DebugLogger
0320: .trace("Unable to load the named class: "
0321: + className);
0322: page = new XPage();
0323: }
0324: setPageName(pageName);
0325:
0326: // Perhaps use classname by default
0327: // or check for existance of the file within XPage ... might add delay in constructing non validated pages
0328: try {
0329: String validationName = model
0330: .getAttribute("Validations");
0331: if (validationName == null
0332: || validationName.length() == 0)
0333: validationName = XProjectManager
0334: .getCurrentProject().getStartupParam(
0335: "Validations");
0336: if (validationName != null) {
0337: if (BuildProperties.DEBUG)
0338: DebugLogger.trace("Reading validations: "
0339: + validationName);
0340: page
0341: .setValidationFactory(getValidationFactory(validationName));
0342: }
0343: } catch (Exception ex) {
0344: ex.printStackTrace();
0345: }
0346:
0347: if (page instanceof XContentPane)
0348: componentFactory
0349: .setParentComponent(((XContentPane) page)
0350: .getContentPane());
0351: else
0352: componentFactory.setParentComponent((Container) page);
0353: String styleName = model.getAttribute("style");
0354: if (styleName != null)
0355: componentFactory.applyStyle(page, styleName);
0356: }
0357:
0358: int width = 0, height = 0;
0359: String layoutMgr = null;
0360: Enumeration attribNamesEnum = model.enumerateAttributeNames();
0361: while (attribNamesEnum.hasMoreElements()) {
0362: String attribName = (String) attribNamesEnum.nextElement();
0363: String attribValue = (String) model
0364: .getAttribute(attribName);
0365: if (attribName.compareTo("class") == 0)
0366: ;
0367: else if (attribName.compareTo("resource") == 0) {
0368: String resName;
0369: if ((attribValue != null) && (attribValue.length() > 0))
0370: resName = page.evaluatePath(attribValue);
0371: else
0372: resName = XResourceManager
0373: .getStartupParam("Language");
0374: componentFactory.setResourceBundle(resName);
0375: page.getComponentFactory().setResourceBundle(resName);
0376: } else if (attribName.compareTo("layout") == 0) {
0377: if (attribValue != null)
0378: layoutMgr = attribValue;
0379: } else
0380: page.setAttribute(attribName, null, evaluateAttribute(
0381: page, attribValue));
0382:
0383: setPageAttribute(page, attribName, attribValue);
0384: }
0385:
0386: if (layoutMgr != null)
0387: componentFactory.addLayout(page, layoutHelper
0388: .getLayoutType(layoutMgr), currentAttributes);
0389: rootPage = (XPage) page;
0390: }
0391:
0392: /**
0393: * Get a page attribute (this version does nothing)
0394: * @param c
0395: * @param name
0396: * @param value
0397: */
0398: public void setPageAttribute(Component c, String name, String value) {
0399: }
0400:
0401: /**
0402: * Get the value of an attribute.
0403: * @param page the page being loaded
0404: * @param attributeValue the raw value of the attribute
0405: * @return the evaluated value of the attribute
0406: */
0407: public Object evaluateAttribute(XPage page, String attributeValue) {
0408: return page.evaluateAttribute(attributeValue);
0409: }
0410:
0411: /**
0412: * Adds the elements specified by the Components element and its children
0413: * @param page the new page object
0414: * @param model the Components XML element (and implicitly its children)
0415: */
0416: protected void addComponents(Component page, XmlElement model) {
0417: // componentFactory.setParentComponent( (Container)page );
0418:
0419: Vector componentNodes = model.getChildren();
0420: int numChildren = componentNodes.size();
0421: for (int i = 0; i < numChildren; i++) {
0422: XmlElement childNode = (XmlElement) componentNodes
0423: .elementAt(i);
0424: addComponent(childNode);
0425: }
0426: }
0427:
0428: /**
0429: * Adds the event handlers. Events are specified in the XML as having 'type'
0430: * (e.g. MouseHandler, ActionHandler etc...), 'name' (the method name to be
0431: * invoked) and 'target' (the name of the component to which the handler is
0432: * attached) attributes
0433: * @param page The page that contains the response methods
0434: * @param model the 'events' XML element
0435: */
0436: protected void addEvents(XPage page, XmlElement model) {
0437: String typeStr, methodStr, targetStr, cursorStr;
0438: cursorStr = "";
0439:
0440: Vector eventNodes = model.getChildren();
0441: int numChildren = eventNodes.size();
0442: for (int i = 0; i < numChildren; i++) {
0443: typeStr = methodStr = targetStr = null;
0444:
0445: XmlElement childNode = (XmlElement) eventNodes.elementAt(i);
0446: try {
0447: Enumeration attribNamesEnum = childNode
0448: .enumerateAttributeNames();
0449: while (attribNamesEnum.hasMoreElements()) {
0450: String attribName = (String) attribNamesEnum
0451: .nextElement();
0452: String attribValue = childNode
0453: .getAttribute(attribName);
0454: if (attribName.compareTo("type") == 0)
0455: typeStr = attribValue;
0456: else if (attribName.compareTo("method") == 0)
0457: methodStr = attribValue;
0458: else if (attribName.compareTo("target") == 0)
0459: targetStr = attribValue;
0460: else if (attribName.compareTo("cursor") == 0)
0461: cursorStr = attribValue;
0462: else
0463: page.setAttribute(attribName, targetStr,
0464: attribValue);
0465: }
0466:
0467: if (typeStr.compareTo("MenuHandler") == 0)
0468: page.addMenuHandler(getMenuItem(targetStr),
0469: methodStr);
0470: else {
0471: Component targetComp = page
0472: .findComponent(targetStr);
0473: if (BuildProperties.DEBUG) {
0474: if (targetComp == null)
0475: DebugLogger
0476: .logError("Unable to find the component '"
0477: + targetStr
0478: + "' named in the event element "
0479: + methodStr);
0480: }
0481: addHandler(page, targetComp, typeStr, methodStr);
0482: if (cursorStr.equals("true"))
0483: targetComp
0484: .setCursor(Cursor
0485: .getPredefinedCursor(Cursor.HAND_CURSOR));
0486: }
0487: } catch (Exception e) {
0488: if (BuildProperties.DEBUG)
0489: DebugLogger
0490: .logError("While adding the event element: "
0491: + methodStr);
0492:
0493: e.printStackTrace();
0494: }
0495: }
0496: }
0497:
0498: /**
0499: * Adds an event handler.
0500: * @param xpage The page that contains the response methods
0501: * @param targetComp the component to which the event handler is added
0502: * @param typeStr the type of handler
0503: * @param name the name of the response method
0504: */
0505: protected void addHandler(XPage xpage, Component targetComp,
0506: String typeStr, String name) {
0507: if (typeStr.compareTo("MouseHandler") == 0)
0508: page.addMouseHandler(targetComp, name);
0509: else if (typeStr.compareTo("MouseMotionHandler") == 0)
0510: page.addMouseMotionHandler(targetComp, name);
0511: else if (typeStr.compareTo("ActionHandler") == 0)
0512: page.addActionHandler(targetComp, name);
0513: else if (typeStr.compareTo("FocusHandler") == 0)
0514: page.addFocusHandler(targetComp, name);
0515: else if (typeStr.compareTo("ItemHandler") == 0)
0516: page.addItemHandler(targetComp, name);
0517: else if (typeStr.compareTo("KeyHandler") == 0)
0518: page.addKeyHandler(targetComp, name);
0519: else if (typeStr.compareTo("TextHandler") == 0)
0520: page.addTextHandler(targetComp, name);
0521: else {
0522: try {
0523: ((XListenerHelper) targetComp).addHandler(page,
0524: typeStr, name);
0525: } catch (NoSuchMethodException ex) {
0526: if (BuildProperties.DEBUG)
0527: DebugLogger
0528: .logError("Unable to add the event handler method: "
0529: + name);
0530: }
0531: }
0532: }
0533:
0534: /**
0535: * Register a data binding factory. An add-on library or plug-in may wish to
0536: * customize the data bindings, particularly if it provides extended model
0537: * node types. Such extended nodes may need adapters to be useful for some or
0538: * all widgets and other binding contexts. Registration of the binding factory
0539: * allows the necessary adapters and bindings to be created on request.
0540: * @param fact the new binding factory
0541: */
0542: public static void registerBindingFactory(XDataBindingFactory fact) {
0543: XProjectManager.getCurrentProject().getBindingsFactories()
0544: .addElement(fact);
0545: }
0546:
0547: /**
0548: * Adds data bindings to the page.
0549: * @param page the page to which the component/data bindings are added
0550: * @param model the data model
0551: */
0552: protected void addBindings(XPage page, XmlElement model) {
0553: if (model == null)
0554: return;
0555:
0556: String nameStr = null, srcStr, adapterStr, typeStr, attribStr;
0557: XModel rootNode = XProjectManager.getModel();
0558:
0559: Vector eventNodes = model.getChildren();
0560: int numChildren = eventNodes.size();
0561: for (int i = 0; i < numChildren; i++) {
0562: XmlElement childNode = (XmlElement) eventNodes.elementAt(i);
0563: try {
0564: nameStr = childNode.getAttribute("target");
0565: srcStr = childNode.getAttribute("source");
0566: typeStr = childNode.getAttribute("type");
0567: attribStr = childNode.getAttribute("attrib");
0568: XModel srcModel = (XModel) rootNode.get(page
0569: .evaluatePath(srcStr));
0570:
0571: Component targetComp = page.findComponent(nameStr);
0572: XDataBinding factoryBinding = getFactoryBinding(
0573: targetComp, srcModel, childNode);
0574: if (factoryBinding != null)
0575: page.addBinding(factoryBinding);
0576: else {
0577: if (typeStr != null
0578: && typeStr.compareTo("custom") == 0) {
0579: String className = childNode
0580: .getAttribute("class");
0581: XDataBinding binding = (XDataBinding) Class
0582: .forName(className).newInstance();
0583: ((XCustomDataBinding) binding).setup(
0584: targetComp, childNode);
0585: page.addBinding(binding);
0586: } else {
0587: String className = targetComp.getClass()
0588: .getName();
0589: if (className.indexOf("XComboBox") >= 0) {
0590: XListBinding binding;
0591:
0592: // Check for a specific binding
0593: adapterStr = childNode
0594: .getAttribute("adapter");
0595: if (adapterStr != null) {
0596: // Assume a table is bound to the node
0597: XModelAdapter adapter = (XModelAdapter) Class
0598: .forName(adapterStr)
0599: .newInstance();
0600: XModel tableModel = (XModel) rootNode
0601: .get(srcStr);
0602: adapter.setModel(tableModel);
0603: binding = new XListBinding(targetComp,
0604: adapter);
0605: } else { // Otherwise static data is being bound
0606: String destStr = childNode
0607: .getAttribute("output");
0608: String saveStr = childNode
0609: .getAttribute("saveToSource");
0610: if (destStr == null)
0611: destStr = srcStr;
0612:
0613: XModel dstModel = (XModel) rootNode
0614: .get(page.evaluatePath(destStr));
0615: binding = new XListBinding(
0616: targetComp,
0617: srcStr,
0618: destStr,
0619: srcModel,
0620: dstModel,
0621: (saveStr != null ? saveStr
0622: .equals("true") : false));
0623: }
0624:
0625: // Check and set the unique rows property
0626: String attrib = childNode
0627: .getAttribute("unique");
0628: if ((attrib != null)
0629: && (attrib.compareTo("true") == 0))
0630: binding.setUseUnique(true);
0631: page.addBinding(binding);
0632: } else if (className.indexOf("XEdit") >= 0)
0633: page.addBinding(new XTextBinding(
0634: targetComp, srcStr, srcModel,
0635: attribStr));
0636: else if (className.indexOf("XTextArea") >= 0)
0637: page.addBinding(new XTextBinding(
0638: targetComp, srcStr, srcModel,
0639: attribStr));
0640: else if (className.indexOf("XButton") >= 0)
0641: page.addBinding(new XTextBinding(
0642: targetComp, srcStr, srcModel));
0643: else if (className.indexOf("XLabel") >= 0)
0644: page.addBinding(new XTextBinding(
0645: targetComp, srcStr, srcModel,
0646: attribStr));
0647: else if (className.indexOf("XRadioButton") >= 0) {
0648: if (typeStr != null) {
0649: if (typeStr.equals("text"))
0650: page.addBinding(new XTextBinding(
0651: targetComp, srcStr,
0652: srcModel, attribStr));
0653: else if (typeStr.equals("state")) {
0654: XStateBinding sb = new XStateBinding(
0655: targetComp,
0656: srcStr,
0657: srcModel,
0658: childNode
0659: .getAttribute("leaf"));
0660: String destStr = childNode
0661: .getAttribute("output");
0662: if (destStr != null)
0663: sb.setOutputPath(destStr);
0664: page.addBinding(sb);
0665: }
0666: } else
0667: page.addBinding(new XRadioBinding(
0668: targetComp, srcStr, srcModel));
0669: } else if (className.indexOf("XCheckbox") >= 0) {
0670: if ((typeStr != null)
0671: && (typeStr.equals("text")))
0672: page.addBinding(new XTextBinding(
0673: targetComp, srcStr, srcModel,
0674: attribStr));
0675: else {
0676: XStateBinding sb = new XStateBinding(
0677: targetComp, srcStr, srcModel,
0678: childNode.getAttribute("leaf"));
0679: String destStr = childNode
0680: .getAttribute("output");
0681: if (destStr != null)
0682: sb.setOutputPath(destStr);
0683: page.addBinding(sb);
0684: }
0685: } else if (className.indexOf("XTable") >= 0) {
0686: XModel tableModel = (XModel) rootNode
0687: .get(srcStr);
0688: ((XModelHolder) targetComp)
0689: .setModel(tableModel);
0690: } else if (targetComp instanceof TextComponent)
0691: page.addBinding(new XTextBinding(
0692: targetComp, srcStr, srcModel,
0693: attribStr));
0694: }
0695: }
0696: } catch (Exception e) {
0697: if (BuildProperties.DEBUG)
0698: DebugLogger
0699: .logError("While adding the data binding element: "
0700: + nameStr);
0701: e.printStackTrace();
0702: }
0703: }
0704: }
0705:
0706: /**
0707: * Try to get a binding factory to construct the binding
0708: * @param compType the component type
0709: * @param model the source data model
0710: * @param bindingNode the XML element defining the binding
0711: * @return the new binding if one could be constructed
0712: */
0713: protected XDataBinding getFactoryBinding(Component compType,
0714: XModel model, XmlElement bindingNode) {
0715: XDataBinding binding = null;
0716: Vector bindingFactories = XProjectManager.getCurrentProject()
0717: .getBindingsFactories();
0718: for (int i = 0; i < bindingFactories.size(); i++) {
0719: binding = ((XDataBindingFactory) bindingFactories
0720: .elementAt(i)).getBinding(compType, model,
0721: bindingNode);
0722: if (binding != null)
0723: return binding;
0724: }
0725:
0726: return null;
0727: }
0728:
0729: /**
0730: * Set the name of the page
0731: * @param pageName the new page name.
0732: */
0733: protected void setPageName(String pageName) {
0734: page.setName(pageName);
0735: }
0736:
0737: /**
0738: * Adds an individual component element to the page (this method may be called
0739: * recursively for nested elements). Several methods will be attempted until a
0740: * component is successfully created. Firstly the built-in component types are
0741: * checked, then any additional registered component constructors. The types
0742: * can be specified by type ID, type name or class name.
0743: * @param childName the name of the child element
0744: * @param childNode the XML element containing the component specification.
0745: * @return the new component
0746: */
0747: protected Component addComponent(XmlElement childNode) {
0748: String nameStr = null, typeStr, xStr, yStr, wStr, hStr, contentStr, styleStr, classStr, layoutMgr, constraintStr, opaqueStr;
0749:
0750: Component comp = null;
0751: String childName = childNode.getName();
0752:
0753: try {
0754: int x = 0, y = 0, w = 30, h = 20;
0755: nameStr = typeStr = wStr = hStr = contentStr = styleStr = classStr = layoutMgr = constraintStr = opaqueStr = null;
0756: xStr = yStr = "0";
0757: wStr = hStr = "-1";
0758: Hashtable componentAttributes = new Hashtable(); // Local copy of the component attributes
0759: currentAttributes = componentAttributes;
0760: Enumeration attribNamesEnum = childNode
0761: .enumerateAttributeNames();
0762: while (attribNamesEnum.hasMoreElements()) {
0763: String attribName = (String) attribNamesEnum
0764: .nextElement();
0765: String attribValue = (String) childNode
0766: .getAttribute(attribName);
0767: if (attribName.compareTo("type") == 0)
0768: typeStr = attribValue;
0769: else if (attribName.compareTo("x") == 0)
0770: xStr = attribValue;
0771: else if (attribName.compareTo("y") == 0)
0772: yStr = attribValue;
0773: else if (attribName.compareTo("w") == 0)
0774: wStr = attribValue;
0775: else if (attribName.compareTo("h") == 0)
0776: hStr = attribValue;
0777: else {
0778: Object retObj = evaluateAttribute(rootPage,
0779: attribValue);
0780: attribValue = retObj.toString();
0781: if (attribName.compareTo("name") == 0)
0782: nameStr = attribValue;
0783: else if (attribName.compareTo("class") == 0)
0784: classStr = attribValue;
0785: else if (attribName.compareTo("layout") == 0)
0786: layoutMgr = attribValue;
0787: else if (attribName.compareTo("constraint") == 0)
0788: constraintStr = attribValue;
0789: else {
0790: if (attribName.compareTo("content") == 0)
0791: contentStr = attribValue;
0792: else if (attribName.compareTo("key") == 0)
0793: contentStr = attribValue;
0794: else if (attribName.compareTo("style") == 0)
0795: styleStr = attribValue;
0796: else if (attribName.compareTo("opaque") == 0)
0797: opaqueStr = attribValue;
0798: else {
0799: if (nameStr == null) {
0800: nameStr = childNode
0801: .getAttribute("name");
0802: // Synthesise a name to distinguish this component
0803: if (nameStr == null)
0804: nameStr = "child"
0805: + new Integer(++nextChildId)
0806: .toString();
0807: }
0808:
0809: // Save a copy of the attributes in the page for post creation usage
0810: ((XPage) rootPage).setAttribute(attribName,
0811: nameStr, attribValue);
0812: }
0813:
0814: // Save a local copy for use during the construction process
0815: componentAttributes
0816: .put(attribName, attribValue);
0817: }
0818: }
0819: }
0820:
0821: x = y = w = h = 0;
0822: x = getInt(xStr, x);
0823: y = getInt(yStr, y);
0824: w = getInt(wStr, w);
0825: h = getInt(hStr, h);
0826:
0827: if (typeStr != null) // Load an enumerated type
0828: childName = typeStr;
0829:
0830: if (classStr != null) // Load a specified type class
0831: comp = componentFactory.addClass(classStr, x, y, w, h,
0832: styleStr);
0833: else { // Load by name
0834: if (childName.compareTo("Include") == 0) {
0835: Component parentObject = componentFactory
0836: .getParentComponent();
0837: loadPage(packageName, childNode
0838: .getAttribute("file"), true);
0839: componentFactory.setParentComponent(parentObject);
0840: return null;
0841: }
0842:
0843: // Load with a name like 'Button'
0844: try {
0845: if (constraintStr == null)
0846: comp = componentFactory.addComponent(childName,
0847: x, y, w, h, contentStr, styleStr);
0848: else {
0849: comp = componentFactory.addComponent(childName,
0850: layoutHelper
0851: .getConstraint(constraintStr),
0852: contentStr, styleStr);
0853: if (w > 0)
0854: comp.setSize(w, h);
0855: }
0856: } catch (Exception e) {
0857: comp = null;
0858: } finally {
0859: if (comp == null) {
0860: componentAttributes.put("x", xStr);
0861: componentAttributes.put("y", yStr);
0862: componentAttributes.put("w", wStr);
0863: componentAttributes.put("h", hStr);
0864: Object obj = componentFactory.addElement(
0865: childName, nameStr, contentStr,
0866: componentAttributes);
0867: if (obj == null) {
0868: if (BuildProperties.DEBUG)
0869: DebugLogger
0870: .logError("Unable to add component: "
0871: + nameStr);
0872:
0873: // Construct a dummy component
0874: comp = componentFactory.addComponent(
0875: XPage.UNKNOWN, x, y, w, h,
0876: contentStr, styleStr);
0877: componentAttributes.put("type", childName);
0878: }
0879: }
0880: }
0881: }
0882:
0883: if (comp == null)
0884: return null;
0885:
0886: // Add the layout manager if one has been specified
0887: if ((comp instanceof Container) && (layoutMgr != null))
0888: componentFactory.addLayout((Container) comp,
0889: layoutHelper.getLayoutType(layoutMgr),
0890: componentAttributes);
0891:
0892: // Set the component name
0893: comp.setName(nameStr);
0894:
0895: // If a panel is found set it to the parent and recursively add its children
0896: if (comp instanceof Container) {
0897: Component parentObject = componentFactory
0898: .getParentComponent();
0899: componentFactory.setParentComponent(comp);
0900: addComponents(comp, childNode);
0901: currentAttributes = componentAttributes;
0902: componentFactory.setParentComponent(parentObject);
0903: comp.doLayout();
0904: }
0905:
0906: // Special handling for radio buttons
0907: // For the first rb create a group
0908: // For subsequent rbs add them to the group
0909: // If another type of component is found reset the groups
0910: if (comp instanceof XRadioButtonGroup) {
0911: XRadioButtonGroup rb = ((XRadioButtonGroup) comp);
0912: if (checkBoxGroup == null) {
0913: // The radio button may already have a group
0914: if (rb.getRadioButtonGroup() == null)
0915: checkBoxGroup = rb.createGroup();
0916: else
0917: checkBoxGroup = rb.getRadioButtonGroup();
0918: } else
0919: rb.setRadioButtonGroup(checkBoxGroup);
0920: } else
0921: checkBoxGroup = null;
0922:
0923: // Setup any extra attributes specified in the XML
0924: setComponentAttributes(comp, componentAttributes);
0925: } catch (Exception e) {
0926: if (BuildProperties.DEBUG)
0927: DebugLogger
0928: .logError("While adding the component element: "
0929: + nameStr);
0930: e.printStackTrace();
0931: }
0932:
0933: return comp;
0934: }
0935:
0936: /**
0937: * Adds a menu to the application. Although specified in the XML as part of the
0938: * page the menu is actually added to the application frame.
0939: * @param page the page
0940: * @param model the Menu XML element
0941: */
0942: protected void addMenu(XPage page, XmlElement model) {
0943: Component parent = componentFactory.getParentComponent();
0944: componentFactory.setParentComponent((Container) page);
0945: String basePackageName = XResourceManager.getPackageName()
0946: + ".X";
0947: try {
0948: menuBar = (XAppender) Class.forName(
0949: basePackageName + XPage.MENUBAR).newInstance();
0950:
0951: Vector menuNodes = model.getChildren();
0952: int numMenus = menuNodes.size();
0953: for (int i = 0; i < numMenus; i++) {
0954: XmlElement childNode = (XmlElement) menuNodes
0955: .elementAt(i);
0956: XAppender menu = (XAppender) Class.forName(
0957: basePackageName + XPage.MENU).newInstance();
0958: ((XTextHolder) menu).setText(componentFactory
0959: .translate(childNode.getAttribute("content")));
0960:
0961: int numMenuItems = childNode.getChildren().size();
0962: for (int j = 0; j < numMenuItems; j++) {
0963: XmlElement itemNode = (XmlElement) childNode
0964: .elementAt(j);
0965: Object menuItem = Class.forName(
0966: basePackageName + XPage.MENUITEM)
0967: .newInstance();
0968: ((XTextHolder) menuItem)
0969: .setText(componentFactory
0970: .translate(itemNode
0971: .getAttribute("content")));
0972: menu
0973: .append(menuItem, itemNode
0974: .getAttribute("name"));
0975: }
0976: menuBar.append(menu, childNode.getAttribute("name"));
0977: }
0978: menuBar.setup();
0979: } catch (Exception ex) {
0980: }
0981: componentFactory.setParentComponent((Container) parent);
0982: }
0983:
0984: /**
0985: * Adds validation rules to the components
0986: * @param page the page
0987: * @param model the Validations XML element
0988: */
0989: protected void addValidations(XPage page, XmlElement model) {
0990: Vector validations = model.getChildren();
0991: int numValidations = validations.size();
0992: for (int i = 0; i < numValidations; i++) {
0993: XmlElement childNode = (XmlElement) validations
0994: .elementAt(i);
0995: Component target = page.findComponent(childNode
0996: .getAttribute("target"));
0997: String whenAttrib = childNode.getAttribute("when");
0998: int whenAttribValue = FocusEvent.FOCUS_LOST;
0999: if (whenAttrib != null) {
1000: if (whenAttrib.toLowerCase().compareTo("mouseclicked") == 0)
1001: whenAttribValue = MouseEvent.MOUSE_CLICKED;
1002: }
1003: page.addValidation(target, childNode.getAttribute("rule"),
1004: childNode.getAttribute("method"), whenAttribValue);
1005: }
1006: }
1007:
1008: /**
1009: * Iterate through the attributes and set the attributes for a component
1010: * @param comp the component
1011: * @param attribs the attributes.
1012: */
1013: protected void setComponentAttributes( Component comp, Hashtable attribs )
1014: {
1015: String compName = page.getComponentName( comp );
1016:
1017: Enumeration enum = attribs.keys();
1018: while ( enum.hasMoreElements() ) {
1019: try {
1020: String key = ( String )enum.nextElement();
1021: String value = ( String )attribs.get( key );
1022: page.setAttribute( key, compName, value );
1023:
1024: // Set a couple of key attributes
1025: if ( key.compareTo( "visible" ) == 0 )
1026: comp.setVisible( value.equals( "true" ) );
1027: else if ( key.compareTo( "enabled" ) == 0 )
1028: comp.setEnabled( value.equals( "true" ) );
1029: else if ( comp instanceof XAttributedComponent )
1030: ( ( XAttributedComponent )comp ).setAttribute( key, value );
1031: }
1032: catch ( Exception ex ) {
1033: }
1034: }
1035: }
1036:
1037: /**
1038: * Gets a reference to a menu object
1039: * @param name the name of the menu item
1040: * @return
1041: */
1042: protected Object getMenuItem(String name) {
1043: return menuBar.getObject(name);
1044: }
1045:
1046: /**
1047: * Construct a validation factory appropriate to this builder
1048: * @param validationFileName the validations file to read
1049: * @return the validation factory
1050: */
1051: protected XValidationFactory getValidationFactory(
1052: String validationFileName) {
1053: if ((validationFileName != null)
1054: && (validationFileName.length() > 0)) {
1055: try {
1056: XValidationFactory vf = (XValidationFactory) validationFactories
1057: .get(validationFileName);
1058: if (vf == null) {
1059: String validationFactName = XProjectManager
1060: .getCurrentProject().getStartupParam(
1061: "ValidationFactory");
1062: if (validationFactName != null) {
1063: vf = (XValidationFactory) Class.forName(
1064: validationFactName).newInstance();
1065: vf.read(XResourceManager.getBufferedReader(
1066: validationFileName, null));
1067: } else {
1068: vf = new XValidationFactory(XResourceManager
1069: .getBufferedReader(validationFileName,
1070: null));
1071: }
1072: validationFactories.put(validationFileName, vf);
1073: }
1074: return vf;
1075: } catch (Exception ex) {
1076: if (BuildProperties.DEBUG)
1077: DebugLogger
1078: .logError("Unable to read the validations file: "
1079: + ex.getMessage()
1080: + ", "
1081: + ex.getMessage());
1082: }
1083: }
1084: return null;
1085: }
1086:
1087: /**
1088: * Convert a string to an int or else return the default value
1089: * @param s the number as a string
1090: * @param def the default value
1091: * @return the converted value
1092: */
1093: protected int getInt(String s, int def) {
1094: try {
1095: return new Integer(s).intValue();
1096: } catch (NumberFormatException ex) {
1097: }
1098: return def;
1099: }
1100:
1101: /**
1102: * Get the table of component attributes
1103: * @return the attributes hash table.
1104: */
1105: public static Hashtable getCurrentAttributes() {
1106: return currentAttributes;
1107: }
1108: }
|