Java tutorial
/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ /** * */ package org.pentaho.ui.xul.impl; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Attribute; import org.dom4j.Namespace; import org.dom4j.QName; import org.pentaho.ui.xul.XulComponent; import org.pentaho.ui.xul.XulContainer; import org.pentaho.ui.xul.XulDomContainer; import org.pentaho.ui.xul.XulException; import org.pentaho.ui.xul.containers.XulRoot; import org.pentaho.ui.xul.containers.XulWindow; import org.pentaho.ui.xul.dom.Document; import org.pentaho.ui.xul.dom.DocumentFactory; import org.pentaho.ui.xul.dom.Element; import org.pentaho.ui.xul.util.XulUtil; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author nbaker * */ public class XulParser { Document xulDocument; private static final Log logger = LogFactory.getLog(XulParser.class); public Map<String, Object> handlers = new HashMap<String, Object>(); private Map<String, Constructor<?>> constructorCache = new HashMap<String, Constructor<?>>(); private XulDomContainer xulDomContainer; private List<ClassLoader> classloaders = new ArrayList<ClassLoader>(); { classloaders.add(this.getClass().getClassLoader()); } public XulParser() throws XulException { try { xulDocument = DocumentFactory.createDocument(); } catch (Exception e) { throw new XulException("Error getting Document instance", e); } } public void setContainer(XulDomContainer xulDomContainer) { this.xulDomContainer = xulDomContainer; xulDocument.setXulDomContainer(xulDomContainer); xulDomContainer.addDocument(xulDocument); } public Document parseDocument(org.dom4j.Element rootSrc) throws XulException { XulContainer parent = null; if (!rootSrc.getName().equalsIgnoreCase("window") && (!rootSrc.getName().equalsIgnoreCase("dialog"))) { parent = getPlaceHolderRoot(); } XulComponent root = parse(rootSrc, parent); // give root reference to runner for service calls and attach root to document if (root instanceof XulRoot) { ((XulRoot) root).setXulDomContainer(this.xulDomContainer); xulDocument.addChild(root); } else { // fragment parsing, wire up dummy ((XulRoot) parent).setXulDomContainer(this.xulDomContainer); parent.addChild(root); xulDocument.addChild(parent); } // descend back down firing notification that everything is on the tree. // do not do this for overlays as they will get the notification when added // to the real document. if (rootSrc.getName().equalsIgnoreCase("window") || rootSrc.getName().equalsIgnoreCase("dialog")) { notifyDomReady(root); } return xulDocument; } private void notifyDomReady(XulComponent node) { node.onDomReady(); for (XulComponent c : node.getChildNodes()) { notifyDomReady(c); } } private Constructor<?> getContructor(String className) throws XulException { Constructor<?> con = constructorCache.get(className); if (con != null) { return con; } Class<?> c = null; Throwable lastException = null; for (ClassLoader loader : classloaders) { try { c = loader.loadClass(className); if (c != null) { break; } } catch (ClassNotFoundException e) { lastException = e; } } if (c == null && lastException != null) { throw new XulException(lastException); } try { Constructor<?> constructor = c.getConstructor( new Class[] { Element.class, XulComponent.class, XulDomContainer.class, String.class }); constructorCache.put(className, constructor); return constructor; } catch (NoSuchMethodException e1) { throw new XulException(e1); } } public XulContainer getPlaceHolderRoot() throws XulException { try { Object handlerClassName = handlers.get("WINDOW"); if (handlerClassName == null) { throw new XulException("Could not find a tag handler for window"); } XulWindow ele = (XulWindow) getContructor(handlerClassName.toString()).newInstance(null, null, xulDomContainer, "window"); return ele; } catch (Exception e) { throw new XulException(e); } } public XulComponent parse(org.dom4j.Element rootSrc, XulContainer parent) throws XulException { // parse element XulComponent root = getElement(rootSrc, parent); if (root == null) { return null; } // descend down a level and parse children (root would be a container in the case) for (Object child : rootSrc.elements()) { XulComponent childElement = parse((org.dom4j.Element) child, (XulContainer) root); // TODO: remove once exception handling in place if (childElement == null) { continue; } // Add to the XML DOM tree ... root.addChild(childElement); } if (root != null) { // should layout be part of the public API? // is this the appropriate place for layout? if (root instanceof AbstractXulComponent) { ((AbstractXulComponent) root).layout(); } } return root; } protected XulComponent getElement(org.dom4j.Element srcEle, XulContainer parent) throws XulException { String handlerName = srcEle.getName().toUpperCase(); Attribute att = srcEle .attribute(new QName("customclass", new Namespace("pen", "http://www.pentaho.org/2008/xul"))); // If the custom handler is registered, use it; otherwise, fall back to the original element handler... if (att != null) { String potentialHandlerName = att.getValue().toUpperCase(); if (handlers.get(potentialHandlerName) != null) { handlerName = potentialHandlerName; } } Object handler = handlers.get(handlerName); if (handler == null) { logger.error("handler not found: " + handlerName); return null; // throw new XulException(String.format("No handler available for input: %s", srcEle.getName())); } String tagName = srcEle.getName(); try { Constructor<?> constructor = getContructor((String) handler); // create a generic element representation of the current Dom4J node Element domEle = DocumentFactory.createElement(srcEle.getName().toLowerCase()); List<Attribute> attrs = srcEle.attributes(); for (Attribute attr : attrs) { domEle.setAttribute(attr.getName(), attr.getValue()); } XulComponent ele = (XulComponent) constructor.newInstance(domEle, parent, xulDomContainer, tagName); // preserve atributes in new Generic Dom node for (Attribute attr : attrs) { ele.setAttribute(attr.getName(), attr.getValue()); } Map<String, String> attributesMap = XulUtil.AttributesToMap(srcEle.attributes()); BeanUtils.populate(ele, attributesMap); return ele; } catch (Exception e) { throw new XulException(e); } } public XulComponent getElement(String name) throws XulException { return getElement(name, null); } public XulComponent getElement(String name, XulComponent defaultParent) throws XulException { Object handler = handlers.get(name.toUpperCase()); if (handler == null) { logger.error("tag handler not found: " + name); throw new XulException(String.format("No handler available for input: %s", name)); } try { Constructor<?> constructor = getContructor((String) handler); XulComponent ele = (XulComponent) constructor.newInstance(null, defaultParent, xulDomContainer, name); return ele; } catch (Exception e) { throw new XulException(e); } } public void registerHandler(String type, String handler) { handlers.put(type.toUpperCase(), handler); } public Document getDocumentRoot() { return this.xulDocument; } /** * @throws XulException * Resets the state of the parser. */ public void reset() throws XulException { try { xulDocument = DocumentFactory.createDocument(); } catch (Exception e) { throw new XulException("Error getting Document instance", e); } } public void setClassLoaders(List<ClassLoader> loaders) { this.classloaders = loaders; } }