net.unicon.warlock.fac.xml.XmlWarlockFactory.java Source code

Java tutorial

Introduction

Here is the source code for net.unicon.warlock.fac.xml.XmlWarlockFactory.java

Source

/*
 * Copyright (C) 2007 Unicon, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this distribution.  It is also available here:
 * http://www.fsf.org/licensing/licenses/gpl.html
 *
 * As a special exception to the terms and conditions of version
 * 2 of the GPL, you may redistribute this Program in connection
 * with Free/Libre and Open Source Software ("FLOSS") applications
 * as described in the GPL FLOSS exception.  You should have received
 * a copy of the text describing the FLOSS exception along with this
 * distribution.
 */

package net.unicon.warlock.fac.xml;

import java.io.Writer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import net.unicon.penelope.IChoiceCollection;
import net.unicon.penelope.IChoiceCollectionParser;
import net.unicon.penelope.IDecisionCollection;
import net.unicon.penelope.IEntityStore;
import net.unicon.penelope.store.jvm.JvmEntityStore;
import net.unicon.warlock.IRenderingEngine;
import net.unicon.warlock.IScreen;
import net.unicon.warlock.IStateQuery;
import net.unicon.warlock.WarlockException;
import net.unicon.warlock.XmlFormatException;
import net.unicon.warlock.fac.AbstractWarlockFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xalan.serialize.Serializer;
import org.apache.xml.serializer.Method;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.dom.DOMNodeHelper;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

public final class XmlWarlockFactory extends AbstractWarlockFactory {

    private static final Object systemLock = new Object();
    private static final Log log = LogFactory.getLog(XmlWarlockFactory.class);

    private static final String xsltDefaultTransformer = TransformerFactory.newInstance().getClass().getName();

    private static Properties serializerProperties = null;
    private static Properties xmlSerializerProperties = null;

    // Instance Members.
    private final Source transSource;
    private final IRenderingEngine engine;
    private final IEntityStore store;
    private SAXTransformerFactory transformerFactory = null;
    private Templates templates = null;

    private boolean useXsltc = false;
    private boolean xsltcDebug = false;
    private String xsltcPackageName = "translets";
    private boolean xsltcGenerateTranslet = false;
    private boolean xsltcAutoTranslet = false;
    private boolean xsltcUseClasspath = false;

    private boolean cacheTemplates = true;

    /*
     * Public API.
     */

