org.zenonpagetemplates.twoPhasesImpl.ZPTDocumentFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.zenonpagetemplates.twoPhasesImpl.ZPTDocumentFactory.java

Source

package org.zenonpagetemplates.twoPhasesImpl;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

import org.apache.xerces.xni.parser.XMLDocumentFilter;

import org.cyberneko.html.parsers.SAXParser;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.zenonpagetemplates.common.Filter;
import org.zenonpagetemplates.common.exceptions.PageTemplateException;
import org.zenonpagetemplates.twoPhasesImpl.ZPTContext;
import org.zenonpagetemplates.twoPhasesImpl.model.ZPTDocument;
import org.zenonpagetemplates.twoPhasesImpl.model.ZPTElement;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.StaticAttributeImpl;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.I18N.I18NAttributes;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.I18N.I18NContent;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.I18N.I18NDefine;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.I18N.I18NDomain;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.I18N.I18NOnError;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.I18N.I18NParams;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.METAL.METALDefineMacro;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.METAL.METALDefineSlot;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.METAL.METALFillSlot;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.METAL.METALUseMacro;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALAttributes;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALCondition;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALContent;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALDefine;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALOmitTag;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALOnError;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALRepeat;
import org.zenonpagetemplates.twoPhasesImpl.model.attributes.TAL.TALTag;
import org.zenonpagetemplates.twoPhasesImpl.model.content.CDATANode;
import org.zenonpagetemplates.twoPhasesImpl.model.content.TextNode;

/**
 * <p>
 *   Read a ZPT from a file (using JNDI to locate it) or from a 
 *   String instance and returns a ZPTDocument instance.
 * </p>
 * 
 * 
 *  Zenon Page Templates
 *
 *  This library 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 3 of the License, or (at your option) any later version.
 *
 *  This library 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 library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *
 * @author <a href="mailto:chris@christophermrossi.com">Chris Rossi</a>
 * @author <a href="mailto:david.javapagetemplates@gmail.com">David Cana</a>
 * @version $Revision: 1.1 $
 */
public class ZPTDocumentFactory {

    static private ZPTDocumentFactory instance;

    public static final String VOID_STRING = "";
    private static final String ENCODING = "UTF-8";
    public static final String TEMPLATE_ERROR_VAR_NAME = "error";

    private static SAXReader htmlReader = null;

    static final SAXReader getHTMLReader() throws SAXNotRecognizedException, SAXNotSupportedException {

        if (htmlReader == null && ZPTContext.getInstance().isUseHTMLReader()) {
            htmlReader = new SAXReader();
            SAXParser parser = new SAXParser();
            parser.setProperty("http://cyberneko.org/html/properties/names/elems", "match");
            parser.setProperty("http://cyberneko.org/html/properties/names/attrs", "no-change");
            parser.setProperty("http://cyberneko.org/html/properties/default-encoding", ENCODING);
            //parser.setProperty( "http://cyberneko.org/html/features/balance-tags/document-fragment ", "true");
            //parser.setProperty( "http://cyberneko.org/html/features/balance-tags", "false" );
            //parser.setProperty( "http://apache.org/xml/features/scanner/notify-builtin-refs", "true");
            //parser.setProperty( "http://cyberneko.org/html/features/scanner/script/strip-comment-delims", "true"); 

            // Attach the filter to the parser
            XMLDocumentFilter[] filters = { new Filter() };
            parser.setProperty("http://cyberneko.org/html/properties/filters", filters);

            htmlReader.setXMLReader(parser);
        }
        return htmlReader;
    }

    private static SAXReader xmlReader = null;

