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.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.XPath; import org.dom4j.io.SAXReader; import org.dom4j.xpath.DefaultXPath; 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.XulLoader; import org.pentaho.ui.xul.XulSettingsManager; import org.pentaho.ui.xul.dom.DocumentFactory; import org.pentaho.ui.xul.dom.dom4j.DocumentDom4J; import org.pentaho.ui.xul.dom.dom4j.ElementDom4J; import org.pentaho.ui.xul.util.ResourceBundleTranslator; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; public abstract class AbstractXulLoader implements XulLoader { protected XulParser parser; protected String rootDir = "/"; protected Object outerContext = null; protected static final Log logger = LogFactory.getLog(AbstractXulLoader.class); private ResourceBundle mainBundle = null; private List<Object> resourceBundleList = new ArrayList<Object>(); private List<ClassLoader> classloaders = new ArrayList<ClassLoader>(); private HashMap<String, ResourceBundle> cachedResourceBundles; private XulSettingsManager settings; private Locale locale; private List<String> includedSources = new ArrayList<String>(); private List<String> resourceBundles = new ArrayList<String>(); private URLClassLoader localDirClassLoader; public AbstractXulLoader() throws XulException { classloaders.add(this.getClass().getClassLoader()); DocumentFactory.registerDOMClass(DocumentDom4J.class); DocumentFactory.registerElementClass(ElementDom4J.class); cachedResourceBundles = new HashMap<String, ResourceBundle>(); try { parser = new XulParser(); } catch (Exception e) { throw new XulException( "Error getting XulParser Instance, probably a DOM Factory problem: " + e.getMessage(), e); } locale = Locale.getDefault(); } /* * (non-Javadoc) * * @see org.pentaho.ui.xul.XulLoader#loadXul(org.w3c.dom.Document) */ public XulDomContainer loadXul(Object xulDocument) throws IllegalArgumentException, XulException { Document document = (Document) xulDocument; try { xulDocument = preProcess(document); String processedDoc = performIncludeTranslations(document.asXML()); String localOutput = (mainBundle != null) ? ResourceBundleTranslator.translate(processedDoc, mainBundle) : processedDoc; SAXReader rdr = new SAXReader(); // localOutput = localOutput.replace("UTF-8", "ISO-8859-1"); // System.out.println("============ Post Processed: ============"); // System.out.println(localOutput); // System.out.println("============ End Post Processed: ============"); final Document doc = rdr.read(new StringReader(localOutput)); // System.out.println("============ After Parse: ============"); // System.out.println(doc.asXML()); // System.out.println("============ After Parse: ============"); XulDomContainer container = new XulWindowContainer(this); container.setOuterContext(outerContext); container.setSettingsManager(settings); container.setResourceBundles(this.resourceBundleList); parser.setContainer(container); parser.setClassLoaders(classloaders); parser.parseDocument(doc.getRootElement()); for (ClassLoader l : classloaders) { container.registerClassLoader(l); } return container; } catch (Exception e) { throw new XulException(e); } } public void setRootDir(String loc) { if (!rootDir.equals("/")) { // lets only set this once return; } if (loc.lastIndexOf("/") > 0 && loc.indexOf(".xul") > -1) { // exists and not first char rootDir = loc.substring(0, loc.lastIndexOf("/") + 1); } else { rootDir = loc; if (!(loc.lastIndexOf('/') == loc.length())) { // no trailing slash, add it rootDir = rootDir + "/"; } } } public void registerResourceBundle(final ResourceBundle bundle) { if (bundle == null) { throw new NullPointerException(); } this.resourceBundleList.add(bundle); } /* * (non-Javadoc) * * @see org.pentaho.ui.xul.XulLoader#loadXulFragment(org.dom4j.Document) */ public XulDomContainer loadXulFragment(Object xulDocument) throws IllegalArgumentException, XulException { Document document = (Document) xulDocument; XulDomContainer container = new XulFragmentContainer(this); container.setResourceBundles(this.resourceBundleList); container.setOuterContext(outerContext); container.setSettingsManager(settings); parser.reset(); parser.setClassLoaders(classloaders); parser.setContainer(container); parser.parseDocument(document.getRootElement()); return container; } public XulComponent createElement(String elementName) throws XulException { return parser.getElement(elementName); } private ResourceBundle loadResourceBundle(final String resStr) { ResourceBundle res = cachedResourceBundles.get(resStr); if (res == null) { for (final ClassLoader cl : classloaders) { try { res = ResourceBundle.getBundle(resStr, locale, cl); if (res != null) { break; } } catch (MissingResourceException e) { // ignored .. } } if (res == null) { try { final URLClassLoader cls = getLocalDirClassLoader(); res = ResourceBundle.getBundle(resStr, locale, cls); } catch (MalformedURLException ex) { return null; } catch (MissingResourceException ex) { return null; } } cachedResourceBundles.put(resStr, res); } return res; } public XulDomContainer loadXul(String resource) throws IllegalArgumentException, XulException { setRootDir(resource); final String resStr = resource.replace(".xul", ""); final ResourceBundle res = loadResourceBundle(resStr); if (res == null) { final Document doc = findDocument(resource); return loadXul(doc); } else { return loadXul(resource, res); } } public XulDomContainer loadXul(String resource, Object bundle) throws XulException { final Document doc = findDocument(resource); setRootDir(resource); mainBundle = (ResourceBundle) bundle; final String resStr = resource.replace(".xul", ""); final ResourceBundle res = loadResourceBundle(resStr); if (res != null) { resourceBundleList.add(res); } else { return loadXul(doc); } resourceBundleList.add(mainBundle); return this.loadXul(doc); } public XulDomContainer loadXulFragment(String resource) throws IllegalArgumentException, XulException { setRootDir(resource); final String resStr = resource.replace(".xul", ""); final ResourceBundle res = loadResourceBundle(resStr); if (res == null) { final Document doc = findDocument(resource); return loadXulFragment(doc); } return loadXulFragment(resource, res); } public XulDomContainer loadXulFragment(String resource, Object bundle) throws XulException { if (bundle instanceof ResourceBundle == false) { throw new XulException("Need a resource-bundle as bundle"); } try { final InputStream in = getResourceAsStream(resource); if (in == null) { throw new XulException("Given resource does not yield a valid document"); } try { resourceBundleList.add(bundle); final String localOutput = ResourceBundleTranslator.translate(in, (ResourceBundle) bundle); final SAXReader rdr = new SAXReader(); final Document doc = rdr.read(new StringReader(localOutput)); setRootDir(resource); return this.loadXulFragment(doc); } finally { in.close(); } } catch (DocumentException e) { throw new XulException("Error parsing Xul Document", e); } catch (IOException e) { throw new XulException("Error loading Xul Document into Freemarker", e); } } public String performIncludeTranslations(final String input) throws XulException { for (final String includeSrc : includedSources) { final String resStr = includeSrc.replace(".xul", ""); final ResourceBundle res = loadResourceBundle(resStr); if (res != null) { resourceBundleList.add(res); } else { try { final URLClassLoader cls = getLocalDirClassLoader(); resourceBundleList.add(ResourceBundle.getBundle(resStr, locale, cls)); } catch (MalformedURLException ex) { // intentionally empty } catch (MissingResourceException ex) { // intentionally empty } } } for (final String resource : resourceBundles) { logger.debug("Processing Resource Bundle: " + resource); final ResourceBundle res = loadResourceBundle(resource); if (res != null) { resourceBundleList.add(res); } else { try { final URLClassLoader cls = getLocalDirClassLoader(); resourceBundleList.add(ResourceBundle.getBundle(resource, locale, cls)); } catch (MalformedURLException ex) { // intentionally empty } catch (MissingResourceException ex) { // intentionally empty } } } String output = input; for (final Object bundle : resourceBundleList) { try { output = ResourceBundleTranslator.translate(output, (ResourceBundle) bundle); } catch (IOException e) { e.printStackTrace(); } } return output; } private URLClassLoader getLocalDirClassLoader() throws MalformedURLException { if (localDirClassLoader == null) { final URL url = new File(".").toURI().toURL(); localDirClassLoader = URLClassLoader.newInstance(new URL[] { url }); } return localDirClassLoader; } public void register(String tagName, String className) { parser.registerHandler(tagName, className); } public String getRootDir() { return this.rootDir; } public Document preProcess(Document srcDoc) throws XulException { XPath xpath = new DefaultXPath("//pen:include"); HashMap uris = new HashMap(); uris.put("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); uris.put("pen", "http://www.pentaho.org/2008/xul"); xpath.setNamespaceURIs(uris); List<Element> eles = xpath.selectNodes(srcDoc); for (Element ele : eles) { String src = ""; src = this.getRootDir() + ele.attributeValue("src"); String resourceBundle = ele.attributeValue("resource"); if (resourceBundle != null) { resourceBundles.add(resourceBundle); } else { resourceBundles.add(src.replace(".xul", "")); } InputStream in = null; try { in = getResourceAsStream(src); if (in != null) { logger.debug("Adding include src: " + src); includedSources.add(src); } else { // try fully qualified name src = ele.attributeValue("src"); in = getResourceAsStream(src); if (in != null) { includedSources.add(src); logger.debug("Adding include src: " + src); } else { File f = new File(this.getRootDir() + src); if (f.exists()) { try { in = new FileInputStream(f); includedSources.add(src); } catch (FileNotFoundException e) { e.printStackTrace(); } } } } final Document doc = getDocFromInputStream(in); Element root = doc.getRootElement(); String ignoreRoot = ele.attributeValue("ignoreroot"); if (root.getName().equals("overlay")) { processOverlay(root, ele.getDocument().getRootElement()); } else if (ignoreRoot == null || ignoreRoot.equalsIgnoreCase("false")) { logger.debug("Including entire file: " + src); List contentOfParent = ele.getParent().content(); int index = contentOfParent.indexOf(ele); contentOfParent.set(index, root); if (root.getName().equals("dialog")) { String newOnload = root.attributeValue("onload"); if (newOnload != null) { String existingOnload = srcDoc.getRootElement().attributeValue("onload"); String finalOnload = ""; if (existingOnload != null) { finalOnload = existingOnload + ", "; } finalOnload += newOnload; srcDoc.getRootElement().setAttributeValue("onload", finalOnload); } } // process any overlay children List<Element> overlays = ele.elements(); for (Element overlay : overlays) { logger.debug("Processing overlay within include"); this.processOverlay(overlay.attributeValue("src"), srcDoc); } } else { logger.debug("Including children: " + src); List contentOfParent = ele.getParent().content(); int index = contentOfParent.indexOf(ele); contentOfParent.remove(index); List children = root.elements(); for (int i = children.size() - 1; i >= 0; i--) { Element child = (Element) children.get(i); contentOfParent.add(index, child); if (child.getName().equals("dialog")) { String newOnload = child.attributeValue("onload"); if (newOnload != null) { String existingOnload = srcDoc.getRootElement().attributeValue("onload"); String finalOnload = ""; if (existingOnload != null) { finalOnload = existingOnload + ", "; } finalOnload += newOnload; srcDoc.getRootElement().setAttributeValue("onload", finalOnload); } } } // process any overlay children List<Element> overlays = ele.elements(); for (Element overlay : overlays) { logger.debug("Processing overlay within include"); this.processOverlay(overlay.attributeValue("src"), srcDoc); } } } finally { try { if (in != null) { in.close(); } } catch (IOException ignored) { //ignored } } } return srcDoc; } protected Document getDocFromInputStream(InputStream in) throws XulException { try { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuffer buf = new StringBuffer(); String line; while ((line = reader.readLine()) != null) { buf.append(line); } in.close(); String upperedIdDoc = this.upperCaseIDAttrs(buf.toString()); SAXReader rdr = new SAXReader(); return rdr.read(new StringReader(upperedIdDoc)); } catch (Exception e) { e.printStackTrace(); return null; } } protected Document findDocument(String src) throws XulException { Document doc = getDocFromClasspath(src); if (doc == null) { doc = getDocFromFile(src); } if (doc == null) { throw new XulException("Can not locate Xul document [" + src + "]"); } return doc; } protected Document getDocFromClasspath(String src) throws XulException { InputStream in = getResourceAsStream(this.getRootDir() + src); if (in != null) { Document doc = getDocFromInputStream(in); return doc; } else { // try fully qualified name in = getResourceAsStream(src); if (in != null) { return getDocFromInputStream(in); } else { return null; // throw new XulException("Can no locate Xul document [" + src + "]"); } } } protected Document getDocFromFile(String src) throws XulException { File f = new File(this.getRootDir() + src); if (f.exists()) { Document doc; try { doc = getDocFromInputStream(new FileInputStream(f)); return doc; } catch (FileNotFoundException ignored) { //ignored } } else { // try fully qualified name f = new File(src); if (f.exists()) { Document doc; try { doc = getDocFromInputStream(new FileInputStream(f)); return doc; } catch (FileNotFoundException ignored) { //ignored } } else { return null; // throw new XulException("Can no locate Xul document [" + src + "]"); } } return null; } protected void processOverlay(String overlaySrc, Document doc) { try { final Document overlayDoc = findDocument(overlaySrc); processOverlay(overlayDoc.getRootElement(), doc.getRootElement()); } catch (Exception e) { logger.error("Could not load include overlay document: " + overlaySrc, e); } } protected void processOverlay(Element overlayEle, Element srcEle) { for (Object child : overlayEle.elements()) { Element overlay = (Element) child; String overlayId = overlay.attributeValue("ID"); logger.debug("Processing overlay\nID: " + overlayId); Element sourceElement = srcEle.getDocument().elementByID(overlayId); if (sourceElement == null) { logger.error("Could not find corresponding element in src doc with id: " + overlayId); continue; } logger.debug("Found match in source doc:"); String removeElement = overlay.attributeValue("removeelement"); if (removeElement != null && removeElement.equalsIgnoreCase("true")) { sourceElement.getParent().remove(sourceElement); } else { List attribs = overlay.attributes(); // merge in attributes for (Object o : attribs) { Attribute atr = (Attribute) o; sourceElement.addAttribute(atr.getName(), atr.getValue()); } Document targetDocument = srcEle.getDocument(); // lets start out by just including everything for (Object overlayChild : overlay.elements()) { Element pluckedElement = (Element) overlay.content() .remove(overlay.content().indexOf(overlayChild)); if (pluckedElement.getName().equals("dialog")) { String newOnload = pluckedElement.attributeValue("onload"); if (newOnload != null) { String existingOnload = targetDocument.getRootElement().attributeValue("onload"); String finalOnload = ""; if (existingOnload != null) { finalOnload = existingOnload + ", "; } finalOnload += newOnload; targetDocument.getRootElement().setAttributeValue("onload", finalOnload); } } String insertBefore = pluckedElement.attributeValue("insertbefore"); String insertAfter = pluckedElement.attributeValue("insertafter"); String position = pluckedElement.attributeValue("position"); // determine position to place it int positionToInsert = -1; if (insertBefore != null) { Element insertBeforeTarget = sourceElement.elementByID(insertBefore); positionToInsert = sourceElement.elements().indexOf(insertBeforeTarget); } else if (insertAfter != null) { Element insertAfterTarget = sourceElement.elementByID(insertAfter); positionToInsert = sourceElement.elements().indexOf(insertAfterTarget); if (positionToInsert != -1) { positionToInsert++; // we want to be after that point; } } else if (position != null) { int pos = Integer.parseInt(position); positionToInsert = (pos <= sourceElement.elements().size()) ? pos : -1; } if (positionToInsert == -1) { // default to last positionToInsert = sourceElement.elements().size(); } if (positionToInsert > sourceElement.elements().size()) { sourceElement.elements().add(pluckedElement); } else { sourceElement.elements().add(positionToInsert, pluckedElement); } logger.debug("processed overlay child: " + ((Element) overlayChild).getName() + " : " + pluckedElement.getName()); } } } } private InputStream getInputStreamForSrc(String src) { InputStream in = getResourceAsStream(this.getRootDir() + src); if (in == null) { // try fully qualified name in = getResourceAsStream(src); if (in == null) { File f = new File(src); if (f.exists()) { try { in = new FileInputStream(f); } catch (FileNotFoundException e) { logger.error(e); } } else { logger.error("Cant find overlay source"); } } } return in; } public void processOverlay(String overlaySrc, org.pentaho.ui.xul.dom.Document targetDocument, XulDomContainer container) throws XulException { String baseName = overlaySrc.replace(".xul", ""); ResourceBundle res = loadResourceBundle(baseName); if (res == null) { baseName = (this.getRootDir() + overlaySrc).replace(".xul", ""); res = loadResourceBundle(baseName); if (res == null) { logger.debug("could not find resource bundle, defaulting to main"); res = mainBundle; } else { resourceBundleList.add(res); } } else { resourceBundleList.add(res); } this.processOverlay(overlaySrc, targetDocument, container, res); } public void processOverlay(String overlaySrc, org.pentaho.ui.xul.dom.Document targetDocument, XulDomContainer container, Object resourceBundle) throws XulException { InputStream in = getInputStreamForSrc(overlaySrc); Document doc = null; ResourceBundle res = (ResourceBundle) resourceBundle; String runningTranslatedOutput = getDocFromInputStream(in).asXML(); // TODO IOUtils this if (resourceBundle != null) { try { runningTranslatedOutput = ResourceBundleTranslator.translate(runningTranslatedOutput, res); } catch (IOException e) { logger.error("Error loading resource bundle for overlay: " + overlaySrc, e); } } // check for top-level message bundle and apply it if (this.mainBundle != null) { try { runningTranslatedOutput = ResourceBundleTranslator.translate(runningTranslatedOutput, this.mainBundle); try { SAXReader rdr = new SAXReader(); String upperedIdDoc = this.upperCaseIDAttrs(runningTranslatedOutput.toString()); doc = rdr.read(new StringReader(upperedIdDoc)); } catch (DocumentException e) { logger.error("Error loading XML while applying top level message bundle to overlay file:", e); } } catch (IOException e) { logger.error("Error loading Resource Bundle File to apply to overlay: ", e); } } else { try { SAXReader rdr = new SAXReader(); String upperedIdDoc = this.upperCaseIDAttrs(runningTranslatedOutput.toString()); doc = rdr.read(new StringReader(upperedIdDoc)); } catch (DocumentException e) { logger.error("Error loading XML while applying top level message bundle to overlay file:", e); } } Element overlayRoot = doc.getRootElement(); for (Object child : overlayRoot.elements()) { Element overlay = (Element) child; String overlayId = overlay.attributeValue("ID"); String removeElement = overlay.attributeValue("removeelement"); org.pentaho.ui.xul.dom.Element sourceElement = targetDocument.getElementById(overlayId); if (sourceElement == null) { logger.warn("Cannot overlay element with id [" + overlayId + "] " + "as it does not exist in the target document."); continue; } if (removeElement != null && removeElement.equals("true")) { sourceElement.getParent().removeChild(sourceElement); } parser.setClassLoaders(classloaders); for (Object childToParse : overlay.elements()) { Element childElement = (Element) childToParse; logger.debug("Processing overlay on element with id: " + overlayId); parser.reset(); parser.setContainer(container); XulComponent c = parser.parse(childElement, (XulContainer) sourceElement); String insertBefore = childElement.attributeValue("insertbefore"); String insertAfter = childElement.attributeValue("insertafter"); String position = childElement.attributeValue("position"); XulContainer sourceContainer = ((XulContainer) sourceElement); // determine position to place it int positionToInsert = -1; if (insertBefore != null) { org.pentaho.ui.xul.dom.Element insertBeforeTarget = targetDocument.getElementById(insertBefore); positionToInsert = sourceContainer.getChildNodes().indexOf(insertBeforeTarget); } else if (insertAfter != null) { org.pentaho.ui.xul.dom.Element insertAfterTarget = targetDocument.getElementById(insertAfter); positionToInsert = sourceContainer.getChildNodes().indexOf(insertAfterTarget); if (positionToInsert != -1) { positionToInsert += 1; } } else if (position != null) { int pos = Integer.parseInt(position); positionToInsert = (pos <= sourceContainer.getChildNodes().size()) ? pos : -1; } if (positionToInsert == -1 || positionToInsert == sourceContainer.getChildNodes().size()) { // default to previous behavior sourceContainer.addChild(c); } else { sourceContainer.addChildAt(c, positionToInsert); } notifyOverlayDomReady(c); logger.debug("added child: " + c); } List attribs = overlay.attributes(); // merge in attributes for (Object o : attribs) { Attribute atr = (Attribute) o; try { BeanUtils.setProperty(sourceElement, atr.getName(), atr.getValue()); sourceElement.setAttribute(atr.getName(), atr.getValue()); } catch (InvocationTargetException e) { logger.error(e); } catch (IllegalAccessException e) { logger.error(e); } } } } protected void notifyOverlayDomReady(XulComponent comp) { comp.onDomReady(); for (XulComponent ele : comp.getChildNodes()) { notifyOverlayDomReady(ele); } } public void removeOverlay(String overlaySrc, org.pentaho.ui.xul.dom.Document targetDocument, XulDomContainer container) throws XulException { final Document doc = findDocument(overlaySrc); Element overlayRoot = doc.getRootElement(); for (Object child : overlayRoot.elements()) { Element overlay = (Element) child; for (Object childToParse : overlay.elements()) { String childId = ((Element) childToParse).attributeValue("ID"); org.pentaho.ui.xul.dom.Element prevOverlayedEle = targetDocument.getElementById(childId); if (prevOverlayedEle == null) { logger.debug("Source Element from target document is null: " + childId); continue; } prevOverlayedEle.getParent().removeChild(prevOverlayedEle); } } } private String upperCaseIDAttrs(String src) { String result = src.replace(" id=", " ID="); return result; } public void setOuterContext(Object context) { outerContext = context; } public boolean isRegistered(String elementName) { return this.parser.handlers.containsKey(elementName); } public void registerClassLoader(Object loader) { if (classloaders.contains(loader) == false) { classloaders.add((ClassLoader) loader); } } @Override public void deRegisterClassLoader(Object loader) { if (classloaders.contains(loader)) { classloaders.remove(loader); } } public InputStream getResourceAsStream(String name) { for (ClassLoader loader : classloaders) { InputStream str = loader.getResourceAsStream(name); if (str != null) { return str; } } return null; } public void setSettingsManager(XulSettingsManager settings) { this.settings = settings; } public XulSettingsManager getSettingsManager() { return settings; } }