0001: package com.xoetrope.builder.generic;
0002:
0003: import com.xoetrope.carousel.build.BuildProperties;
0004: import java.awt.Component;
0005: import java.awt.Container;
0006: import java.awt.LayoutManager;
0007: import java.io.BufferedReader;
0008: import java.io.InputStream;
0009: import java.io.InputStreamReader;
0010: import java.io.Reader;
0011: import java.net.MalformedURLException;
0012: import java.net.URL;
0013: import java.net.URLConnection;
0014: import java.util.ArrayList;
0015: import java.util.Hashtable;
0016: import java.util.Properties;
0017: import java.util.Stack;
0018: import java.util.Vector;
0019: import net.xoetrope.builder.XuiBuilder;
0020: import net.xoetrope.debug.DebugLogger;
0021: import net.xoetrope.optional.data.XOptionalDataSource;
0022: import net.xoetrope.optional.layout.LayerLayout;
0023: import net.xoetrope.optional.layout.ColumnLayout;
0024: import net.xoetrope.registry.ComponentAdapter;
0025: import net.xoetrope.registry.ComponentCustomizer;
0026: import net.xoetrope.swing.XPanel;
0027: import net.xoetrope.xui.PageSupport;
0028: import net.xoetrope.xml.XmlElement;
0029: import net.xoetrope.xml.XmlSource;
0030: import net.xoetrope.xui.WidgetAdapter;
0031: import net.xoetrope.xui.XAttributedComponent;
0032: import net.xoetrope.xui.XImageHolder;
0033: import net.xoetrope.xui.XPage;
0034: import net.xoetrope.xui.XProject;
0035: import net.xoetrope.xui.XRadioButtonGroup;
0036: import net.xoetrope.xui.XTextHolder;
0037: import net.xoetrope.xui.data.XDataBinding;
0038: import net.xoetrope.xui.data.XModel;
0039: import net.xoetrope.xui.helper.XuiUtilities;
0040:
0041: //import org.openide.util.Exceptions;
0042:
0043: /**
0044: * A generic builder for forms/pages based on generation markup. The builder can
0045: * create XUI pages on-the-fly or it can be used to convert and save a page to a
0046: * xui format.
0047: * <p> Copyright (c) Xoetrope Ltd., 2001-2008, This software is licensed under
0048: * the GNU Public License (GPL), please see license.txt for more details. If
0049: * you make commercial use of this software you must purchase a commercial
0050: * license from Xoetrope.</p>
0051: */
0052: public class XGenericBuilder extends XuiBuilder {
0053: protected XGenericBuilderAttributeEvaluator evaluator;
0054:
0055: //private static Hashtable swingConstants;
0056: protected Hashtable typeMap;
0057: protected Hashtable processors;
0058: protected ArrayList commonAttributeMapping;
0059:
0060: protected String packageName;
0061:
0062: protected static URL documentUrl;
0063:
0064: protected String fileExtension;
0065: protected String componentsNodeName;// = "PhysioEvent".toLowerCase();
0066:
0067: protected LayoutManager layoutManager;
0068:
0069: protected XPanel decorationPanel, formPanel;
0070:
0071: protected ArrayList escapes;
0072:
0073: /**
0074: * Create a new builder. An attempt is made to configure the builder with a
0075: * configuration file when the builder is constructed if the configuration file is
0076: * specified as part of the startup properties via the
0077: * <code>XGenericBuilderConfig</code> setting.
0078: * @param project the current xui project
0079: * @param factory the component factory
0080: */
0081: public XGenericBuilder(XProject project) {
0082: super (project, XPage.XUI_SWING_PACKAGE);
0083: evaluator = new XGenericBuilderAttributeEvaluator(project);
0084: }
0085:
0086: /**
0087: * Initialize the builder
0088: */
0089: protected void init(String packageName) {
0090: adapter = WidgetAdapter.getInstance();
0091:
0092: fileExtension = ".zml";
0093: escapes = new ArrayList();
0094: // styles = new Hashtable<String,String>();
0095: typeMap = new Hashtable();
0096: processors = new Hashtable();
0097: commonAttributeMapping = new ArrayList();
0098:
0099: String builderConfigFilename = currentProject
0100: .getStartupParam("XGenericBuilderConfig");
0101: if (builderConfigFilename != null) {
0102: String urlStr = currentProject.findResource(
0103: builderConfigFilename).toExternalForm();
0104: setupFileTypeHandler(urlStr);
0105: }
0106: }
0107:
0108: /**
0109: * Setup the file type handling, mapping a file extension to a set of
0110: * configuration files for a particular file format. An attempt is made to
0111: * load this file when the builder is constructed if the configuration file is
0112: * specified as part of the startup properties via the
0113: * <code>XGenericBuilderConfig</code> setting.
0114: * @param urlStr a filename/url for the main configuration file
0115: */
0116: public void setupFileTypeHandler(String urlStr) {
0117: Reader r = null;
0118: try {
0119: InputStream is = getUrlInputStream(urlStr);
0120: if (is != null)
0121: r = new BufferedReader(new InputStreamReader(is));
0122: else {
0123: String fileName = urlStr;
0124: if (urlStr.indexOf(fileExtension) < 0)
0125: fileName += fileExtension;
0126: r = currentProject.getBufferedReader(fileName, null);
0127: documentUrl = new URL(urlStr);
0128: }
0129: } catch (Exception ex) {
0130: if (BuildProperties.DEBUG)
0131: DebugLogger.logError("BUILDER", "File NOT found: "
0132: + urlStr);
0133: }
0134:
0135: XmlElement model = XmlSource.read(r);
0136: Vector configNodes = model.getChildren();
0137: int numChildren = configNodes.size();
0138: for (int i = 0; i < numChildren; i++) {
0139: XmlElement childNode = (XmlElement) configNodes
0140: .elementAt(i);
0141: String typeStr = childNode.getName().toLowerCase();
0142: if (typeStr.equals("components"))
0143: setupComponentMapping(childNode);
0144: else if (typeStr.equals("instructions"))
0145: setupInstructionMapping(childNode);
0146: else if (typeStr.equals("attributes"))
0147: setupCommonAttributeMapping(childNode);
0148: }
0149: }
0150:
0151: /**
0152: * Setup the mapping for component elements
0153: * @param mappingNode the node whose children describe the mapping of the file
0154: * elements to components
0155: */
0156: protected void setupComponentMapping(XmlElement mappingNode) {
0157: componentsNodeName = mappingNode.getAttribute("id")
0158: .toLowerCase();
0159:
0160: Vector configNodes = mappingNode.getChildren();
0161: int numChildren = configNodes.size();
0162: for (int i = 0; i < numChildren; i++) {
0163: XmlElement childNode = (XmlElement) configNodes
0164: .elementAt(i);
0165: String type = childNode.getAttribute("id");
0166: XComponentMapping cm = new XComponentMapping(type);
0167:
0168: addComponentMapping(cm, childNode);
0169: ArrayList types = (ArrayList) typeMap.get(type);
0170: if (types == null) {
0171: types = new ArrayList();
0172: typeMap.put(type, types);
0173: }
0174: types.add(cm);
0175: }
0176: }
0177:
0178: private Object getTypeMap(String childName, XmlElement childNode) {
0179: Object obj = typeMap.get(childName);
0180: if (obj instanceof ArrayList) {
0181: // Iterate the available mappings to find the best match
0182: ArrayList mappings = (ArrayList) obj;
0183: for (Object o : mappings) {
0184: XComponentMapping cm = (XComponentMapping) o;
0185: Object[] matchParams = cm.getMatchParams();
0186: if (matchParams == null)
0187: return cm;
0188: else {
0189: String[] matchAttribs = (String[]) matchParams[0];
0190: String[] matchValues = (String[]) matchParams[1];
0191: boolean match = true;
0192: for (int j = 0; j < matchAttribs.length; j++) {
0193: String srcValue = childNode
0194: .getAttribute(matchAttribs[j]);
0195: if (!matchValues[j].equals(srcValue)
0196: && !matchValues[j].equals("*")) {
0197: match = false;
0198: break;
0199: }
0200: }
0201:
0202: if (match)
0203: return cm;
0204: }
0205: }
0206: }
0207:
0208: return obj;
0209: }
0210:
0211: private void addComponentMapping(XComponentMapping cm,
0212: XmlElement childNode) {
0213: String mp = childNode.getAttribute("matchAttrib");
0214: if (mp != null) {
0215: Object[] params = { mp.split(","),
0216: childNode.getAttribute("matchValue").split(",") };
0217: cm.setMatchParams(params);
0218: }
0219:
0220: Vector subElements = childNode.getChildren();
0221: int numSubElements = subElements.size();
0222: for (int j = 0; j < numSubElements; j++) {
0223: XmlElement subElementNode = (XmlElement) subElements
0224: .elementAt(j);
0225: String elementName = subElementNode.getName();
0226: if (elementName.equalsIgnoreCase("Component")) {
0227: int numSubChildren = subElementNode.getChildren()
0228: .size();
0229: cm.addComponentType(
0230: subElementNode.getAttribute("type"),
0231: subElementNode.getAttribute("name"),
0232: numSubChildren, "true".equals(subElementNode
0233: .getAttribute("main")));
0234: if (numSubChildren > 0)
0235: addComponentMapping(cm, subElementNode);
0236: } else if (elementName.equalsIgnoreCase("Attributes")) {
0237: Vector attribElements = subElementNode.getChildren();
0238: int numAttribs = attribElements.size();
0239: for (int k = 0; k < numAttribs; k++) {
0240: XmlElement attribNode = (XmlElement) attribElements
0241: .elementAt(k);
0242: cm.addAttributeMapping(attribNode
0243: .getAttribute("srcAttrib"), attribNode
0244: .getAttribute("destAttrib"), attribNode
0245: .getAttribute("component"), attribNode
0246: .getAttribute("value"));
0247: }
0248: }
0249: }
0250: }
0251:
0252: /**
0253: * Setup the mapping for process/instruction elements
0254: * @param mappingNode the node whose children describe the mapping of the file
0255: * elements to components
0256: */
0257: protected void setupInstructionMapping(XmlElement mappingNode) {
0258: Vector configNodes = mappingNode.getChildren();
0259: int numChildren = configNodes.size();
0260: for (int i = 0; i < numChildren; i++) {
0261: XmlElement childNode = (XmlElement) configNodes
0262: .elementAt(i);
0263: String type = childNode.getName();
0264: String id = childNode.getAttribute("id");
0265: if (type.equals("Instruction")) {
0266: XInstructionMapping im = new XInstructionMapping(id);
0267: String processorName = childNode
0268: .getAttribute("processor");
0269: if ((processorName != null)
0270: && (processorName.trim().length() > 0))
0271: im.setProcessor((XInstructionProcessor) processors
0272: .get(processorName));
0273:
0274: Vector subElements = childNode.getChildren();
0275: int numSubElements = subElements.size();
0276: for (int j = 0; j < numSubElements; j++) {
0277: XmlElement subElementNode = (XmlElement) subElements
0278: .elementAt(j);
0279: String elementName = subElementNode.getName();
0280: if (elementName.equalsIgnoreCase("Action")) {
0281: im.addInstruction(subElementNode
0282: .getAttributes());
0283: }
0284: }
0285:
0286: typeMap.put(id, im);
0287: } else if (type.equals("Processor")) {
0288: try {
0289: Class cls = Class.forName(childNode
0290: .getAttribute("class"));
0291: XInstructionProcessor ip = (XInstructionProcessor) cls
0292: .newInstance();
0293: processors.put(id, ip);
0294: } catch (Exception ex) {
0295: ex.printStackTrace();
0296: }
0297: }
0298: }
0299: }
0300:
0301: /**
0302: * Load the definition of the common attribute mapping
0303: */
0304: public void setupCommonAttributeMapping(XmlElement mappingNode) {
0305: Vector configNodes = mappingNode.getChildren();
0306: int numChildren = configNodes.size();
0307: for (int i = 0; i < numChildren; i++) {
0308: XmlElement attribNode = (XmlElement) configNodes
0309: .elementAt(i);
0310: String elementName = attribNode.getName();
0311: if (elementName.equalsIgnoreCase("Attribute")) {
0312: commonAttributeMapping.add(new XAttributeMapping(
0313: attribNode.getAttribute("srcAttrib"),
0314: attribNode.getAttribute("destAttrib"),
0315: attribNode.getAttribute("component"),
0316: attribNode.getAttribute("value")));
0317: }
0318: }
0319: }
0320:
0321: /**
0322: * Attempt to get an input stream from the specified path
0323: * @param urlStr the url string
0324: * @return the inputstream or null if the strean cannot be opened.
0325: */
0326: protected InputStream getUrlInputStream(String urlStr) {
0327: try {
0328: documentUrl = new URL(urlStr);
0329:
0330: // Open the connection and set the properties so that the input is loaded
0331: // from the server and even if a cached version is available.
0332: URLConnection conn = documentUrl.openConnection();
0333: conn.setDefaultUseCaches(false);
0334: conn.setIfModifiedSince(0);
0335: conn.setDoInput(true);
0336: conn.setDoOutput(false);
0337: conn.setUseCaches(false);
0338: conn.connect();
0339:
0340: InputStream is = conn.getInputStream();
0341: return is;
0342: } catch (MalformedURLException ex) {
0343: } catch (Exception ex) {
0344: if (BuildProperties.DEBUG)
0345: DebugLogger
0346: .logError("Unable to load the page from the server: "
0347: + urlStr);
0348: //ex.printStackTrace();
0349: }
0350:
0351: return null;
0352: }
0353:
0354: /**
0355: * Loads an XPage via a reader obtained from the XProject (searches
0356: * the classpath). The pageName is assumed to be the name of an XML file. For
0357: * example if the pageName is 'welcome' then the 'welcome.xml' file is read as
0358: * a UTF8 encoded XML file (by default).
0359: * @param defPackageName the package or path to the page
0360: * @param pageName the page name or the name of the class implementing the page
0361: * @param include true if the page to be loaded is being included in another
0362: * page in which case any class attribute of the included page is ignored
0363: * @return the page
0364: */
0365: public PageSupport loadPage(String defPackageName, String pageName,
0366: boolean include) {
0367: packageName = currentProject.getStartupParam("StartPackage");
0368:
0369: Reader r = null;
0370: try {
0371: InputStream is = null;
0372: URL url = currentProject.findResource(pageName);
0373: if (url != null) {
0374: String urlStr = url.toExternalForm();
0375: is = getUrlInputStream(urlStr);
0376: }
0377:
0378: if (is != null)
0379: r = new BufferedReader(new InputStreamReader(is));
0380: else {
0381: String fileName = pageName;
0382: if (pageName.indexOf(fileExtension) < 0)
0383: fileName += fileExtension;
0384: r = currentProject.getBufferedReader(fileName, null);
0385: documentUrl = currentProject.findResource(fileName);
0386: }
0387: } catch (Exception ex) {
0388: if (BuildProperties.DEBUG)
0389: DebugLogger.logError("BUILDER", "File NOT found: "
0390: + pageName);
0391: }
0392:
0393: try {
0394: if ((r == null) || !r.ready())
0395: return null;
0396:
0397: PageSupport formPage;
0398: if (!include)
0399: formPage = readPage(r, pageName, fileExtension, include);
0400: else {
0401: if (layoutManager instanceof ColumnLayout)
0402: ((ColumnLayout) layoutManager).setIndentY(30);
0403: formPage = super .readPage(r, pageName, fileExtension,
0404: include);
0405: }
0406:
0407: if (!include) {
0408: String decorationPage;
0409: int pos = pageName.indexOf('.');
0410: if (pos > 0)
0411: decorationPage = pageName.substring(0, pos);
0412: else
0413: decorationPage = pageName;
0414: decorationPage += "Decoration.xml";
0415: componentFactory.setParentComponent(decorationPanel);
0416: loadPage(packageName, decorationPage, true);
0417: }
0418:
0419: return formPage;
0420: } catch (Exception e) {
0421: if (BuildProperties.DEBUG) {
0422: DebugLogger.logError("BUILDER", "File NOT found: "
0423: + pageName);
0424: e.printStackTrace();
0425: }
0426: } finally {
0427: if (!include)
0428: rootPage = null;
0429: }
0430: return null;
0431: }
0432:
0433: /**
0434: * Read an XML description of the page and construct a new XPage. An instance
0435: * of the class specified by the class attribute is constructed or else an
0436: * instance of XPage if no class attribute is specified. The new page is
0437: * populated but is not yet added to its parent.
0438: * <br>
0439: * The startup file parameter 'DefaultClass' is used to obtain a default for
0440: * each page's class if a class parameter is not specified in the page's XML
0441: * <br>
0442: * The startup file parameter 'Validations' is used to obtain a default for
0443: * each page's set of validation rules
0444: *
0445: * @param reader a input stream from which to read the page
0446: * @param pageName the name of the page
0447: * @param ext the file extension
0448: * @param include the page to be loaded is being included in another page
0449: * @return the page
0450: */
0451: public PageSupport readPage(Reader reader, String pageName,
0452: String ext, boolean include) {
0453: XmlElement model = XmlSource.read(reader);
0454: if (BuildProperties.DEBUG && (model == null))
0455: DebugLogger.logError("BUILDER", "The file - " + pageName
0456: + " - could not be parsed!");
0457: setupPage(model, pageName, ext, include);
0458: eventHandler = page.getEventHandler();
0459:
0460: XmlElement componentsNode = null;
0461: XmlElement eventsNode = null;
0462: XmlElement vocabNode = null;
0463: XmlElement validationsNode = null;
0464: XmlElement scriptsNode = null;
0465:
0466: Vector componentNodes = model.getChildren();
0467: int numChildren = componentNodes.size();
0468: for (int i = 0; i < numChildren; i++) {
0469: XmlElement childNode = (XmlElement) componentNodes
0470: .elementAt(i);
0471: String typeStr = childNode.getName().toLowerCase();
0472: if (typeStr.equals(componentsNodeName))
0473: componentsNode = childNode;
0474: else if (typeStr.equals("events"))
0475: eventsNode = childNode;
0476: else if (typeStr.equals("vocabularies"))
0477: vocabNode = childNode;
0478: else if (typeStr.equals("validations"))
0479: validationsNode = childNode;
0480: else if (typeStr.equals("attributes"))
0481: loadAttributeSet(childNode);
0482: else if (typeStr.equals("include")) {
0483: loadPage(packageName, childNode.getAttribute("file"),
0484: true);
0485: componentFactory.setParentComponent(page);
0486: } else if (typeStr.equals("scripts"))
0487: scriptsNode = childNode;
0488: else
0489: loadOtherElement(page, childNode);
0490: }
0491:
0492: if (vocabNode != null)
0493: addVocabulary(page, vocabNode);
0494:
0495: if (componentsNode != null) {
0496: setupPageLayout(componentsNode);
0497: addComponents((include ? componentFactory
0498: .getParentComponent() : formPanel), componentsNode);
0499: }
0500:
0501: if (eventsNode != null)
0502: addEvents(page, eventsNode);
0503:
0504: if (validationsNode != null)
0505: addValidations(page, validationsNode);
0506:
0507: if (scriptsNode != null)
0508: addScripts(page, scriptsNode);
0509:
0510: page.validate();
0511: return page;
0512: }
0513:
0514: /**
0515: * Loads the page based on the contents of the page tag or by using default
0516: * values.
0517: *
0518: * @param pageName the name of the page
0519: * @param ext the file extension
0520: * @param include the page to be loaded is being included in another page
0521: */
0522: protected void setupPage(XmlElement model, String pageName,
0523: String ext, boolean include) {
0524: String className;
0525:
0526: if (!include) {
0527: int pos;
0528: if ((pos = pageName.indexOf(ext)) > 0)
0529: pageName = pageName.substring(0, pos);
0530: if ((pageName.indexOf('.') <= 0)
0531: && (packageName.length() > 1))
0532: className = packageName + "."
0533: + pageName.substring(0, 1).toUpperCase()
0534: + pageName.substring(1);
0535: else
0536: className = pageName;
0537:
0538: try {
0539: page = loadClass(className);
0540: } catch (Exception e) {
0541: if (BuildProperties.DEBUG)
0542: DebugLogger
0543: .trace("Unable to load the named class: "
0544: + className);
0545: } finally {
0546: if (page == null)
0547: page = new XPage();
0548: }
0549: setPageName(pageName);
0550: setPageExtension(ext);
0551: page.setLayout(null);
0552:
0553: componentFactory.setParentComponent((Container) page);
0554: } else
0555: super .setupPage(model, pageName, ext, include);
0556:
0557: evaluator.setPage(page);
0558: rootPage = (XPage) page;
0559: }
0560:
0561: /**
0562: * Setup the layout for this page
0563: */
0564: public void setupPageLayout(XmlElement componentsNode) {
0565: page.setLayout(new LayerLayout());
0566: XPage xp = ((XPage) page);
0567:
0568: String colStr = componentsNode.getAttribute("cols");
0569: String paddingStr = componentsNode.getAttribute("padding");
0570: String maxWidthStr = componentsNode.getAttribute("maxWidth");
0571: ColumnLayout columnLayout = new ColumnLayout(colStr == null ? 1
0572: : Integer.parseInt(colStr), paddingStr == null ? 5
0573: : Integer.parseInt(paddingStr));
0574: columnLayout.setMaxRowWidth(maxWidthStr == null ? 0.75 : Double
0575: .parseDouble(maxWidthStr));
0576: layoutManager = columnLayout;
0577: xp.add(formPanel = new XPanel());
0578: formPanel.setLayout(layoutManager);
0579: formPanel.setOpaque(false);
0580: componentFactory.setParentComponent(formPanel);
0581:
0582: xp.add(decorationPanel = new XPanel());
0583: decorationPanel.setOpaque(false);
0584: decorationPanel.setBackground(xp.getBackground());
0585: }
0586:
0587: /**
0588: * Adds data vocaulary associated with the page.
0589: * @param page the page to which the component/data bindings are added
0590: * @param model the vocabulary model
0591: */
0592: protected void addVocabulary(PageSupport page, XmlElement model) {
0593: if (model == null)
0594: return;
0595:
0596: String fileStr = null;
0597: XModel rootNode = currentProject.getModel();
0598: XOptionalDataSource.setUseValueAsId(true);
0599:
0600: Vector vocabNodes = model.getChildren();
0601: int numChildren = vocabNodes.size();
0602: for (int i = 0; i < numChildren; i++) {
0603: XmlElement childNode = (XmlElement) vocabNodes.elementAt(i);
0604: try {
0605: fileStr = childNode.getAttribute("file");
0606: String urlStr = currentProject.findResource(fileStr)
0607: .toExternalForm();
0608: if (urlStr != null) {
0609: InputStream is = getUrlInputStream(urlStr);
0610: if (is != null) {
0611: BufferedReader r = new BufferedReader(
0612: new InputStreamReader(is));
0613: XOptionalDataSource ds = new XOptionalDataSource(
0614: currentProject);
0615: XmlSource newSrc = new XmlSource();
0616: XmlElement vocabModel = newSrc.read(r);
0617: ds.loadTable(vocabModel, rootNode);
0618: }
0619: }
0620: } catch (Exception e) {
0621: if (BuildProperties.DEBUG) {
0622: DebugLogger
0623: .logError(
0624: "BUILDER",
0625: "Undetermined error while processing the vocabulary element for the target component");
0626: e.printStackTrace();
0627: }
0628: }
0629: }
0630: }
0631:
0632: // Type handling -------------------------------------------------------------
0633: /**
0634: * Adds an individual component element to the page (this method may be called
0635: * recursively for nested elements). Several methods will be attempted until a
0636: * component is successfully created. Firstly the built-in component types are
0637: * checked, then any additional registered component constructors. The types
0638: * can be specified by type ID, type name or class name.
0639: * @param childNode the XML element containing the component specification.
0640: * @return the new component
0641: */
0642: protected Object addComponent(XmlElement childNode) {
0643: String childName = childNode.getName();
0644:
0645: try {
0646: Hashtable components = new Hashtable();
0647: Object mapping = getTypeMap(childName, childNode);
0648: Component mainComponent = null;
0649: if (mapping != null) {
0650: if (mapping instanceof XComponentMapping) {
0651: XComponentMapping componentMapping = (XComponentMapping) mapping;
0652: Stack spans = new Stack();
0653: int currentSpan = 0;
0654: int numTypes = componentMapping.getComponentTypes()
0655: .size();
0656: for (int i = 0; i < numTypes; i++) {
0657: XTypeMapping tm = (XTypeMapping) componentMapping
0658: .getComponentTypes().get(i);
0659: Component c = (Component) componentFactory
0660: .addComponent(tm.typeName, null, null);
0661: if ((mainComponent == null) || tm.isMain)
0662: mainComponent = c;
0663:
0664: currentSpan--;
0665: if ((currentSpan == 0) && (spans.size() > 0)) {
0666: currentSpan = ((Integer) spans.pop())
0667: .intValue();
0668: componentFactory
0669: .setParentComponent(((Component) componentFactory
0670: .getParentComponent())
0671: .getParent());
0672: }
0673:
0674: if (tm.span > 0) {
0675: spans.push(new Integer(currentSpan));
0676: componentFactory.setParentComponent(c);
0677: currentSpan = tm.span;
0678: }
0679:
0680: if (c != null)
0681: components.put(tm.name, c);
0682: else {
0683: if (BuildProperties.DEBUG)
0684: DebugLogger
0685: .logError("Cannot instantiate type: "
0686: + childName);
0687: }
0688: }
0689:
0690: if (componentMapping.getAttributeMapping() != null) {
0691: int numMappings = componentMapping
0692: .getAttributeMapping().size();
0693: for (int ii = 0; ii < numMappings; ii++) {
0694: XAttributeMapping am = (XAttributeMapping) componentMapping
0695: .getAttributeMapping().get(ii);
0696: Component c = (Component) components.get(am
0697: .getComponentRef());
0698: String valueStr;
0699: // If the source attribute is not specified the attribute should be
0700: // applied anyhow - or always, as a default, for example specifying
0701: // that a component is opaque
0702: if (am.getSrcAttrib() != null)
0703: valueStr = getMappedValue(
0704: childNode.getAttribute(am
0705: .getSrcAttrib()), am
0706: .getValue());
0707: else
0708: valueStr = getMappedValue(c, am
0709: .getValue());
0710:
0711: if (valueStr != null) {
0712: // The name has a special role so we must set it before anything else
0713: if (am.getDestAttrib().equals("name"))
0714: c.setName(valueStr);
0715: else
0716: setComponentAttributes(c, am
0717: .getDestAttrib(), valueStr,
0718: childNode);
0719: }
0720: }
0721:
0722: // Now try the common attibutes, for example a style attribute that could
0723: // be specified for any component
0724: int amis = commonAttributeMapping.size();
0725: for (int ami = 0; ami < amis; ami++) {
0726: XAttributeMapping am = (XAttributeMapping) commonAttributeMapping
0727: .get(ami);
0728: Component c = (Component) components.get(am
0729: .getComponentRef());
0730: if (c != null) {
0731: String valueStr;
0732: // If the source attribute is not specified the attribute should be
0733: // applied anyhow - or always, as a default, for example specifying
0734: // that a component is opaque
0735: if (am.getSrcAttrib() != null)
0736: valueStr = getMappedValue(childNode
0737: .getAttribute(am
0738: .getSrcAttrib()),
0739: am.getValue());
0740: else
0741: valueStr = am.getValue();
0742:
0743: if (valueStr != null)
0744: setComponentAttributes(c, am
0745: .getDestAttrib(), valueStr,
0746: childNode);
0747: }
0748: }
0749: }
0750: } else if (mapping instanceof XInstructionMapping) {
0751: XInstructionMapping instructionMapping = (XInstructionMapping) mapping;
0752: XInstructionProcessor processor = instructionMapping
0753: .getProcessor();
0754: for (Object in : instructionMapping.instructions) {
0755: Properties instruction = (Properties) in;
0756: if (processor != null)
0757: processor.process(this , childNode);
0758: else {
0759: String method = (String) instruction
0760: .get("method");
0761: if (method.contains("addSpacer")
0762: && (layoutManager instanceof ColumnLayout))
0763: ((ColumnLayout) layoutManager)
0764: .addRowSpacer();
0765: }
0766: }
0767: }
0768:
0769: // If a panel is found set it to the parent and recursively add its children
0770: if (adapter.isContainer(mainComponent)) {
0771: Object parentObject = componentFactory
0772: .getParentComponent();
0773: componentFactory.setParentComponent(mainComponent);
0774: addComponents(mainComponent, childNode);
0775: componentFactory.setParentComponent(parentObject);
0776: adapter.doLayout(mainComponent);
0777: }
0778:
0779: // Special handling for radio buttons
0780: // For the first rb create a group
0781: // For subsequent rbs add them to the group
0782: // If another type of component is found reset the groups
0783: if (mainComponent instanceof XRadioButtonGroup) {
0784: XRadioButtonGroup rb = ((XRadioButtonGroup) mainComponent);
0785: if (checkBoxGroup == null) {
0786: // The radio button may already have a group
0787: if (rb.getRadioButtonGroup() == null)
0788: checkBoxGroup = rb.createGroup();
0789: else
0790: checkBoxGroup = rb.getRadioButtonGroup();
0791: } else
0792: rb.setRadioButtonGroup(checkBoxGroup);
0793: } else
0794: checkBoxGroup = null;
0795:
0796: // Now customize the component
0797: String customization = (String) childNode
0798: .getAttribute("customizer");
0799: if ((customization != null)
0800: && (customization.length() > 0))
0801: customizer.customize(mainComponent, customization,
0802: ComponentCustomizer.POST_CREATE_TIME);
0803: } else
0804: return super .addComponent(childNode);
0805: } catch (Exception e) {
0806: if (BuildProperties.DEBUG)
0807: DebugLogger.logError("BUILDER",
0808: "While adding the component element: "
0809: + childName);
0810: e.printStackTrace();
0811: }
0812: if (layoutManager instanceof ColumnLayout)
0813: ((ColumnLayout) layoutManager).endRow();
0814:
0815: return null;
0816: }
0817:
0818: /**
0819: * Iterate through the attributes and set the attributes for a component
0820: * @param comp the component
0821: * @param attribName the name of the attribute.
0822: * @param attribValue the value of the attribute.
0823: * @param childNode the source xml node
0824: */
0825: protected void setComponentAttributes(Component comp,
0826: String attribName, String attribValue, XmlElement childNode) {
0827: try {
0828: Object evaluatedAttribute = evaluateAttribute(page,
0829: attribValue);
0830: page.setAttribute(attribName, comp.getName(),
0831: evaluatedAttribute);
0832:
0833: if (attribName.startsWith("${")) {
0834: evaluator.evaluateAttribute(comp, attribName,
0835: attribValue, childNode);
0836: return;
0837: }
0838: // Set a couple of key attributes
0839: int rc = -1;
0840: ComponentAdapter ca = componentFactory
0841: .getComponentAdapter(comp.getClass().getName());
0842: if (ca != null)
0843: rc = ca
0844: .setProperty(comp, attribName, attribValue,
0845: null);
0846: if (rc < 0) {
0847: // The component hasn't gone through the normal component factory with
0848: // all the attributes and so the content attribute hasn't been passed
0849: // to the component factory - now we need to set it.
0850: if (attribName.equals("content")) {
0851: if (comp instanceof XTextHolder) {
0852: ((XTextHolder) comp).setText(componentFactory
0853: .translate(evaluatedAttribute
0854: .toString()));
0855: return;
0856: } else if (comp instanceof XImageHolder) {
0857: ((XImageHolder) comp)
0858: .setImage(currentProject
0859: .getImage(evaluatedAttribute
0860: .toString()));
0861: return;
0862: }
0863: }
0864:
0865: if (comp instanceof XAttributedComponent)
0866: ((XAttributedComponent) comp).setAttribute(
0867: attribName, evaluatedAttribute);
0868: }
0869: /** @todo add a return type to the XAttributedComponent interface and check it
0870: * so that if the attribute is not set try to use the bean api/reflection to
0871: * set the attribute based on the attribute name */
0872: } catch (Exception ex) {
0873: ex.printStackTrace();
0874: }
0875: }
0876:
0877: // End type handling ---------------------------------------------------------
0878:
0879: /**
0880: * Get the argument value in the specified format, substituting the value
0881: * into the mapping string as appropriate. The mapping allows a value to
0882: * be included from a model or a path within the model
0883: * @param mapping e.g. /vocab/lists/${value}
0884: * @param srcValue the value that is used to replace $value, or the return
0885: * value if no substitution is specified in the mapping
0886: */
0887: public String getMappedValue(String srcValue, String mapping) {
0888: if (srcValue == null)
0889: return null;
0890:
0891: if ((mapping != null) && mapping.indexOf("${value}") > 0)
0892: return XuiUtilities.replace(mapping, "${value}", srcValue);
0893:
0894: return srcValue;
0895: }
0896:
0897: /**
0898: * Get the argument value in the specified format, substituting the value
0899: * into the mapping string as appropriate. The mapping allows a value to
0900: * be included from a model or a path within the model
0901: * @param comp the affected component
0902: * @param mapping e.g. /vocab/lists/${value}
0903: * @param srcValue the value that is ${compName}
0904: * value if no substitution is specified in the mapping
0905: */
0906: public String getMappedValue(Component comp, String srcValue) {
0907: if (srcValue == null)
0908: return null;
0909:
0910: while (srcValue.indexOf("${compName}") > 0)
0911: srcValue = XuiUtilities.replace(srcValue, "${compName}",
0912: comp.getName());
0913:
0914: srcValue = escape(srcValue);
0915: srcValue = (String) evaluateAttribute(page, srcValue);
0916: srcValue = unescape(srcValue);
0917:
0918: return srcValue;
0919: }
0920:
0921: /**
0922: * Replace the [...] escaped blocks with [idx] where idx is the index of the
0923: * store for the escaped value
0924: * @param srcValue the source/unescaped value
0925: * @return the escaped value
0926: */
0927: protected String escape(String srcValue) {
0928: int startPos = 0;
0929: escapes.clear();
0930: int escapeIdx = 0;
0931: while ((startPos = srcValue.indexOf('<', startPos)) >= 0) {
0932: int endPos = XuiUtilities.indexOfMatchingEnding(srcValue,
0933: '<', '>', ++startPos);
0934: if (endPos > 0) {
0935: String escape = srcValue.substring(startPos, endPos);
0936: escapes.add(escape);
0937: srcValue = srcValue.substring(0, startPos)
0938: + Integer.toString(escapeIdx++)
0939: + srcValue.substring(endPos);
0940: }
0941: }
0942: return srcValue;
0943: }
0944:
0945: /**
0946: * Replace the [...] escaped blocks with [idx] where idx is the index of the
0947: * store for the escaped value
0948: * @param srcValue the source/unescaped value
0949: * @return the escaped value
0950: */
0951: protected String unescape(String srcValue) {
0952: int startPos = 0;
0953: while ((startPos = srcValue.indexOf('<', startPos)) >= 0) {
0954: int endPos = XuiUtilities.indexOfMatchingEnding(srcValue,
0955: '<', '>', ++startPos);
0956: if (endPos > 0) {
0957: int escapeIdx = Integer.parseInt(srcValue.substring(
0958: startPos, endPos));
0959: srcValue = srcValue.substring(0, startPos - 1)
0960: + escapes.get(escapeIdx)
0961: + srcValue.substring(endPos + 1);
0962: }
0963: }
0964: return srcValue;
0965: }
0966:
0967: /**
0968: * Add a data binding for the component
0969: * @param targetComp the affected component
0970: * @param srcStr the data source path
0971: */
0972: public void addBinding(Component targetComp, String srcStr) {
0973: Hashtable instanceConfig = new Hashtable();
0974: instanceConfig.put("source", page.evaluatePath(srcStr));
0975: instanceConfig.put("sourcePath", srcStr);
0976:
0977: String componentName = targetComp.getName();
0978: String[] attribs = { "target", "type", "attrib", "class" };
0979: for (int i = 0; i < attribs.length; i++) {
0980: String attrib = attribs[i];
0981: String pa = (String) page.getAttribute(attrib,
0982: componentName);
0983: if (pa != null)
0984: instanceConfig.put(attrib, pa);
0985: }
0986:
0987: XDataBinding binding = null;
0988: String typeStr = (String) instanceConfig.get("type");
0989: if ((typeStr != null) && (typeStr.compareTo("custom") == 0)) {
0990: String customClass = (String) instanceConfig.get("class");
0991: try {
0992: binding = (XDataBinding) Class.forName(
0993: customClass.trim()).newInstance();
0994: binding.setup(currentProject, targetComp, null,
0995: instanceConfig);
0996: page.addBinding(binding);
0997: return;
0998: } catch (Exception ex) {
0999: System.out.println("could not load binding class - "
1000: + customClass);
1001: //if ( BuildProperties.DEBUG )
1002: ex.printStackTrace();
1003: }
1004: }
1005:
1006: binding = getFactoryBinding(page, targetComp, instanceConfig);
1007: if (binding != null)
1008: page.addBinding(binding);
1009: else if (BuildProperties.DEBUG) {
1010: DebugLogger.logError("BUILDER",
1011: "Failed to create the binding for the target: "
1012: + componentName + " and the data source:"
1013: + srcStr);
1014: }
1015: }
1016:
1017: /**
1018: * Apply a style to a component
1019: * @param comp the affected component
1020: * @param value the style name/value
1021: */
1022: public void setStyle(Component comp, String value) {
1023: componentFactory.applyStyle(comp, value);
1024: }
1025:
1026: /**
1027: * Get the page loader type - a unique name identifying the loader
1028: * @return "generic"
1029: */
1030: public String getType() {
1031: return "generic";
1032: }
1033: }
|