/*
* Beryl - A web platform based on XML, XSLT and Java
* This file is part of the Beryl XML GUI
*
* Copyright (C) 2004 Wenzel Jakob <wazlaf@tigris.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-3107 USA
*/
package org.beryl.gui;
import java.awt.Component;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import javax.swing.JComponent;
import javax.xml.parsers.DocumentBuilderFactory;
import org.beryl.gui.model.MapDataModel;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* The widget factory is required to construct XML GUI interfaces from XML description files
*/
public class WidgetFactory {
public static final String DEFAULT_PREFIX = "org.beryl.gui.widgets.";
private static WidgetFactory factoryInstance = new WidgetFactory();
private DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
private PropertyFactory pf = PropertyFactory.getInstance();
private LayoutFactory lf = LayoutFactory.getInstance();
private AnchorFactory af = AnchorFactory.getInstance();
private HashMap constructorCache = null;
private HashMap documentCache = null;
private WidgetFactory() {
constructorCache = new HashMap();
documentCache = new HashMap();
}
public static final WidgetFactory getInstance() {
return factoryInstance;
}
public Widget constructWidget(Class controllerClass, String widgetName) throws GUIException {
return constructWidget(controllerClass, widgetName, null, null, null);
}
public Widget constructWidget(Class controllerClass, String widgetName, GUIEventListener listener)
throws GUIException {
return constructWidget(controllerClass, widgetName, listener, null, null);
}
public Widget constructWidget(Class controllerClass, String widgetName, GUIEventListener listener, MapDataModel dataModel)
throws GUIException {
return constructWidget(controllerClass, widgetName, listener, dataModel, null);
}
public Widget constructWidget(
Class controllerClass,
String widgetName,
GUIEventListener listener,
MapDataModel dataModel,
Widget parentWidget)
throws GUIException {
/* Find GUI description file using the controller class */
URL url = getClass().getResource("/" + controllerClass.getName().replace('.', '/') + ".xml");
if (url == null)
throw new GUIException(
"Class [" + controllerClass.getName() + "] has no gui description file at [" + url + "]");
return constructWidget(url, widgetName, listener, dataModel, parentWidget);
}
public Widget constructWidget(
URL url,
String widgetName,
GUIEventListener listener,
MapDataModel dataModel,
Widget parentWidget)
throws GUIException {
Document document = null;
try {
document = (Document) documentCache.get(url.toString());
if (document == null) {
document = dbf.newDocumentBuilder().parse(url.openStream());
documentCache.put(url.toString(), document);
}
} catch (Exception e) {
throw new GUIException("Could not parse widget file", e);
}
return constructWidget(document, widgetName, listener, dataModel, parentWidget);
}
private Widget constructWidget(
Document document,
String widgetName,
GUIEventListener listener,
MapDataModel dataModel,
Widget parentWidget)
throws GUIException {
Element root = document.getDocumentElement();
String version = root.getAttribute("version");
NodeList widgets = root.getChildNodes();
if (!version.equals("1.0")) {
throw new GUIException("Invalid XML description file version [" + version + "]");
}
for (int i = 0; i < widgets.getLength(); i++) {
Node node = widgets.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE
&& node.getNodeName().equals("widget")
&& ((Element) node).getAttribute("name").equals(widgetName))
return constructWidget(document, (Element) node, listener, dataModel, parentWidget);
}
throw new GUIException("Widget [" + widgetName + "] could not be found");
}
private Widget constructWidget(
Document descriptionFile,
Element widgetNode,
GUIEventListener listener,
MapDataModel dataModel,
Widget parentWidget)
throws GUIException {
Widget widget = null;
String widgetClass = widgetNode.getAttribute("class");
String widgetName = widgetNode.getAttribute("name");
String widgetPreset = widgetNode.getAttribute("preset");
boolean hasPreset = !widgetPreset.equals("");
Constructor widgetConstructor = (Constructor) constructorCache.get(widgetClass + (hasPreset ? "_P" : ""));
NodeList childNodes = widgetNode.getChildNodes();
try {
try {
if (widgetConstructor == null) {
String classAttribute = widgetNode.getAttribute("class");
String className = DEFAULT_PREFIX + classAttribute;
if (classAttribute.indexOf('.') != -1)
className = classAttribute;
Class wClass = Class.forName(className);
if (hasPreset)
widgetConstructor = wClass.getConstructor(new Class[] { Widget.class, String.class, String.class });
else
widgetConstructor = wClass.getConstructor(new Class[] { Widget.class, String.class });
constructorCache.put(widgetClass + (hasPreset ? "_P" : ""), widgetConstructor);
}
if (hasPreset)
widget =
(Widget) widgetConstructor.newInstance(
new Object[] { parentWidget, widgetName.equals("") ? null : widgetName, widgetPreset });
else
widget =
(Widget) widgetConstructor.newInstance(
new Object[] { parentWidget, widgetName.equals("") ? null : widgetName });
} catch (ClassNotFoundException e) {
throw new GUIException("Unknown widget class [" + widgetNode.getAttribute("class") + "]");
} catch (NoSuchMethodException e) {
throw new GUIException("Widget constructor not found", e);
} catch (IllegalAccessException e) {
throw new GUIException("Widget constructor could not be called", e);
} catch (InstantiationException e) {
throw new GUIException("Widget is abstract", e);
} catch (InvocationTargetException e) {
throw new GUIException("Widget constructor threw an exception", e);
}
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i).getNodeName().equals("property")) {
Element propertyNode = (Element) childNodes.item(i);
widget.setProperty(propertyNode.getAttribute("name"), pf.constructProperty(propertyNode));
} else if (listener != null && childNodes.item(i).getNodeName().equals("emit")) {
Element emitNode = (Element) childNodes.item(i);
widget.addListener(emitNode.getAttribute("event"), emitNode.getAttribute("name"), listener);
} else if (childNodes.item(i).getNodeName().equals("layout")) {
Element layoutNode = (Element) childNodes.item(i);
widget.setProperty("layout", lf.constructLayout(widget, layoutNode));
}
}
} catch (Exception e) {
throw new GUIException("Error while creating a widget of class [" + widgetClass + "], name [" + widgetName + "] and preset [" + widgetPreset + "]", e);
}
/* Process child nodes after all properties are set */
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i).getNodeName().equals("widget")) {
Element childNode = (Element) childNodes.item(i);
Widget childWidget = null;
Object anchor = null;
/* Get the child's anchor subnode */
Element anchorNode = XMLUtils.getChild(childNode, "anchor");
/* Include other files if required */
if (!childNode.getAttribute("include").equals("")) {
String includeName = childNode.getAttribute("name");
String includeFile = childNode.getAttribute("include");
if (includeName.equals(""))
throw new GUIException("The name attribute needs to be specified for includes");
if (!includeFile.equals("this")) {
URL url = getClass().getResource("/" + includeFile);
if (url == null)
throw new GUIException("Include file [" + includeFile + "] does not exist");
childWidget = constructWidget(url, includeName, listener, dataModel, widget);
} else {
childWidget = constructWidget(descriptionFile, includeName, listener, dataModel, widget);
}
if (childWidget == null)
throw new GUIException(
"Widget [" + includeName + "] not found in include file [" + includeFile + "]");
} else {
childWidget = constructWidget(descriptionFile, childNode, listener, dataModel, widget);
}
if (anchorNode != null) {
anchor = af.constructAnchor(anchorNode);
if (anchor instanceof AnchorFactory.BoxAnchor) {
AnchorFactory.BoxAnchor ba = (AnchorFactory.BoxAnchor) anchor;
JComponent component = (JComponent) childWidget.getWidget();
component.setAlignmentX(ba.getAlignmentX());
component.setAlignmentY(ba.getAlignmentY());
anchor = null;
}
} else {
Component comp = (Component) childWidget.getWidget();
if (comp != null && comp instanceof JComponent) {
JComponent component = (JComponent) comp;
component.setAlignmentX(0.0f);
component.setAlignmentY(0.0f);
}
}
widget.addChild(childWidget, anchor);
}
}
try {
/* Set the data model */
if (dataModel != null)
widget.setDataModel(dataModel);
/* Finalize construction */
widget.finalizeConstruction();
} catch (Exception e) {
throw new GUIException("Error while finalizing a widget of class [" + widgetClass + "], name [" + widgetName + "] and preset [" + widgetPreset + "]", e);
}
return widget;
}
}