Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2014 Attila Szegedi, Daniel Dekany, Jonathan Revusky
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   * http://www.apache.org/licenses/LICENSE-2.0
   * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
  
 package freemarker.ext.dom;
 
 
 import java.io.File;
 import java.util.List;
 import java.util.Map;
 
 
 
A base class for wrapping a W3C DOM Node as a FreeMarker template model.

Note that freemarker.template.DefaultObjectWrapper automatically wraps W3C DOM org.w3c.dom.Node-s into this, so you may not need to do that with this class manually. Though, before dropping the org.w3c.dom.Node-s into the data-model, you may want to apply simplify(org.w3c.dom.Node) on them.

 
 abstract public class NodeModel
 {
 
     static private final Logger LOG = Logger.getLogger("freemarker.dom");
 
     private static final Object STATIC_LOCK = new Object();
     
     static private DocumentBuilderFactory docBuilderFactory;
     
     static private final Map xpathSupportMap = Collections.synchronizedMap(new WeakHashMap());
     
     static private XPathSupport jaxenXPathSupport;
     
     static private ErrorHandler errorHandler;
     
     static Class xpathSupportClass;
     
     static {
         try {
             useDefaultXPathSupport();
         } catch (Exception e) {
             // do nothing
         }
         if ( == null && .isWarnEnabled()) {
             .warn("No XPath support is available.");
         }
     }
    
    
The W3C DOM Node being wrapped.
    final Node node;
    private NodeModel parent;
    
    
Sets the DOM Parser implementation to be used when building NodeModel objects from XML files.
    static public void setDocumentBuilderFactory(DocumentBuilderFactory docBuilderFactory) {
        synchronized () {
            . = docBuilderFactory;
        }
    }
    
    

Returns:
the DOM Parser implementation that is used when building NodeModel objects from XML files.
        synchronized () {
            if ( == null) {
                DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
                newFactory.setNamespaceAware(true);
                newFactory.setIgnoringElementContentWhitespace(true);
                 = newFactory;  // We only write it out when the initialization was full 
            }
            return ;
        }
    }
    
    
sets the error handler to use when parsing the document.
    static public void setErrorHandler(ErrorHandler errorHandler) {
        synchronized () {
            . = errorHandler;
        }
    }

    

Since:
2.3.20
    static public ErrorHandler getErrorHandler() {
        synchronized () {
            return .;
        }
    }
    
    
Create a NodeModel from a SAX input source. Adjacent text nodes will be merged (and CDATA sections are considered as text nodes).

