Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (c) 2003 The Visigoth Software Society. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  *
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in
  *    the documentation and/or other materials provided with the
  *    distribution.
  *
  * 3. The end-user documentation included with the redistribution, if
  *    any, must include the following acknowledgement:
  *       "This product includes software developed by the
  *        Visigoth Software Society (http://www.visigoths.org/)."
  *    Alternately, this acknowledgement may appear in the software itself,
  *    if and wherever such third-party acknowledgements normally appear.
  *
  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
  *    project contributors may be used to endorse or promote products derived
  *    from this software without prior written permission. For written
  *    permission, please contact visigoths@visigoths.org.
  *
  * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
  *    nor may "FreeMarker" or "Visigoth" appear in their names
  *    without prior written permission of the Visigoth Software Society.
  *
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * ====================================================================
  *
  * This software consists of voluntary contributions made by many
  * individuals on behalf of the Visigoth Software Society. For more
  * information on the Visigoth Software Society, please see
  * http://www.visigoths.org/
  */
 
 package freemarker.ext.xml;
 
 import java.util.List;
 import java.util.Set;
 

A data model adapter for three widespread XML document object model representations: W3C DOM, dom4j, and JDOM. The adapter automatically recognizes the used XML object model and provides a unified interface for it toward the template. The model provides access to all XML InfoSet features of the XML document and includes XPath support if it has access to the XPath- evaluator library Jaxen. The model's philosophy (which closely follows that of XML InfoSet and XPath) is as follows: it always wraps a list of XML nodes (the "nodelist"). The list can be empty, can have a single element, or can have multiple elements. Every operation applied to the model is applied to all nodes in its nodelist. You usually start with a single- element nodelist, usually the root element node or the document node of the XML tree. Additionally, the nodes can contain String objects as a result of certain evaluations (getting the names of elements, values of attributes, etc.)

Implementation note: If you are using W3C DOM documents built by the Crimson XML parser (or you are using the built-in JDK 1.4 XML parser, which is essentially Crimson), make sure you call setNamespaceAware(true) on the javax.xml.parsers.DocumentBuilderFactory instance used for document building even when your documents don't use XML namespaces. Failing to do so, you will experience incorrect behavior when using the documents wrapped with this model.

Deprecated:
Use freemarker.ext.dom.NodeModel instead.
Author(s):
Attila Szegedi
Version:
$Id: NodeListModel.java,v 1.15 2004/01/06 17:06:43 szegedia Exp $
public class NodeListModel
implements
    private static final Logger logger = Logger.getLogger("freemarker.xml");
    
    private static final Class DOM_NODE_CLASS = getClass("org.w3c.dom.Node");
    private static final Class DOM4J_NODE_CLASS = getClass("org.dom4j.Node");
    private static final Navigator DOM_NAVIGATOR = getNavigator("Dom");
    private static final Navigator DOM4J_NAVIGATOR = getNavigator("Dom4j");
    private static final Navigator JDOM_NAVIGATOR = getNavigator("Jdom");
    private static volatile boolean useJaxenNamespaces = true;
    
    // The navigator object that implements document model-specific behavior.
    private final Navigator navigator;
    // The contained nodes
    private final List nodes;
    // The namespaces object (potentially shared by multiple models)
    private Namespaces namespaces;

    
Creates a new NodeListModel, wrapping the passed nodes.

Parameters:
nodes you can pass it a single XML node from any supported document model, or a Java collection containing any number of nodes. Passing null is prohibited. To create an empty model, pass it an empty collection. If a collection is passed, all passed nodes must belong to the same XML object model, i.e. you can't mix JDOM and dom4j in a single instance of NodeListModel. The model itself doesn't check for this condition, as it can be time consuming, but will throw spurious java.lang.ClassCastExceptions when it encounters mixed objects.
Throws:
java.lang.IllegalArgumentException if you pass null
    public NodeListModel(Object nodes) {
        Object node = nodes;
        if(nodes instanceof Collection) {
            this. = new ArrayList((Collection)nodes);
            node = this..isEmpty() ? null : this..get(0);
        }
        else if(nodes != null) {
            this. = Collections12.singletonList(nodes);
        }
        else {
            throw new IllegalArgumentException("nodes == null");
        }
        if( != null && .isInstance(node)) {
             = ;
        }
        else if( != null && .isInstance(node)) {
             = ;
        }
        else {
            // Assume JDOM
             = ;
        }
         = createNamespaces();
    }
    
    private Namespaces createNamespaces() {
        if() {
            try {
                return new JaxenNamespaces();
            }
            catch(Throwable t) {
                 = false;
            }
        }
        return new Namespaces();
    }
    private NodeListModel(Navigator navigatorList nodesNamespaces namespaces) {
        this. = navigator;
        this. = nodes;
        this. = namespaces;
    }
    private NodeListModel deriveModel(List derivedNodes) {
        .markShared();
        return new NodeListModel(derivedNodes);
    }
    
    
Returns the number of nodes in this model's nodelist.

    public int size() {
        return .size();
    }

    
Evaluates an XPath expression on XML nodes in this model.

Parameters:
arguments the arguments to the method invocation. Expectes exactly one argument - the XPath expression.
Returns:
a new NodeListModel with nodes selected by applying the XPath expression to this model's nodelist.
See also:
freemarker.template.TemplateMethodModel.exec(java.util.List)
    public Object exec(List argumentsthrows TemplateModelException {
        if(arguments.size() != 1) {
            throw new TemplateModelException(
                "Expecting exactly one argument - an XPath expression");
        }
        return deriveModel(.applyXPath(, (String)arguments.get(0), ));
    }

    
Returns the string representation of the wrapped nodes. String objects in the nodelist are rendered as-is (with no XML escaping applied). All other nodes are rendered in the default XML serialization format ("plain XML"). This makes the model quite suited for use as an XML-transformation tool.

Returns:
the string representation of the wrapped nodes. String objects in the nodelist are rendered as-is (with no XML escaping applied). All other nodes are rendered in the default XML serialization format ("plain XML").
See also:
freemarker.template.TemplateScalarModel.getAsString()
    public String getAsString() throws TemplateModelException {
        StringWriter sw = new StringWriter(size() * 128);
        for (Iterator iter = .iterator(); iter.hasNext();) {
            Object o = iter.next();
            if(o instanceof String) {
                sw.write((String)o);
            }
            else {
                .getAsString(osw);
           }
        }
        return sw.toString();
    }

    
Selects a single node from this model's nodelist by its list index and returns a new NodeListModel containing that single node.

Parameters:
index the ordinal number of the selected node
See also:
freemarker.template.TemplateSequenceModel.get(int)
    public TemplateModel get(int index) {
        return deriveModel(Collections12.singletonList(.get(index)));
    }

    
Returns a new NodeListModel containing the nodes that result from applying an operator to this model's nodes.

Parameters:
key the operator to apply to nodes. Available operators are:
Key nameEvaluates to
* or _childrenall direct element children of current nodes (non-recursive). Applicable to element and document nodes.
@* or _attributesall attributes of current nodes. Applicable to elements only.
@attributeNamenamed attributes of current nodes. Applicable to elements, doctypes and processing instructions. On doctypes it supports attributes publicId, systemId and elementName. On processing instructions, it supports attributes target and data, as well as any other attribute name specified in data as name="value" pair on dom4j or JDOM models. The attribute nodes for doctype and processing instruction are synthetic, and as such have no parent. Note, however that @* does NOT operate on doctypes or processing instructions.
_ancestorall ancestors up to root element (recursive) of current nodes. Applicable to same node types as _parent.
_ancestorOrSelfall ancestors of current nodes plus current nodes. Applicable to same node types as _parent.
_cnamethe canonical names of current nodes (namespace URI + local name), one string per node (non-recursive). Applicable to elements and attributes
_contentthe complete content of current nodes, including children elements, text, entity references, and processing instructions (non-recursive). Applicable to elements and documents.
_descendantall recursive descendant element children of current nodes. Applicable to document and element nodes.
_descendantOrSelfall recursive descendant element children of current nodes plus current nodes. Applicable to document and element nodes.
_documentall documents the current nodes belong to. Applicable to all nodes except text.
_doctypedoctypes of the current nodes. Applicable to document nodes only.
_filterTypeis a filter-by-type template method model. When called, it will yield a node list that contains only those current nodes whose type matches one of types passed as argument. You can pass as many string arguments as you want, each representing one of the types to select: "attribute", "cdata", "comment", "document", "documentType", "element", "entity", "entityReference", "namespace", "processingInstruction", or "text".
_namethe names of current nodes, one string per node (non-recursive). Applicable to elements and attributes (returns their local names), entity references, processing instructions (returns its target), doctypes (returns its public ID)
_nsprefixthe namespace prefixes of current nodes, one string per node (non-recursive). Applicable to elements and attributes
_nsurithe namespace URIs of current nodes, one string per node (non-recursive). Applicable to elements and attributes
_parentparent elements of current nodes. Applicable to element, attribute, comment, entity, processing instruction.
_qnamethe qualified names of current nodes in [namespacePrefix:]localName form, one string per node (non-recursive). Applicable to elements and attributes
_registerNamespace(prefix, uri)register a XML namespace with the specified prefix and URI for the current node list and all node lists that are derived from the current node list. After registering, you can use the nodelist["prefix:localname"] or nodelist["@prefix:localname"] syntaxes to reach elements and attributes whose names are namespace-scoped. Note that the namespace prefix need not match the actual prefix used by the XML document itself since namespaces are compared solely by their URI.
_textthe text of current nodes, one string per node (non-recursive). Applicable to elements, attributes, comments, processing instructions (returns its data) and CDATA sections. The reserved XML characters ('<' and '&') are NOT escaped.
_typeReturns a string describing the type of nodes, one string per node. The returned values are "attribute", "cdata", "comment", "document", "documentType", "element", "entity", "entityReference", "namespace", "processingInstruction", "text", or "unknown".
_uniquea copy of the current nodes that keeps only the first occurrence of every node, eliminating duplicates. Duplicates can occur in the node list by applying uptree-traversals _parent, _ancestor, _ancestorOrSelf, and _document on a node list with multiple elements. I.e. foo._children._parent will return a node list that has duplicates of nodes in foo - each node will have the number of occurrences equal to the number of its children. In these cases, use foo._children._parent._unique to eliminate duplicates. Applicable to all node types.
any other keyelement children of current nodes with name matching the key. This allows for convenience child traversal in book.chapter.title style syntax. Applicable to document and element nodes.
Returns:
a new NodeListModel containing the nodes that result from applying the operator to this model's nodes.
See also:
freemarker.template.TemplateHashModel.get(java.lang.String)
    public TemplateModel get(String keythrows TemplateModelException {
        // Try a built-in navigator operator
        NodeOperator op = .getOperator(key);
        String localName = null;
        String namespaceUri = "";
        // If not a nav op, then check for special keys.
        if(op == null && key.length() > 0 && key.charAt(0) == '_') {
            if(key.equals("_unique")) {
                return deriveModel(removeDuplicates());
            }
            else if(key.equals("_filterType") || key.equals("_ftype")) {
                return new FilterByType();
            }
            else if(key.equals("_registerNamespace")) {
                if(.isShared()) {
                     = (Namespaces).clone();
                }
            }
        }
        // Last, do a named child element or attribute lookup 
        if(op == null) {
            int colon = key.indexOf(':');
            if(colon == -1) {
                // No namespace prefix specified
                localName = key;
            }
            else {
                // Namespace prefix specified
                localName = key.substring(colon + 1);
                String prefix = key.substring(0, colon);
                namespaceUri = .translateNamespacePrefixToUri(prefix);
                if(namespaceUri == null) {
                    throw new TemplateModelException("Namespace prefix " + prefix + " is not registered.");
                }
            }
            if(localName.charAt(0) == '@') {
                op = .getAttributeOperator();
                localName = localName.substring(1);
            }
            else {
                op = .getChildrenOperator();
            }
        }
        List result = new ArrayList();
        for (Iterator iter = .iterator(); iter.hasNext();) {
            try {
                op.process(iter.next(), localNamenamespaceUriresult);
            }
            catch(RuntimeException e) {
                throw new TemplateModelException(e);
            }
        }
        return deriveModel(result);
    }

    
Returns true if this NodeListModel contains no nodes.

    public boolean isEmpty() {
        return .isEmpty();
    }

    
Registers a namespace prefix-URI pair for subsequent use in get(java.lang.String) as well as for use in XPath expressions.

Parameters:
prefix the namespace prefix to use for the namespace
uri the namespace URI that identifies the namespace.
    public void registerNamespace(String prefixString uri) {
        if(.isShared()) {
             = (Namespaces).clone();
        }
        .registerNamespace(prefixuri);
    }
    
    private class FilterByType
    implements
        TemplateMethodModel
    {
        public Object exec(List arguments)
        {
            List filteredNodes = new ArrayList();
            for (Iterator iter = arguments.iterator(); iter.hasNext();)
            {
                Object node = iter.next();
                if(arguments.contains(.getType(node))) {
                    filteredNodes.add(node);
                }
            }
            return deriveModel(filteredNodes);
        }
    }
    private static final List removeDuplicates(List list)
    {
        int s = list.size();
        ArrayList ulist = new ArrayList(s);
        Set set = new HashSet(s * 4 / 3, .75f);
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Object o = it.next();
            if (set.add(o)) {
                ulist.add(o);
            }
        }
        return ulist;
    }
    private static Class getClass(String className) {
        try {
            return ClassUtil.forName(className);
        }
        catch(Exception e) {
            if(.isDebugEnabled()) {
                .debug("Couldn't load class " + classNamee);
            }
            return null;
        }
    }
    
    private static Navigator getNavigator(String navType) {
        try {
            return (Navigator) ClassUtil.forName("freemarker.ext.xml." + 
                    navType + "Navigator").newInstance();
        }
        catch(Throwable t) {
            if(.isDebugEnabled()) {
                .debug("Could not load navigator for " + navTypet);
            }
            return null;
        }
    }
    {
        return (TemplateSequenceModel)get("_content");
    }
    public String getNodeName() throws TemplateModelException
    {
        return getUniqueText((NodeListModel)get("_name"), "name");
    }
    {
        return getUniqueText((NodeListModel)get("_nsuri"), "namespace");
    }
    public String getNodeType() throws TemplateModelException
    {
        return getUniqueText((NodeListModel)get("_type"), "type");
    }
    {
        return (TemplateNodeModel)get("_parent"); 
    }
    private String getUniqueText(NodeListModel modelString propertythrows TemplateModelException {
        String s1 = null;
        Set s = null;
        for(Iterator it = model.nodes.iterator(); it.hasNext();) {
            String s2 = (String)it.next();
            if(s2 != null) {
                // No text yet, make this text the current text
                if(s1 == null) {
                    s1 = s2;
                }
                // else if there's already a text and they differ, start 
                // accumulating them for an error message
                else if(!s1.equals(s2)) {
                    if(s == null) {
                        s = new HashSet();
                        s.add(s1);
                    }
                    s.add(s2);
                }
            }
        }
        // If the set for the error messages is empty, return the retval
        if(s == null) {
            return s1;
        }
        // Else throw an exception signaling ambiguity
        throw new TemplateModelException(
            "Value for node " + property + " is ambiguos: " + s);
    }
New to GrepCode? Check out our FAQ X