001: package com.xoetrope.builder.ms.infopath;
002:
003: import java.io.Reader;
004: import java.util.Enumeration;
005: import java.util.Hashtable;
006: import java.util.Vector;
007:
008: import java.awt.Component;
009: import java.awt.Container;
010:
011: import net.xoetrope.builder.XuiBuilder;
012: import net.xoetrope.debug.DebugLogger;
013: import net.xoetrope.xml.XmlElement;
014: import net.xoetrope.xml.XmlSource;
015: import net.xoetrope.xui.XPage;
016: import net.xoetrope.xui.PageSupport;
017: import net.xoetrope.xui.XProject;
018: import net.xoetrope.xui.XRadioButtonGroup;
019: import net.xoetrope.xui.build.BuildProperties;
020: import net.xoetrope.xui.data.XModel;
021: import net.xoetrope.swing.XPanel;
022: import net.xoetrope.swing.XEdit;
023: import java.awt.GridLayout;
024: import com.xoetrope.swing.XSlider;
025: import net.xoetrope.swing.XButton;
026: import net.xoetrope.swing.XLabel;
027: import javax.swing.JComponent;
028: import java.awt.BorderLayout;
029: import java.awt.FlowLayout;
030: import java.awt.Color;
031: import javax.swing.BorderFactory;
032: import net.xoetrope.swing.XScrollPane;
033: import net.xoetrope.swing.XList;
034:
035: /**
036: * An experimental build for another XAML format with some bovine connections
037: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
038: * the GNU Public License (GPL), please see license.txt for more details. If
039: * you make commercial use of this software you must purchase a commercial
040: * license from Xoetrope.</p>
041: * <p> $Revision: 1.7 $</p>
042: */
043: public class InfopathBuilder extends XuiBuilder {
044: public static final String XAML_XMLNS = "http://schemas.microsoft.com/2003/xaml";
045: public static final String XAML_DEF_XMLNS = "Definition";
046:
047: public static final int CANVAS = 0;
048: public static final int BUTTON = 1;
049: public static final int TEXT = 2;
050: public static final int TEXTBOX = 3;
051: public static final int CHECKBOX = 4;
052: public static final int BORDER = 5;
053: public static final int DOCKPANEL = 6;
054: public static final int TABLE = 7;
055: public static final int RADIOBUTTON = 8;
056: public static final int LISTBOX = 9;
057: public static final int VERTICALSLIDER = 10;
058: public static final int HORIZONTALSLIDER = 11;
059: public static final int VERTICALSCROLLBAR = 12;
060: public static final int HORIZONTALSCROLLBAR = 14;
061: public static final int FLOWPANEL = 15;
062:
063: public static final int CODE = 200;
064:
065: public static final int DOCK_PANEL_AREA_FILL = 0;
066: public static final int DOCK_PANEL_AREA_TOP = 1;
067: public static final int DOCK_PANEL_AREA_LEFT = 2;
068: public static final int DOCK_PANEL_AREA_RIGHT = 3;
069: public static final int DOCK_PANEL_AREA_BOTTOM = 4;
070:
071: protected static final String dockPanelPositions[] = {
072: BorderLayout.CENTER, BorderLayout.NORTH, BorderLayout.WEST,
073: BorderLayout.EAST, BorderLayout.SOUTH };
074:
075: private static Hashtable formTags = null;
076:
077: protected String packageName;
078:
079: protected String selectStyle = XPage.RADIO;
080:
081: public InfopathBuilder(XProject project) {
082: super (project);
083: setupFormTags();
084: }
085:
086: /**
087: * Read an XML description of the page and construct a new XPage. An instance
088: * of the class specified by the class attribute is constructed or else an
089: * instance of XPage if no class attribute is specified. The new page is
090: * populated but is not yet added to its parent.
091: * <br>
092: * The startup file parameter 'DefaultClass' is used to obtain a default for
093: * each page's class if a class parameter is not specified in the page's XML
094: * <br>
095: * The startup file parameter 'Validations' is used to obtain a default for
096: * each page's set of validation rules
097: *
098: * @param reader a input stream from which to read the page
099: * @param pageName the name of the page
100: * @param include the page to be loaded is being included in another page
101: * @return the page
102: */
103: public PageSupport readPage(Reader reader, String pageName,
104: boolean include) {
105: XmlElement model = XmlSource.read(reader);
106: setupPage(model, pageName, include);
107:
108: String namespace = model.getNamespace();
109: if (namespace.toLowerCase().equals(XAML_XMLNS))
110: readForm(model);
111:
112: page.doLayout();
113: return page;
114: }
115:
116: /**
117: * Read a form element
118: * @param model the XML
119: * @return true if the children require further processing
120: */
121: protected void readForm(XmlElement model) {
122: String typeName = model.getName();
123:
124: Object obj = formTags.get(typeName.toLowerCase());
125: if (obj != null) {
126: int tagId = ((Integer) obj).intValue();
127:
128: Component parent = (Component) componentFactory
129: .getParentComponent();
130: try {
131: switch (tagId) {
132: case BORDER:
133: case CANVAS:
134: readCanvas(model, tagId);
135: break;
136: case DOCKPANEL:
137: readDockPanel(model, tagId);
138: break;
139: case FLOWPANEL:
140: readFlowPanel(model, tagId);
141: break;
142: case TABLE:
143: readTable(model);
144: break;
145: case BUTTON:
146: readButton(model);
147: break;
148: case TEXT:
149: readText(model);
150: break;
151: case TEXTBOX:
152: readTextBox(model);
153: break;
154: case CHECKBOX:
155: readCheckBox(model);
156: break;
157: case CODE:
158: readCode(model);
159: break;
160: case RADIOBUTTON:
161: readCode(model);
162: break;
163: case LISTBOX:
164: readList(model);
165: break;
166: case VERTICALSLIDER:
167: readSlider(model, false);
168: break;
169: case HORIZONTALSLIDER:
170: readSlider(model, true);
171: break;
172: case VERTICALSCROLLBAR:
173: readScrollbar(model, false);
174: break;
175: case HORIZONTALSCROLLBAR:
176: readScrollbar(model, true);
177: break;
178: }
179: } catch (Exception ex) {
180: ex.printStackTrace();
181: }
182:
183: // Reset the parent component to its state prior to this call
184: componentFactory.setParentComponent(parent);
185: } else
186: addComponent(typeName, model);
187: }
188:
189: /**
190: * Read a select node
191: * @param selectNode the xml representing the select item
192: * @param tagId the enumerated tag type for this node
193: */
194: protected void readCanvas(XmlElement selectNode, int tagId) {
195: XPanel panel = (XPanel) addComponent("panel", selectNode);
196: componentFactory.setParentComponent(panel);
197: panel.setLayout(new GridLayout(0, 1));
198:
199: if (tagId == BORDER) {
200: String borderThickness = selectNode
201: .getAttribute("BorderThickness");
202: String borderBrush = selectNode.getAttribute("BorderBrush");
203: Color borderColor = parseColorAttribute(borderBrush);
204: int thickness = 1;
205: if (borderThickness != null) {
206: int pos = borderThickness.indexOf(',');
207: thickness = Integer.parseInt(pos > 0 ? borderThickness
208: .substring(0, pos) : borderThickness);
209: }
210: panel.setBorder(BorderFactory.createLineBorder(
211: borderColor != null ? borderColor : Color.black,
212: thickness));
213: }
214:
215: Vector componentNodes = selectNode.getChildren();
216: int numNodes = componentNodes.size();
217: for (int i = 0; i < numNodes; i++) {
218: XmlElement childNode = (XmlElement) componentNodes
219: .elementAt(i);
220: if (childNode != null)
221: readForm(childNode);
222: }
223: }
224:
225: /**
226: * Read a select node
227: * @param selectNode the xml representing the select item
228: * @param tagId the enumerated tag type for this node
229: */
230: protected void readDockPanel(XmlElement selectNode, int tagId) {
231: XPanel panel = (XPanel) addComponent("panel", selectNode);
232: panel.setLayout(new BorderLayout());
233: componentFactory.setParentComponent(panel);
234:
235: XPanel panels[] = new XPanel[5];
236:
237: Vector componentNodes = selectNode.getChildren();
238: int numNodes = componentNodes.size();
239: for (int i = 0; i < numNodes; i++) {
240: XmlElement childNode = (XmlElement) componentNodes
241: .elementAt(i);
242: String dockPanelArea = (tagId == DOCKPANEL ? childNode
243: .getAttribute("DockPanel.Dock") : "fill");
244: int panelIdx = DOCK_PANEL_AREA_FILL;
245: if (dockPanelArea != null) {
246: dockPanelArea = dockPanelArea.toLowerCase();
247: if (dockPanelArea.equals("fill"))
248: panelIdx = DOCK_PANEL_AREA_FILL;
249: else if (dockPanelArea.equals("left"))
250: panelIdx = DOCK_PANEL_AREA_LEFT;
251: else if (dockPanelArea.equals("right"))
252: panelIdx = DOCK_PANEL_AREA_RIGHT;
253: else if (dockPanelArea.equals("top"))
254: panelIdx = DOCK_PANEL_AREA_TOP;
255: else if (dockPanelArea.equals("bottom"))
256: panelIdx = DOCK_PANEL_AREA_BOTTOM;
257: }
258: XPanel currentPanel = null;
259: if (panels[panelIdx] == null) {
260: currentPanel = panels[panelIdx] = new XPanel();
261: currentPanel.setLayout(new GridLayout(0, 1));
262: panel.add(currentPanel, dockPanelPositions[panelIdx]);
263: } else
264: currentPanel = panels[panelIdx];
265: componentFactory.setParentComponent(currentPanel);
266:
267: if (childNode != null)
268: readForm(childNode);
269: }
270: }
271:
272: /**
273: * Read a flowpanel node
274: * @param selectNode the xml representing the select item
275: * @param tagId the enumerated tag type for this node
276: */
277: protected void readFlowPanel(XmlElement selectNode, int tagId) {
278: XPanel panel = (XPanel) addComponent("panel", selectNode);
279: panel.setLayout(new FlowLayout());
280: componentFactory.setParentComponent(panel);
281:
282: Vector componentNodes = selectNode.getChildren();
283: int numNodes = componentNodes.size();
284: for (int i = 0; i < numNodes; i++)
285: readForm(selectNode);
286: }
287:
288: protected XModel readItemset(XmlElement itemsetNode) {
289: String model = "XForms/" + itemsetNode.getAttribute("model");
290: String nodeSet = itemsetNode.getAttribute("nodeset");
291: if (nodeSet.charAt(0) == '/')
292: nodeSet = nodeSet.substring(1);
293:
294: XModel modelNode = (XModel) currentProject.getModel()
295: .get(model);
296: return (XModel) modelNode.get(nodeSet);
297: }
298:
299: protected XModel readItem(XmlElement itemsetNode) {
300: String model = "XForms/" + itemsetNode.getAttribute("model");
301: String nodeSet = itemsetNode.getAttribute("ref");
302: if (nodeSet.charAt(0) == '/')
303: nodeSet = nodeSet.substring(1);
304:
305: XModel modelNode = (XModel) currentProject.getModel()
306: .get(model);
307: return (XModel) modelNode.get(nodeSet);
308: }
309:
310: protected void readCode(XmlElement codeNode) {
311: String modelId = codeNode.getContent();
312: }
313:
314: protected void readTable(XmlElement tableNode) {
315: XPanel panel = (XPanel) addComponent("panel", tableNode);
316: componentFactory.setParentComponent(panel);
317:
318: XmlElement bodyElement = tableNode.getFirstChildNamed("Body");
319: XmlElement firstRow = bodyElement.getFirstChildNamed("Row");
320: Vector rows = bodyElement.getChildren();
321: Vector cols = firstRow.getChildren();
322: int numRows = rows.size();
323: int numCols = cols.size();
324:
325: panel.setLayout(new GridLayout(0, numCols));
326: for (int i = 0; i < numRows; i++) {
327: cols = ((XmlElement) (rows.elementAt(i))).getChildren();
328: for (int j = 0; j < numCols; j++) {
329: XmlElement column = (XmlElement) cols.elementAt(j);
330: addCell(column);
331: }
332: }
333: }
334:
335: protected void addCell(XmlElement cellNode) {
336: XmlElement cellContents = cellNode.elementAt(0);
337: addComponent(cellContents.getName(), cellContents);
338: }
339:
340: protected void readButton(XmlElement btnNode) {
341: XButton button = (XButton) addComponent("button", btnNode);
342:
343: String clickEvent = btnNode.getAttribute("Click");
344: }
345:
346: protected void readText(XmlElement btnNode) {
347: XLabel text = (XLabel) addComponent("text", btnNode);
348: }
349:
350: protected void readTextBox(XmlElement btnNode) {
351: XEdit textBox = (XEdit) addComponent("textarea", btnNode);
352: }
353:
354: protected void readCheckBox(XmlElement lblNode) {
355: XLabel checkBox = (XLabel) addComponent("label", lblNode);
356: }
357:
358: protected void readList(XmlElement model) {
359: XList checkBox = (XList) addComponent("list", model);
360: }
361:
362: protected void readSlider(XmlElement model, boolean isHorizontal) {
363: XSlider checkBox = (XSlider) addComponent("slider", model);
364: }
365:
366: protected void readScrollbar(XmlElement model, boolean isHorizontal) {
367: XScrollPane checkBox = (XScrollPane) addComponent("scrollbar",
368: model);
369: }
370:
371: /**
372: * Adds an individual component element to the page (this method may be called
373: * recursively for nested elements). Several methods will be attempted until a
374: * component is successfully created. Firstly the built-in component types are
375: * checked, then any additional registered component constructors. The types
376: * can be specified by type ID, type name or class name.
377: * @param childName the name of the child element
378: * @param childNode the XML element containing the component specification.
379: * @return the new component
380: */
381: protected Component addComponent(String childName,
382: XmlElement childNode) {
383: String nameStr = null, contentStr, styleStr;
384:
385: Component comp = null;
386: try {
387: nameStr = contentStr = styleStr = null;
388: Hashtable componentAttributes = new Hashtable(); // Local copy of the component attributes
389: Enumeration attribNamesEnum = childNode
390: .enumerateAttributeNames();
391: while (attribNamesEnum.hasMoreElements()) {
392: String attribName = (String) attribNamesEnum
393: .nextElement();
394: String attribValue = (String) childNode
395: .getAttribute(attribName);
396: if (attribName.compareTo("ref") == 0)
397: nameStr = attribValue;
398: else if (attribName.compareTo("content") == 0) {
399: contentStr = (String) rootPage
400: .evaluateAttribute(attribValue);
401: componentAttributes.put(attribName, contentStr);
402: } else if (attribName.compareTo("class") == 0)
403: styleStr = childNode.getName() + "/" + attribValue;
404: else {
405: // Save a copy of the attributes in the page for post creation usage
406: ((XPage) rootPage).setAttribute(attribName,
407: nameStr, attribValue);
408:
409: // Save a local copy for use during the construction process
410: componentAttributes.put(attribName, attribValue);
411: }
412: }
413:
414: contentStr = childNode.getContent();
415:
416: // Load with a name like 'Button'
417: try {
418: comp = (Component) componentFactory.addComponent(
419: getComponentRenderType(childName), 0, 0, -1,
420: -1, contentStr, styleStr);
421: } catch (Exception e) {
422: comp = null;
423: }
424:
425: if (comp == null)
426: return null;
427:
428: applyAttributes((JComponent) comp, componentAttributes);
429:
430: // Set the component name
431: comp.setName(nameStr);
432:
433: // Special handling for radio buttons
434: // For the first rb create a group
435: // For subsequent rbs add them to the group
436: // If another type of component is found reset the groups
437: if (comp instanceof XRadioButtonGroup) {
438: XRadioButtonGroup rb = ((XRadioButtonGroup) comp);
439: if (checkBoxGroup == null) {
440: // The radio button may already have a group
441: if (rb.getRadioButtonGroup() == null)
442: checkBoxGroup = rb.createGroup();
443: else
444: checkBoxGroup = rb.getRadioButtonGroup();
445: } else
446: rb.setRadioButtonGroup(checkBoxGroup);
447: } else
448: checkBoxGroup = null;
449:
450: // Setup any extra attributes specified in the XML
451: setComponentAttributes(childName, comp, componentAttributes);
452: } catch (Exception e) {
453: if (BuildProperties.DEBUG)
454: DebugLogger
455: .logError("While adding the component element: "
456: + nameStr);
457: e.printStackTrace();
458: }
459:
460: return comp;
461: }
462:
463: protected void applyAttributes(JComponent comp, Hashtable attribs) {
464: Enumeration enumeration = attribs.keys();
465: while (enumeration.hasMoreElements()) {
466: String attribName = (String) enumeration.nextElement();
467: String attribValue = (String) attribs.get(attribName);
468: attribName = attribName.toLowerCase();
469:
470: try {
471: if (attribName.equals("height")) {
472: comp.setSize(comp.getSize().width, Integer
473: .parseInt(attribValue));
474: // comp.setPreferredSize( new Dimension( /*comp.getSize().width*/20, Integer.parseInt( attribValue ) ));
475: } else if (attribName.equals("width")) {
476: comp.setSize(Integer.parseInt(attribValue), comp
477: .getSize().height);
478: // comp.setPreferredSize( new Dimension( Integer.parseInt( attribValue ), 20 ));//comp.getSize().height ));
479: } else if (attribName.equals("foreground"))
480: comp
481: .setForeground(parseColorAttribute(attribValue));
482: else if (attribName.equals("background"))
483: comp
484: .setBackground(parseColorAttribute(attribValue));
485: } catch (Exception ex) {
486: ex.printStackTrace();
487: }
488: }
489: }
490:
491: protected void setupPage(XmlElement model, String pageName,
492: boolean include) {
493: String className = model.getAttribute("class");
494: if (className == null) {
495: // Try to get a default startup class name if none has been specified in the XML
496: try {
497: className = currentProject
498: .getStartupParam("DefaultClass");
499: } catch (Exception ex) {
500: }
501: if (className == null)
502: className = "net.xoetrope.xui.XPage";
503: }
504:
505: if (!include) {
506: if ((className.indexOf('.') <= 0)
507: && (packageName.length() > 1))
508: className = packageName + className;
509: try {
510: page = loadClass(className);
511: } catch (Exception e) {
512: if (BuildProperties.DEBUG)
513: DebugLogger
514: .trace("Unable to load the named class: "
515: + className);
516: page = new XPage();
517: }
518: setPageName(pageName);
519:
520: componentFactory.setParentComponent((Container) page);
521: String styleName = model.getAttribute("style");
522: if (styleName != null)
523: componentFactory.applyStyle(page, styleName);
524: }
525: String layoutStyle = model.getAttribute("layoutStyle");
526: if (layoutStyle == null)
527: layoutStyle = "BoxLayout";
528: Hashtable ht = new Hashtable();
529: ht.put("isHorz", "1");
530: componentFactory.addLayout(null, layoutHelper
531: .getLayoutType("box"), ht);
532:
533: int width = 0, height = 0;
534: Enumeration attribNamesEnum = model.enumerateAttributeNames();
535: while (attribNamesEnum.hasMoreElements()) {
536: String attribName = (String) attribNamesEnum.nextElement();
537: String attribValue = (String) model
538: .getAttribute(attribName);
539: page.setAttribute(attribName, null, page
540: .evaluateAttribute(attribValue));
541: if (attribName.compareTo("resource") == 0) {
542: String resName;
543: if ((attribValue != null) && (attribValue.length() > 0))
544: resName = page.evaluatePath(attribValue);
545: else
546: resName = currentProject
547: .getStartupParam("Language");
548: componentFactory.setResourceBundle(resName);
549: page.getComponentFactory().setResourceBundle(resName);
550: }
551: }
552:
553: rootPage = (XPage) page;
554: }
555:
556: protected String getComponentRenderType(String childType) {
557: if (childType.equals("choices")) {
558: if (selectStyle.equals(XPage.COMBO))
559: return "combo";
560: }
561: return childType;
562: }
563:
564: protected String getEventType(String event) {
565: if (event == null)
566: return null;
567:
568: if (event.equals("xforms-focus"))
569: return "FocusHandler";
570: else if (event.equals("submit"))
571: return "ActionHandler";
572:
573: return null;
574: }
575:
576: protected void setupFormTags() {
577: if (formTags == null) {
578: formTags = new Hashtable();
579: formTags.put("canvas", new Integer(CANVAS));
580: formTags.put("button", new Integer(BUTTON));
581: formTags.put("text", new Integer(TEXT));
582: formTags.put("textbox", new Integer(TEXTBOX));
583: formTags.put("checkbox", new Integer(CHECKBOX));
584: formTags.put("border", new Integer(BORDER));
585: formTags.put("dockpanel", new Integer(DOCKPANEL));
586: formTags.put("table", new Integer(TABLE));
587: formTags.put("code", new Integer(CODE));
588: }
589: }
590:
591: /**
592: * Gets a color value from a name or an RGB value
593: * @param colorName
594: * @return the color or null if no color can be matched
595: */
596: protected Color parseColorAttribute(String colorName) {
597: if (colorName == null)
598: return null;
599: String name = colorName.toLowerCase();
600: if (name.equals("red"))
601: return Color.red;
602: else if (name.equals("green"))
603: return Color.green;
604: else if (name.equals("blue"))
605: return Color.blue;
606: else if (name.equals("black"))
607: return Color.black;
608: else if (name.equals("cyan"))
609: return Color.cyan;
610: else if (name.equals("darkGray"))
611: return Color.darkGray;
612: else if (name.equals("gray"))
613: return Color.gray;
614: else if (name.equals("lightGray"))
615: return Color.lightGray;
616: else if (name.equals("magenta"))
617: return Color.magenta;
618: else if (name.equals("orange"))
619: return Color.orange;
620: else if (name.equals("pink"))
621: return Color.pink;
622: else if (name.equals("white"))
623: return Color.white;
624: else if (name.equals("yellow"))
625: return Color.yellow;
626: else if (name.charAt(0) == '#') {
627: return new Color(Integer.parseInt(name.substring(1), 16));
628: }
629:
630: return null;
631: }
632:
633: /**
634: * Get the page loader type - a unique name identifying the loader
635: * @return "infopath"
636: */
637: public String getType() {
638: return "infopath";
639: }
640: }
|