Parameters:
removeComments whether to remove all comment nodes (recursively) from the tree before processing
removePIs whether to remove all processing instruction nodes (recursively from the tree before processing
    static public NodeModel parse(InputSource isboolean removeCommentsboolean removePIs)
    {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != nullbuilder.setErrorHandler(errorHandler);
        final Document doc;
        try {
        	doc = builder.parse(is);
        } catch (MalformedURLException e) {
    		// This typical error has an error message that is hard to understand, so let's translate it:
        	if (is.getSystemId() == null && is.getCharacterStream() == null && is.getByteStream() == null) {
        		throw new MalformedURLException(
        				"The SAX InputSource has systemId == null && characterStream == null && byteStream == null. "
        				+ "This is often because it was created with a null InputStream or Reader, which is often because "
        				+ "the XML file it should point to was not found. "
        				+ "(The original exception was: " + e + ")");
        	} else {
        		throw e;
        	}
        }
        if (removeComments && removePIs) {
            simplify(doc);
        } else {
            if (removeComments) {
                removeComments(doc);
            }
            if (removePIs) {
                removePIs(doc);
            }
            mergeAdjacentText(doc);
        }
        return wrap(doc);
    }
    
    
Create a NodeModel from an XML input source. By default, all comments and processing instruction nodes are stripped from the tree.
    static public NodeModel parse(InputSource is
        return parse(istruetrue);
    }
    
    
    
Create a NodeModel from an XML file.

Parameters:
removeComments whether to remove all comment nodes (recursively) from the tree before processing
removePIs whether to remove all processing instruction nodes (recursively from the tree before processing
    static public NodeModel parse(File fboolean removeCommentsboolean removePIs
    {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != nullbuilder.setErrorHandler(errorHandler);
        Document doc = builder.parse(f);
        if (removeComments) {
            removeComments(doc);
        }
        if (removePIs) {
            removePIs(doc);
        }
        mergeAdjacentText(doc);
        return wrap(doc);
    }
    
    
Create a NodeModel from an XML file. By default, all comments and processing instruction nodes are stripped from the tree.
    static public NodeModel parse(File f
        return parse(ftruetrue);
    }
    
    protected NodeModel(Node node) {
        this. = node;
    }
    
    

Returns:
the underling W3C DOM Node object that this TemplateNodeModel is wrapping.
    public Node getNode() {
        return ;
    }
    
    public TemplateModel get(String keythrows TemplateModelException {
        if (key.startsWith("@@")) {
            if (key.equals("@@text")) {
                return new SimpleScalar(getText());
            }
            if (key.equals("@@namespace")) {
                String nsURI = .getNamespaceURI();
                return nsURI == null ? null : new SimpleScalar(nsURI);
            }
            if (key.equals("@@local_name")) {
                String localName = .getLocalName();
                if (localName == null) {
                    localName = getNodeName();
                }
                return new SimpleScalar(localName);
            }
            if (key.equals("@@markup")) {
                StringBuffer buf = new StringBuffer();
                NodeOutputter nu = new NodeOutputter();
                nu.outputContent(buf);
                return new SimpleScalar(buf.toString());
            }
            if (key.equals("@@nested_markup")) {
                StringBuffer buf = new StringBuffer();
                NodeOutputter nu = new NodeOutputter();
                nu.outputContent(.getChildNodes(), buf);
                return new SimpleScalar(buf.toString());
            }
            if (key.equals("@@qname")) {
                String qname = getQualifiedName();
                return qname == null ? null : new SimpleScalar(qname);
            }
        }
        XPathSupport xps = getXPathSupport();
        if (xps != null) {
            return xps.executeQuery(key);
        } else {
            throw new TemplateModelException(
                    "Can't try to resolve the XML query key, because no XPath support is available. "
                    + "It's either malformed or an XPath expression: " + key);
        }
    }
    
    public TemplateNodeModel getParentNode() {
        if ( == null) {
            Node parentNode = .getParentNode();
            if (parentNode == null) {
                if ( instanceof Attr) {
                    parentNode = ((Attr).getOwnerElement();
                }
            }
             = wrap(parentNode);
        }
        return ;
    }
    
        if ( == null) {
             = new NodeListModel(.getChildNodes(), this);
        }
        return ;
    }
    
    public final String getNodeType() throws TemplateModelException {
        short nodeType = .getNodeType();
        switch (nodeType) {
            case . : return "attribute";
            case . : return "text";
            case . : return "comment";
            case . : return "document_fragment";
            case . : return "document";
            case . : return "document_type";
            case . : return "element";
            case . : return "entity";
            case . : return "entity_reference";
            case . : return "notation";
            case . : return "pi";
            case . : return "text";
        }
        throw new TemplateModelException("Unknown node type: " + nodeType + ". This should be impossible!");
    }
    
    public TemplateModel exec(List argsthrows TemplateModelException {
        if (args.size() != 1) {
            throw new TemplateModelException("Expecting exactly one arguments");
        }
        String query = (Stringargs.get(0);
        // Now, we try to behave as if this is an XPath expression
        XPathSupport xps = getXPathSupport();
        if (xps == null) {
            throw new TemplateModelException("No XPath support available");
        }
        return xps.executeQuery(query);
    }
    
    public final int size() {return 1;}
    
    public final TemplateModel get(int i) {
        return i==0 ? this : null;
    }
    
    public String getNodeNamespace() {
        int nodeType = .getNodeType();
        if (nodeType != . && nodeType != .) { 
            return null;
        }
        String result = .getNamespaceURI();
        if (result == null && nodeType == .) {
            result = "";
        } else if ("".equals(result) && nodeType == .) {
            result = null;
        }
        return result;
    }
    
    public final int hashCode() {
        return .hashCode();
    }
    
    public boolean equals(Object other) {
        if (other == nullreturn false;
        return other.getClass() == this.getClass() 
                && ((NodeModelother)..equals(this.);
    }
    
    static public NodeModel wrap(Node node) {
        if (node == null) {
            return null;
        }
        NodeModel result = null;
        switch (node.getNodeType()) {
            case . : result = new DocumentModel((Documentnode); break;
            case . : result = new ElementModel((Elementnode); break;
            case . : result = new AttributeNodeModel((Attrnode); break;
            case . : 
            case . :
            case . : result = new CharacterDataNodeModel((org.w3c.dom.CharacterDatanode); break;
            case . : result = new PINodeModel((ProcessingInstructionnode); break;
            case . : result = new DocumentTypeModel((DocumentTypenode); break;
        }
        return result;
    }
    
    
Recursively removes all comment nodes from the subtree.

    static public void removeComments(Node node) {
        NodeList children = node.getChildNodes();
        int i = 0;
        int len = children.getLength();
        while (i < len) {
            Node child = children.item(i);
            if (child.hasChildNodes()) {
                removeComments(child);
                i++;
            } else {
                if (child.getNodeType() == .) {
                    node.removeChild(child);
                    len--;
                } else {
                    i++;
                }
            }
        }
    }
    
    
Recursively removes all processing instruction nodes from the subtree.

    static public void removePIs(Node node) {
        NodeList children = node.getChildNodes();
        int i = 0;
        int len = children.getLength();
        while (i < len) {
            Node child = children.item(i);
            if (child.hasChildNodes()) {
                removePIs(child);
                i++;
            } else {
                if (child.getNodeType() == .) {
                    node.removeChild(child);
                    len--;
                } else {
                    i++;
                }
            }
        }
    }
    
    
Merges adjacent text/cdata nodes, so that there are no adjacent text/cdata nodes. Operates recursively on the entire subtree. You thus lose information about any CDATA sections occurring in the doc.

    static public void mergeAdjacentText(Node node) {
        Node child = node.getFirstChild();
        while (child != null) {
            if (child instanceof Text || child instanceof CDATASection) {
                Node next = child.getNextSibling();
                if (next instanceof Text || next instanceof CDATASection) {
                    String fullText = child.getNodeValue() + next.getNodeValue();
                    ((CharacterDatachild).setData(fullText);
                    node.removeChild(next);
                }
            }
            else {
                mergeAdjacentText(child);
            }
            child = child.getNextSibling();
        }
    }
    
    
Removes comments and processing instruction, and then unites adjacent text nodes. Note that CDATA sections count as text nodes.
    
    static public void simplify(Node node) {
        NodeList children = node.getChildNodes();
        int i = 0;
        int len = children.getLength();
        Node prevTextChild = null;
        while (i < len) {
            Node child = children.item(i);
            if (child.hasChildNodes()) {
                simplify(child);
                prevTextChild = null;
                i++;
            } else {
                int type = child.getNodeType();
                if (type == .) {
                    node.removeChild(child);
                    len--;
                } else if (type == .) {
                    node.removeChild(child);
                    len--;
                } else if (type == . || type == . ) {
                    if (prevTextChild != null) {
                        CharacterData ptc = (CharacterDataprevTextChild;
                        ptc.setData(ptc.getNodeValue() + child.getNodeValue());
                        node.removeChild(child);
                        len--;
                    } else {
                        prevTextChild = child;
                        i++;
                    }
                } else {
                    prevTextChild = null;
                    i++;
                }
            }
        }
    }
    
        if ( instanceof Document) {
            return this;
        }
        else {
            return wrap(.getOwnerDocument());
        }
    }

    
Tells the system to use (restore) the default (initial) XPath system used by this FreeMarker version on this system.
    static public void useDefaultXPathSupport() {
        synchronized () {
             = null;
             = null;
            try {
                useXalanXPathSupport();
            } catch (Exception e) {
                ; // ignore
            }
            if ( == nulltry {
            	useSunInternalXPathSupport();
            } catch (Exception e) {
            	; // ignore
            }
            if ( == nulltry {
                useJaxenXPathSupport();
            } catch (Exception e) {
                ; // ignore
            }
        }
    }
    
    
Convenience method. Tells the system to use Jaxen for XPath queries.

Throws:
java.lang.Exception if the Jaxen classes are not present.
    static public void useJaxenXPathSupport() throws Exception {
        Class.forName("org.jaxen.dom.DOMXPath");
        Class c = Class.forName("freemarker.ext.dom.JaxenXPathSupport");
         = (XPathSupportc.newInstance();
        synchronized () {
             = c;
        }
        if (.isDebugEnabled()) {
            .debug("Using Jaxen classes for XPath support");
        }
    }
    
    
Convenience method. Tells the system to use Xalan for XPath queries.

Throws:
java.lang.Exception if the Xalan XPath classes are not present.
    static public void useXalanXPathSupport() throws Exception {
        Class.forName("org.apache.xpath.XPath");
        Class c = Class.forName("freemarker.ext.dom.XalanXPathSupport");
        synchronized () {
             = c;
        }
        if (.isDebugEnabled()) {
            .debug("Using Xalan classes for XPath support");
        }
    }
    
    static public void useSunInternalXPathSupport() throws Exception {
        Class.forName("com.sun.org.apache.xpath.internal.XPath");
        Class c = Class.forName("freemarker.ext.dom.SunInternalXalanXPathSupport");
        synchronized () {
             = c;
        }
        if (.isDebugEnabled()) {
            .debug("Using Sun's internal Xalan classes for XPath support");
        }
    }
    
    
Set an alternative implementation of freemarker.ext.dom.XPathSupport to use as the XPath engine.

Parameters:
cl the class, or null to disable XPath support.
    static public void setXPathSupportClass(Class cl) {
        if (cl != null && !XPathSupport.class.isAssignableFrom(cl)) {
            throw new RuntimeException("Class " + cl.getName()
                    + " does not implement freemarker.ext.dom.XPathSupport");
        }
        synchronized () {
             = cl;
        }
    }

    
Get the currently used freemarker.ext.dom.XPathSupport used as the XPath engine. Returns null if XPath support is disabled.
    static public Class getXPathSupportClass() {
        synchronized () {
            return ;
        }
    }
    static private String getText(Node node) {
        String result = "";
        if (node instanceof Text || node instanceof CDATASection) {
            result = ((org.w3c.dom.CharacterDatanode).getData();
        }
        else if (node instanceof Element) {
            NodeList children = node.getChildNodes();
            for (int i= 0; i<children.getLength(); i++) {
                result += getText(children.item(i));
            }
        }
        else if (node instanceof Document) {
            result = getText(((Documentnode).getDocumentElement());
        }
        return result;
    }
    
        if ( != null) {
            return ;
        }
        XPathSupport xps = null;
        Document doc = .getOwnerDocument();
        if (doc == null) {
            doc = (Document;
        }
        synchronized (doc) {
            WeakReference ref = (WeakReference.get(doc);
            if (ref != null) {
                xps = (XPathSupportref.get();
            }
            if (xps == null) {
                try {
                    xps = (XPathSupport.newInstance();
                    .put(docnew WeakReference(xps));
                } catch (Exception e) {
                    .error("Error instantiating xpathSupport class"e);
                }                
            }
        }
        return xps;
    }
    
    
        return getNodeName();
    }
    
    public Object getAdaptedObject(Class hint) {
        return ;
    }
    
    public Object getWrappedObject() {
        return ;
    }
    
    public Object[] explainTypeError(Class[] expectedClasses) {
        for (int i = 0; i < expectedClasses.lengthi++) {
            Class expectedClass = expectedClasses[i];
            if (TemplateDateModel.class.isAssignableFrom(expectedClass)
                    || TemplateNumberModel.class.isAssignableFrom(expectedClass)
                    || TemplateBooleanModel.class.isAssignableFrom(expectedClass)) {
                return new Object[] {
                        "XML node values are always strings (text), that is, they can't be used as number, "
                        + "date/time/datetime or boolean without explicit conversion (such as "
                        + "someNode?number, someNode?datetime.xs, someNode?date.xs, someNode?time.xs, "
                        + "someNode?boolean).",
                        };
            }
        }
        return null;
    }
    
New to GrepCode? Check out our FAQ X