    public XmlWarlockFactory(Source transSource, boolean cacheTemplates) {
        // Assertions.
        if (transSource == null) {
            String msg = "Arg:wument 'transSource' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        // Instance Members.
        this.transSource = transSource;
        this.engine = new RenderingEngineImpl(this);
        this.store = new JvmEntityStore();
        this.cacheTemplates = cacheTemplates;

        this.useXsltc = false;
        initializeDefaultTransformerFactory();
        initializeSerializer();
    }

    public XmlWarlockFactory(Source transSource, String transformerImplemention, boolean debug, String packageName,
            boolean generateTranslet, boolean autoTranslet, boolean useClasspath, boolean cacheTemplates) {

        // Assertions.
        if (transSource == null) {
            String msg = "Arg:wument 'transSource' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        /*
        try {
        Transformer emptyt=TransformerFactory.newInstance().newTransformer();
        StringWriter sw = new StringWriter();
        emptyt.transform(transSource, new StreamResult(sw));
        System.out.println("ZZZ XmlWarlockFactory XSL SOURCE:\n" + sw.toString());
        } catch (Exception e) {
        e.printStackTrace();
        }
        */

        // Instance Members.
        this.transSource = transSource;
        this.engine = new RenderingEngineImpl(this);
        this.store = new JvmEntityStore();

        this.cacheTemplates = cacheTemplates;

        this.useXsltc = true;
        initializeXsltcTransformerFactory(transformerImplemention, debug, packageName, generateTranslet,
                autoTranslet, useClasspath);
        initializeSerializer();
    }

    private void initializeSerializer() {
        if (serializerProperties == null) {
            synchronized (systemLock) {
                if (serializerProperties == null) {
                    serializerProperties = new Properties();
                    serializerProperties.put(OutputKeys.METHOD, Method.HTML);
                }
                if (xmlSerializerProperties == null) {
                    xmlSerializerProperties = new Properties();
                    xmlSerializerProperties.put(OutputKeys.METHOD, Method.XML);
                }
            }
        }
    }

    private Templates getTemplates() {
        if (templates != null) {
            return templates;
        }

        try {
            if (useXsltc) {
                // cache the precompiled templates object
                transformerFactory.setAttribute("debug", Boolean.toString(xsltcDebug));
                transformerFactory.setAttribute("package-name", xsltcPackageName);
                transformerFactory.setAttribute("generate-translet", Boolean.toString(xsltcGenerateTranslet));
                transformerFactory.setAttribute("auto-translet", Boolean.toString(xsltcAutoTranslet));
                transformerFactory.setAttribute("use-classpath", Boolean.toString(xsltcUseClasspath));
            }
            Templates tmpls = transformerFactory.newTemplates(transSource);
            if (cacheTemplates) {
                templates = tmpls;
            }
            return tmpls;
        } catch (TransformerConfigurationException tce) {
            throw new RuntimeException("Failed caching templates.", tce);
        }
    }

    private void initializeDefaultTransformerFactory() {
        synchronized (systemLock) {
            this.transformerFactory = (SAXTransformerFactory) TransformerFactory.newInstance();
            log.info("Warlock using transformer - " + transformerFactory.getClass().getName());
        }
    }

    private void initializeXsltcTransformerFactory(String transformerImplemention, boolean debug,
            String packageName, boolean generateTranslet, boolean autoTranslet, boolean useClasspath) {

        synchronized (systemLock) {
            System.setProperty("javax.xml.transform.TransformerFactory", transformerImplemention);
            this.transformerFactory = (SAXTransformerFactory) TransformerFactory.newInstance();
            this.xsltcDebug = debug;
            this.xsltcPackageName = packageName;
            this.xsltcGenerateTranslet = generateTranslet;
            this.xsltcAutoTranslet = autoTranslet;
            this.xsltcUseClasspath = useClasspath;

            log.info("Warlock using transformer - " + transformerFactory.getClass().getName());
            log.info("Setting auto-translet to " + autoTranslet);
            log.info("Setting generate-translet to " + generateTranslet);
            log.info("Setting use-classpath to " + useClasspath);

            // restore the default implementation
            System.setProperty("javax.xml.transform.TransformerFactory", xsltDefaultTransformer);

            log.info("Warlock restored transformer - " + TransformerFactory.newInstance().getClass().getName());

            // cache the source as a precompile templates object 
        }
    }

    public IRenderingEngine getRenderingEngine() {
        return engine;
    }

    public IScreen parseScreen(Element e) throws WarlockException {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Element]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        return parseScreen(e, new Element[0]);

    }

    public IScreen parseScreen(Element e, Element[] reference) throws WarlockException {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Element]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        if (reference == null) {
            String msg = "Argument 'reference' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        // Resolve the References.
        List ref = e.selectNodes("//include");
        Iterator it = ref.iterator();
        while (it.hasNext()) {

            Element incl = (Element) it.next();

            // Read the library handle.
            Attribute h = incl.attribute("handle");
            if (h == null) {
                String msg = "Element <include> is missing required attribute " + "'handle'.";
                throw new XmlFormatException(msg);
            }
            String handle = h.getValue();

            // Read the xpath expression.
            Attribute x = incl.attribute("xpath");
            if (x == null) {
                String msg = "Element <include> is missing required attribute " + "'xpath'.";
                throw new XmlFormatException(msg);
            }
            String xpath = x.getValue();

            // Find the relevent library.
            Element lib = null;
            for (int i = 0; i < reference.length; i++) {
                if (reference[i].valueOf("@handle").equals(handle)) {
                    lib = reference[i];
                    break;
                }
            }
            if (lib == null) {
                String msg = "Unable to locate the specified reference " + "library:  " + handle;
                throw new WarlockException(msg);
            }

            // Replace the node(s).
            org.w3c.dom.Element dIncl = DOMNodeHelper.asDOMElement(incl);
            Iterator targets = lib.selectNodes(xpath).iterator();
            while (targets.hasNext()) {
                Element elm = (Element) targets.next();
                org.w3c.dom.Element dElm = DOMNodeHelper.asDOMElement(elm);
                org.w3c.dom.Element dImp = (org.w3c.dom.Element) dIncl.getOwnerDocument().importNode(dElm, true);
                dIncl.getParentNode().insertBefore(dImp, dIncl);
            }
            dIncl.getParentNode().removeChild(dIncl);

        }

        return AbstractWarlockFactory.ScreenImpl.parse(e, this);

    }

    /*
     * Implementation.
     */

    private synchronized TransformerHandler getTransformerHandler() throws WarlockException {
        TransformerHandler rslt = null;

        // Create a new one.
        synchronized (systemLock) {
            try {
                if (useXsltc) {
                    transformerFactory.setAttribute("debug", Boolean.toString(xsltcDebug));
                    transformerFactory.setAttribute("package-name", xsltcPackageName);
                    transformerFactory.setAttribute("generate-translet", Boolean.toString(xsltcGenerateTranslet));
                    transformerFactory.setAttribute("auto-translet", Boolean.toString(xsltcAutoTranslet));
                    transformerFactory.setAttribute("use-classpath", Boolean.toString(xsltcUseClasspath));
                }
                rslt = transformerFactory.newTransformerHandler(getTemplates());
            } catch (Throwable t) {
                String msg = "Unable to create a new transformer instance.";
                throw new WarlockException(msg, t);
            }
        }

        return rslt;
    }

    private IEntityStore getEntityStore() {
        return store;
    }

    /*
     * Nested Types.
     */

    private static final class RenderingEngineImpl implements IRenderingEngine {

        // Instance Members.
        private final XmlWarlockFactory owner;

        /*
         * Public API.
         */

        public RenderingEngineImpl(XmlWarlockFactory owner) {

            // Assertions.
            if (owner == null) {
                String msg = "Argument 'owner' cannot be null.";
                throw new IllegalArgumentException(msg);
            }

            // Instance Members.
            this.owner = owner;

        }

        public IChoiceCollection[] render(IScreen screen, IStateQuery query, Map params, Writer writer)
                throws WarlockException {

            // Assertions.
            if (screen == null) {
                String msg = "Argument 'screen' cannot be null.";
                throw new IllegalArgumentException(msg);
            }
            if (query == null) {
                String msg = "Argument 'query' cannot be null.";
                throw new IllegalArgumentException(msg);
            }
            if (params == null) {
                String msg = "Argument 'params' cannot be null.";
                throw new IllegalArgumentException(msg);
            }

            final String screenText = "screen";
            final String decisionsText = "decisions";
            final String emptyText = "";

            IChoiceCollection[] choices = null;

            try {
                // Evaluate the screen.

                AttributesImpl attributes = new AttributesImpl();

                // Transform to browser markup.
                TransformerHandler th = owner.getTransformerHandler();
                Transformer trans = th.getTransformer();
                th.setResult(new StreamResult(writer));

                Iterator it = params.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    trans.setParameter(key, params.get(key));
                }
                if (log.isDebugEnabled()) {
                    log.debug("Transforming for evaluated screen: " + screen.getHandle().getValue());
                }

                // Prepare choice collection(s).
                IChoiceCollectionParser ccParser = owner.getEntityStore().getChoiceCollectionParser(th);

                ccParser.startDocument();

                ccParser.startElement(emptyText, screenText, screenText, attributes);
                if (log.isDebugEnabled()) {
                    log.debug("Evaluating IStateQuery: " + query.getClass().getName());
                }
                screen.evaluate(query, ccParser);

                ccParser.startElement(emptyText, decisionsText, decisionsText, attributes);
                it = Arrays.asList(query.getDecisions()).iterator();
                while (it.hasNext()) {
                    IDecisionCollection c = (IDecisionCollection) it.next();
                    c.sendXmlEvents(ccParser);
                }
                ccParser.endElement(emptyText, decisionsText, decisionsText);
                ccParser.endElement(emptyText, screenText, screenText);
                ccParser.endDocument();

                choices = ccParser.getChoiceCollections();

            } catch (SAXException se) {
                throw new WarlockException(
                        "Rendering engine failed to build the specified screen: " + screen.getHandle().getValue(),
                        se);
            }

            return choices;
        }

        public class TestHandler implements ContentHandler {

            private ContentHandler handler = null;

            public TestHandler(ContentHandler handler) {
                this.handler = handler;
            }

            public void endDocument() throws SAXException {
                System.out.println("endDocument");
                handler.endDocument();
            }

            public void startDocument() throws SAXException {
                System.out.println("startDocument");
                handler.startDocument();
            }

            public void characters(char[] ch, int start, int length) throws SAXException {
                System.out.println("characters: " + new String(ch, start, length));
                handler.characters(ch, start, length);
            }

            public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
                System.out.println("ignorableWhitespace: " + new String(ch, start, length));
                handler.ignorableWhitespace(ch, start, length);
            }

            public void endPrefixMapping(String prefix) throws SAXException {
                System.out.println("endPrefixMapping: " + prefix);
                handler.endPrefixMapping(prefix);
            }

            public void skippedEntity(String name) throws SAXException {
                System.out.println("skippedEntity: " + name);
                handler.skippedEntity(name);
            }

            public void setDocumentLocator(Locator locator) {
                System.out.println("setDocumentLocator: " + locator);
                System.out.println("columnNumber: " + locator.getColumnNumber());
                System.out.println("lineNumber: " + locator.getLineNumber());
                System.out.println("publicId: " + locator.getPublicId());
                System.out.println("systemId: " + locator.getSystemId());
                handler.setDocumentLocator(locator);
            }

            public void processingInstruction(String target, String data) throws SAXException {
                System.out.println("processingInstruction: " + target + ", " + data);
                handler.processingInstruction(target, data);
            }

            public void startPrefixMapping(String prefix, String uri) throws SAXException {
                System.out.println("startPrefixMapping: " + prefix + ", " + uri);
                handler.startPrefixMapping(prefix, uri);
            }

            public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
                System.out.println("endElement: " + namespaceURI + ", " + localName + ", " + qName);
                handler.endElement(namespaceURI, localName, qName);
            }

            public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
                    throws SAXException {
                System.out.println("startElement: " + namespaceURI + ", " + localName + ", " + qName + ", " + atts);
                handler.startElement(namespaceURI, localName, qName, atts);
            }

        }
    }

    public class TransformerObjects {
        private TransformerHandler th = null;
        private Serializer ser = null;

        public TransformerObjects(TransformerHandler th, Serializer ser) {
            this.th = th;
            this.ser = ser;
        }

        public TransformerHandler getTransformerHandler() {
            return th;
        }

        public Serializer getSerializer() {
            return ser;
        }
    }
}