    static final SAXReader getXMLReader() throws SAXException {
        if (xmlReader == null) {
            xmlReader = new SAXReader();
            xmlReader.setEncoding(ENCODING);
            xmlReader.setIgnoreComments(true);
            xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            //xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        }
        return xmlReader;
    }

    private ZPTDocumentFactory() {
    }

    public static ZPTDocumentFactory getInstance() {

        if (instance == null) {
            instance = new ZPTDocumentFactory();
        }

        return instance;
    }

    public ZPTDocument getZPTDocument(URI uri) throws PageTemplateException {

        try {
            ZPTDocument result = recoverFromCache(uri);

            if (result != null) {
                return result;
            }

            result = getNewZPTDocument(uri);

            saveToCache(uri, result);

            return result;

        } catch (Exception e) {
            throw new PageTemplateException(e);
        }
    }

    public ZPTDocument getZPTDocument(URI uri, String templateString) throws PageTemplateException {

        try {
            ZPTDocument result = recoverFromCache(uri);

            if (result != null) {
                return result;
            }

            result = getNewZPTDocument(uri, templateString);

            saveToCache(uri, result);

            return result;

        } catch (Exception e) {
            throw new PageTemplateException(e);
        }
    }

    static private ZPTDocument recoverFromCache(URI uri) throws PageTemplateException {

        if (!ZPTContext.getInstance().isCacheOn()) {
            return null;
        }

        return ZPTContext.getInstance().getZPTDocumentCache().get(uri);
    }

    static private void saveToCache(URI uri, ZPTDocument zptDocument) throws PageTemplateException {

        if (!ZPTContext.getInstance().isCacheOn()) {
            return;
        }

        ZPTContext.getInstance().getZPTDocumentCache().put(uri, zptDocument);
    }

    private ZPTDocument getNewZPTDocument(URI uri) throws Exception, DocumentException, URISyntaxException,
            SAXException, PageTemplateException, IOException {

        return getNewZPTDocument(readTemplate(uri), uri);
    }

    static private ZPTDocument getNewZPTDocument(URI uri, String templateString)
            throws DocumentException, URISyntaxException, SAXException, PageTemplateException, IOException {

        return getNewZPTDocument(readTemplate(templateString), uri);
    }

    static private Document readTemplate(URI uri) throws DocumentException, SAXException, MalformedURLException {

        URL url = uri.toURL();
        SAXReader reader = getXMLReader();
        try {
            return reader.read(url);

        } catch (DocumentException e) {
            try {
                reader = getHTMLReader();
                if (reader == null) {
                    throw (e);
                }
                return reader.read(url);

            } catch (NoClassDefFoundError ee) {
                // Allow user to omit nekohtml package
                // to disable html parsing
                //System.err.println( "Warning: no nekohtml" );
                //ee.printStackTrace();
                throw e;
            }
        }

    }

    static private Document readTemplate(String stringTemplate) throws DocumentException, SAXException {

        SAXReader reader = getXMLReader();
        StringReader stringReader = new StringReader(stringTemplate);
        try {
            return reader.read(stringReader);

        } catch (DocumentException e) {
            try {
                reader = getHTMLReader();
                if (reader == null) {
                    throw (e);
                }
                return reader.read(stringReader);

            } catch (NoClassDefFoundError ee) {
                // Allow user to omit nekohtml package
                // to disable html parsing
                //System.err.println( "Warning: no nekohtml" );
                //ee.printStackTrace();
                throw e;
            }
        }

    }

    static private ZPTDocument getNewZPTDocument(Document template, URI uri)
            throws SAXException, PageTemplateException, IOException {

        try {
            ZPTDocument result = new ZPTDocument(uri);

            // Set up template name and doc type in zptDocument
            result.setTemplateName(template.getName());
            result.setDocType(DocType.generateDocTypeFromDOM4jDocument(template));

            // Process root element and set it to zptDocument
            result.setRoot(getNewZPTElement(template.getRootElement(), result, new Stack<Map<String, Slot>>()));

            return result;

        } catch (Exception e) {
            throw new PageTemplateException(e);
        }
    }

    static protected ZPTElement getNewZPTElement(Element element, ZPTDocument zptDocument,
            Stack<Map<String, Slot>> slotStack) throws SAXException, PageTemplateException, IOException {

        // Create a new zptElement and set its name
        ZPTElement result = new ZPTElement(element.getName(), element.getNamespace().getPrefix());

        // Map first namespaces to make all namespaces prefixes available
        mapNamespaces(element, result, zptDocument);

        // Get attributes
        mapAttributes(element, result, zptDocument);

        // Continue processing if it is not define tal:content or i18n:content
        if (processContentIsOn(result, zptDocument)) {
            mapContent(element, result, zptDocument, slotStack);
        }

        return result;
    }

    private static boolean processContentIsOn(ZPTElement zptElement, ZPTDocument zptDocument) {

        return !zptElement.existsTALAttribute(TwoPhasesPageTemplate.TAL_CONTENT, zptDocument)
                && !zptElement.existsI18nAttribute(TwoPhasesPageTemplate.I18N_CONTENT, zptDocument);
    }

    @SuppressWarnings({ "unchecked" })
    static private void mapContent(Element element, ZPTElement zptElement, ZPTDocument zptDocument,
            Stack<Map<String, Slot>> slotStack) throws SAXException, PageTemplateException, IOException {

        // Use default template content
        for (Iterator<Node> i = element.nodeIterator(); i.hasNext();) {
            Node node = i.next();
            switch (node.getNodeType()) {
            case Node.ELEMENT_NODE:
                zptElement.addContent(getNewZPTElement((Element) node, zptDocument, slotStack));
                break;

            case Node.TEXT_NODE:
                zptElement.addContent(new TextNode(node.getText()));
                break;

            case Node.CDATA_SECTION_NODE:
                zptElement.addContent(new CDATANode(node.getText()));
                break;

            case Node.NAMESPACE_NODE: // Already handled
                /*
                 Namespace declared = (Namespace)node;
                 if (zptDocument.isNamespaceToDeclare(declared)){
                    zptDocument.addNamespace(declared);
                 } else {
                    zptElement.addNamespaceStaticAttribute(declared);
                 }
                 break;*/

            case Node.ATTRIBUTE_NODE: // Already handled
            case Node.COMMENT_NODE: // Remove all comments
            case Node.DOCUMENT_TYPE_NODE:
            case Node.ENTITY_REFERENCE_NODE:
            case Node.PROCESSING_INSTRUCTION_NODE:
            default:
                // Nothing to do
            }
        }
    }

    @SuppressWarnings("unchecked")
    static private void mapNamespaces(Element element, ZPTElement zptElement, ZPTDocument zptDocument)
            throws PageTemplateException {

        for (Iterator<Attribute> i = element.nodeIterator(); i.hasNext();) {
            Node node = i.next();
            if (node.getNodeType() == Node.NAMESPACE_NODE) {
                Namespace declared = (Namespace) node;
                if (zptDocument.isNamespaceToDeclare(declared)) {
                    zptDocument.addNamespace(declared);
                } else {
                    zptElement.addNamespaceStaticAttribute(declared);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    static private void mapAttributes(Element element, ZPTElement zptElement, ZPTDocument zptDocument)
            throws PageTemplateException {

        String talOmitTag = null;
        boolean isReplace = false;

        for (Iterator<Attribute> i = element.attributeIterator(); i.hasNext();) {
            Attribute attribute = i.next();
            Namespace namespace = attribute.getNamespace();
            String name = attribute.getName();
            String namespacePrefix = namespace.getPrefix();

            // Handle ZPT attributes
            if (TwoPhasesPageTemplate.TAL_NAMESPACE_URI.equals(namespace.getURI())) {

                // tal:define
                if (name.equals(TwoPhasesPageTemplate.TAL_DEFINE)) {
                    zptElement.addDynamicAttribute(new TALDefine(namespacePrefix, attribute.getValue()));
                }

                // tal:condition
                else if (name.equals(TwoPhasesPageTemplate.TAL_CONDITION)) {
                    zptElement.addDynamicAttribute(new TALCondition(namespacePrefix, attribute.getValue()));
                }

                // tal:repeat
                else if (name.equals(TwoPhasesPageTemplate.TAL_REPEAT)) {
                    zptElement.addDynamicAttribute(new TALRepeat(namespacePrefix, attribute.getValue()));
                }

                // tal:content
                else if (name.equals(TwoPhasesPageTemplate.TAL_CONTENT)) {
                    zptElement.addDynamicAttribute(new TALContent(namespacePrefix, attribute.getValue()));
                }

                // tal:replace
                else if (name.equals(TwoPhasesPageTemplate.TAL_REPLACE)) {
                    isReplace = true;
                    zptElement.addDynamicAttribute(new TALContent(namespacePrefix, attribute.getValue()));
                }

                // tal:attributes
                else if (name.equals(TwoPhasesPageTemplate.TAL_ATTRIBUTES)) {
                    zptElement.addDynamicAttribute(new TALAttributes(namespacePrefix, attribute.getValue()));
                }

                // tal:omit-tag
                else if (name.equals(TwoPhasesPageTemplate.TAL_OMIT_TAG)) {
                    talOmitTag = attribute.getValue();
                }

                // tal:on-error
                else if (name.equals(TwoPhasesPageTemplate.TAL_ON_ERROR)) {
                    zptElement.addDynamicAttribute(new TALOnError(namespacePrefix, attribute.getValue()));
                }

                // tal:tag
                else if (name.equals(TwoPhasesPageTemplate.TAL_TAG)) {
                    zptElement.addDynamicAttribute(new TALTag(namespacePrefix, attribute.getValue()));
                }

                // error
                else {
                    throw new PageTemplateException("Unknown TAL attribute: " + name + " in template");
                }
            }

            else if (TwoPhasesPageTemplate.METAL_NAMESPACE_URI.equals(namespace.getURI())) {

                // metal:use-macro
                if (name.equals(TwoPhasesPageTemplate.METAL_USE_MACRO)) {
                    zptElement.addDynamicAttribute(new METALUseMacro(namespacePrefix, attribute.getValue()));
                }

                // metal:define-slot
                else if (name.equals(TwoPhasesPageTemplate.METAL_DEFINE_SLOT)) {
                    zptElement.addDynamicAttribute(new METALDefineSlot(namespacePrefix, attribute.getValue()));
                }

                // metal:define-macro
                else if (name.equals(TwoPhasesPageTemplate.METAL_DEFINE_MACRO)) {
                    zptElement.addDynamicAttribute(new METALDefineMacro(namespacePrefix, attribute.getValue()));
                }

                // metal:fill-slot
                else if (name.equals(TwoPhasesPageTemplate.METAL_FILL_SLOT)) {
                    zptElement.addDynamicAttribute(new METALFillSlot(namespacePrefix, attribute.getValue()));
                }

                // error
                else {
                    throw new PageTemplateException("Unknown metal attribute: " + name + " in template");
                }
            }

            else if (TwoPhasesPageTemplate.I18N_NAMESPACE_URI.equals(namespace.getURI())) {

                // i18n:domain
                if (name.equals(TwoPhasesPageTemplate.I18N_DOMAIN)) {
                    zptElement.addDynamicAttribute(new I18NDomain(namespacePrefix, attribute.getValue()));
                }

                // i18n:define
                else if (name.equals(TwoPhasesPageTemplate.I18N_DEFINE)) {
                    zptElement.addDynamicAttribute(new I18NDefine(namespacePrefix, attribute.getValue()));
                }

                // i18n:content
                else if (name.equals(TwoPhasesPageTemplate.I18N_CONTENT)) {
                    zptElement.addDynamicAttribute(new I18NContent(namespacePrefix, attribute.getValue()));
                }

                // i18n:replace
                else if (name.equals(TwoPhasesPageTemplate.I18N_REPLACE)) {
                    isReplace = true;
                    zptElement.addDynamicAttribute(new I18NContent(namespacePrefix, attribute.getValue()));
                }

                // i18n:attributes
                else if (name.equals(TwoPhasesPageTemplate.I18N_ATTRIBUTES)) {
                    zptElement.addDynamicAttribute(new I18NAttributes(namespacePrefix, attribute.getValue()));
                }

                // i18n:params
                else if (name.equals(TwoPhasesPageTemplate.I18N_PARAMS)) {
                    zptElement.addDynamicAttribute(new I18NParams(namespacePrefix, attribute.getValue()));
                }

                // i18n:on-error
                else if (name.equals(TwoPhasesPageTemplate.I18N_ON_ERROR)) {
                    zptElement.addDynamicAttribute(new I18NOnError(namespacePrefix, attribute.getValue()));
                }

                // error
                else {
                    throw new PageTemplateException("Unknown i18n attribute: " + name + " in template");
                }
            }

            // Pass on all other attributes
            else {
                zptElement.addStaticAttribute(new StaticAttributeImpl(namespacePrefix, name, attribute.getValue()));
            }
        }

        //  Add omit-tag
        if (talOmitTag != null) {
            zptElement.addDynamicAttribute(new TALOmitTag(zptDocument.getTALPrefix(), talOmitTag));

        } else if (isReplace) {
            zptElement.addDynamicAttribute(new TALOmitTag(zptDocument.getTALPrefix(), VOID_STRING));
        }
    }

}