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