001: /*
002: * Beryl - A web platform based on XML, XSLT and Java
003: * This file is part of the Beryl XML GUI
004: *
005: * Copyright (C) 2004 Wenzel Jakob <wazlaf@tigris.org>
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011:
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-3107 USA
020: */
021:
022: package org.beryl.gui;
023:
024: import java.awt.Component;
025: import java.lang.reflect.Constructor;
026: import java.lang.reflect.InvocationTargetException;
027: import java.net.URL;
028: import java.util.HashMap;
029:
030: import javax.swing.JComponent;
031: import javax.xml.parsers.DocumentBuilderFactory;
032:
033: import org.beryl.gui.model.MapDataModel;
034: import org.w3c.dom.Document;
035: import org.w3c.dom.Element;
036: import org.w3c.dom.Node;
037: import org.w3c.dom.NodeList;
038:
039: /**
040: * The widget factory is required to construct XML GUI interfaces from XML description files
041: */
042: public class WidgetFactory {
043: public static final String DEFAULT_PREFIX = "org.beryl.gui.widgets.";
044: private static WidgetFactory factoryInstance = new WidgetFactory();
045: private DocumentBuilderFactory dbf = DocumentBuilderFactory
046: .newInstance();
047: private PropertyFactory pf = PropertyFactory.getInstance();
048: private LayoutFactory lf = LayoutFactory.getInstance();
049: private AnchorFactory af = AnchorFactory.getInstance();
050: private HashMap constructorCache = null;
051: private HashMap documentCache = null;
052:
053: private WidgetFactory() {
054: constructorCache = new HashMap();
055: documentCache = new HashMap();
056: }
057:
058: public static final WidgetFactory getInstance() {
059: return factoryInstance;
060: }
061:
062: public Widget constructWidget(Class controllerClass,
063: String widgetName) throws GUIException {
064: return constructWidget(controllerClass, widgetName, null, null,
065: null);
066: }
067:
068: public Widget constructWidget(Class controllerClass,
069: String widgetName, GUIEventListener listener)
070: throws GUIException {
071: return constructWidget(controllerClass, widgetName, listener,
072: null, null);
073: }
074:
075: public Widget constructWidget(Class controllerClass,
076: String widgetName, GUIEventListener listener,
077: MapDataModel dataModel) throws GUIException {
078: return constructWidget(controllerClass, widgetName, listener,
079: dataModel, null);
080: }
081:
082: public Widget constructWidget(Class controllerClass,
083: String widgetName, GUIEventListener listener,
084: MapDataModel dataModel, Widget parentWidget)
085: throws GUIException {
086: /* Find GUI description file using the controller class */
087: String path = "/" + controllerClass.getName().replace('.', '/')
088: + ".xml";
089: URL url = getClass().getResource(path);
090: if (url == null)
091: throw new GUIException("Class ["
092: + controllerClass.getName()
093: + "] has no gui description file at [" + path + "]");
094: return constructWidget(url, widgetName, listener, dataModel,
095: parentWidget);
096: }
097:
098: public Widget constructWidget(URL url, String widgetName,
099: GUIEventListener listener, MapDataModel dataModel,
100: Widget parentWidget) throws GUIException {
101: Document document = null;
102: try {
103: document = (Document) documentCache.get(url.toString());
104: if (document == null) {
105: document = dbf.newDocumentBuilder().parse(
106: url.openStream());
107: documentCache.put(url.toString(), document);
108: }
109: } catch (Exception e) {
110: throw new GUIException("Could not parse widget file", e);
111: }
112:
113: return constructWidget(document, widgetName, listener,
114: dataModel, parentWidget);
115: }
116:
117: private Widget constructWidget(Document document,
118: String widgetName, GUIEventListener listener,
119: MapDataModel dataModel, Widget parentWidget)
120: throws GUIException {
121:
122: Element root = document.getDocumentElement();
123: String version = root.getAttribute("version");
124: NodeList widgets = root.getChildNodes();
125:
126: if (!version.equals("1.0")) {
127: throw new GUIException(
128: "Invalid XML description file version [" + version
129: + "]");
130: }
131:
132: for (int i = 0; i < widgets.getLength(); i++) {
133: Node node = widgets.item(i);
134: if (node.getNodeType() == Node.ELEMENT_NODE
135: && node.getNodeName().equals("widget")
136: && ((Element) node).getAttribute("name").equals(
137: widgetName))
138: return constructWidget(document, (Element) node,
139: listener, dataModel, parentWidget);
140: }
141: throw new GUIException("Widget [" + widgetName
142: + "] could not be found");
143: }
144:
145: private Widget constructWidget(Document descriptionFile,
146: Element widgetNode, GUIEventListener listener,
147: MapDataModel dataModel, Widget parentWidget)
148: throws GUIException {
149: Widget widget = null;
150: String widgetClass = widgetNode.getAttribute("class");
151: String widgetName = widgetNode.getAttribute("name");
152: String widgetPreset = widgetNode.getAttribute("preset");
153: boolean hasPreset = !widgetPreset.equals("");
154: Constructor widgetConstructor = (Constructor) constructorCache
155: .get(widgetClass + (hasPreset ? "_P" : ""));
156: NodeList childNodes = widgetNode.getChildNodes();
157:
158: try {
159: try {
160: if (widgetConstructor == null) {
161: String classAttribute = widgetNode
162: .getAttribute("class");
163: String className = DEFAULT_PREFIX + classAttribute;
164: if (classAttribute.indexOf('.') != -1)
165: className = classAttribute;
166:
167: Class wClass = Class.forName(className);
168: if (hasPreset)
169: widgetConstructor = wClass
170: .getConstructor(new Class[] {
171: Widget.class, String.class,
172: String.class });
173: else
174: widgetConstructor = wClass
175: .getConstructor(new Class[] {
176: Widget.class, String.class });
177: constructorCache.put(widgetClass
178: + (hasPreset ? "_P" : ""),
179: widgetConstructor);
180: }
181: if (hasPreset)
182: widget = (Widget) widgetConstructor
183: .newInstance(new Object[] {
184: parentWidget,
185: widgetName.equals("") ? null
186: : widgetName, widgetPreset });
187: else
188: widget = (Widget) widgetConstructor
189: .newInstance(new Object[] {
190: parentWidget,
191: widgetName.equals("") ? null
192: : widgetName });
193: } catch (ClassNotFoundException e) {
194: throw new GUIException("Unknown widget class ["
195: + widgetNode.getAttribute("class") + "]");
196: } catch (NoSuchMethodException e) {
197: throw new GUIException("Widget constructor not found",
198: e);
199: } catch (IllegalAccessException e) {
200: throw new GUIException(
201: "Widget constructor could not be called", e);
202: } catch (InstantiationException e) {
203: throw new GUIException("Widget is abstract", e);
204: } catch (InvocationTargetException e) {
205: throw new GUIException(
206: "Widget constructor threw an exception", e);
207: }
208:
209: for (int i = 0; i < childNodes.getLength(); i++) {
210: if (childNodes.item(i).getNodeName().equals("property")) {
211: Element propertyNode = (Element) childNodes.item(i);
212: widget.setProperty(propertyNode
213: .getAttribute("name"), pf
214: .constructProperty(propertyNode));
215: } else if (listener != null
216: && childNodes.item(i).getNodeName().equals(
217: "emit")) {
218: Element emitNode = (Element) childNodes.item(i);
219: widget.addListener(emitNode.getAttribute("event"),
220: emitNode.getAttribute("name"), listener);
221: } else if (childNodes.item(i).getNodeName().equals(
222: "layout")) {
223: Element layoutNode = (Element) childNodes.item(i);
224: widget.setProperty("layout", lf.constructLayout(
225: widget, layoutNode));
226: }
227: }
228: } catch (Exception e) {
229: throw new GUIException(
230: "Error while creating a widget of class ["
231: + widgetClass + "], name [" + widgetName
232: + "] and preset [" + widgetPreset + "]", e);
233: }
234: /* Process child nodes after all properties are set */
235: for (int i = 0; i < childNodes.getLength(); i++) {
236: if (childNodes.item(i).getNodeName().equals("widget")) {
237: Element childNode = (Element) childNodes.item(i);
238: Widget childWidget = null;
239: Object anchor = null;
240:
241: /* Get the child's anchor subnode */
242: Element anchorNode = XMLUtils.getChild(childNode,
243: "anchor");
244:
245: /* Include other files if required */
246: if (!childNode.getAttribute("include").equals("")) {
247: String includeName = childNode.getAttribute("name");
248: String includeFile = childNode
249: .getAttribute("include");
250: if (includeName.equals(""))
251: throw new GUIException(
252: "The name attribute needs to be specified for includes");
253: if (!includeFile.equals("this")) {
254: URL url = getClass().getResource(
255: "/" + includeFile);
256: if (url == null)
257: throw new GUIException("Include file ["
258: + includeFile + "] does not exist");
259: childWidget = constructWidget(url, includeName,
260: listener, dataModel, widget);
261: } else {
262: childWidget = constructWidget(descriptionFile,
263: includeName, listener, dataModel,
264: widget);
265: }
266: if (childWidget == null)
267: throw new GUIException("Widget [" + includeName
268: + "] not found in include file ["
269: + includeFile + "]");
270: } else {
271: childWidget = constructWidget(descriptionFile,
272: childNode, listener, dataModel, widget);
273: }
274: if (anchorNode != null) {
275: anchor = af.constructAnchor(anchorNode);
276: if (anchor instanceof AnchorFactory.BoxAnchor) {
277: AnchorFactory.BoxAnchor ba = (AnchorFactory.BoxAnchor) anchor;
278: JComponent component = (JComponent) childWidget
279: .getWidget();
280: component.setAlignmentX(ba.getAlignmentX());
281: component.setAlignmentY(ba.getAlignmentY());
282: anchor = null;
283: }
284: } else {
285: Component comp = (Component) childWidget
286: .getWidget();
287:
288: if (comp != null && comp instanceof JComponent) {
289: JComponent component = (JComponent) comp;
290:
291: component.setAlignmentX(0.0f);
292: component.setAlignmentY(0.0f);
293: }
294: }
295: widget.addChild(childWidget, anchor);
296: }
297: }
298: try {
299: /* Set the data model */
300: if (dataModel != null)
301: widget.setDataModel(dataModel);
302:
303: /* Finalize construction */
304: widget.finalizeConstruction();
305: } catch (Exception e) {
306: throw new GUIException(
307: "Error while finalizing a widget of class ["
308: + widgetClass + "], name [" + widgetName
309: + "] and preset [" + widgetPreset + "]", e);
310: }
311:
312: return widget;
313: }
314: }
|