Save This Page
Home » freemarker-2.3.13 » freemarker.ext.xml » [javadoc | source]
    1   /*
    2    * Copyright (c) 2003 The Visigoth Software Society. All rights
    3    * reserved.
    4    *
    5    * Redistribution and use in source and binary forms, with or without
    6    * modification, are permitted provided that the following conditions
    7    * are met:
    8    *
    9    * 1. Redistributions of source code must retain the above copyright
   10    *    notice, this list of conditions and the following disclaimer.
   11    *
   12    * 2. Redistributions in binary form must reproduce the above copyright
   13    *    notice, this list of conditions and the following disclaimer in
   14    *    the documentation and/or other materials provided with the
   15    *    distribution.
   16    *
   17    * 3. The end-user documentation included with the redistribution, if
   18    *    any, must include the following acknowledgement:
   19    *       "This product includes software developed by the
   20    *        Visigoth Software Society (http://www.visigoths.org/)."
   21    *    Alternately, this acknowledgement may appear in the software itself,
   22    *    if and wherever such third-party acknowledgements normally appear.
   23    *
   24    * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
   25    *    project contributors may be used to endorse or promote products derived
   26    *    from this software without prior written permission. For written
   27    *    permission, please contact visigoths@visigoths.org.
   28    *
   29    * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
   30    *    nor may "FreeMarker" or "Visigoth" appear in their names
   31    *    without prior written permission of the Visigoth Software Society.
   32    *
   33    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   34    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   35    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   36    * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
   37    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   38    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   39    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   40    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   41    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   42    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   43    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   44    * SUCH DAMAGE.
   45    * ====================================================================
   46    *
   47    * This software consists of voluntary contributions made by many
   48    * individuals on behalf of the Visigoth Software Society. For more
   49    * information on the Visigoth Software Society, please see
   50    * http://www.visigoths.org/
   51    */
   52   
   53   package freemarker.ext.xml;
   54   
   55   import java.io.StringWriter;
   56   import java.util.ArrayList;
   57   import java.util.Collection;
   58   import java.util.HashSet;
   59   import java.util.Iterator;
   60   import java.util.List;
   61   import java.util.Set;
   62   
   63   import freemarker.log.Logger;
   64   import freemarker.template.TemplateHashModel;
   65   import freemarker.template.TemplateMethodModel;
   66   import freemarker.template.TemplateModel;
   67   import freemarker.template.TemplateModelException;
   68   import freemarker.template.TemplateNodeModel;
   69   import freemarker.template.TemplateScalarModel;
   70   import freemarker.template.TemplateSequenceModel;
   71   import freemarker.template.utility.ClassUtil;
   72   import freemarker.template.utility.Collections12;
   73   
   74   /**
   75    * <p>A data model adapter for three widespread XML document object model
   76    * representations: W3C DOM, dom4j, and JDOM. The adapter automatically
   77    * recognizes the used XML object model and provides a unified interface for it
   78    * toward the template. The model provides access to all XML InfoSet features
   79    * of the XML document and includes XPath support if it has access to the XPath-
   80    * evaluator library Jaxen. The model's philosophy (which closely follows that
   81    * of XML InfoSet and XPath) is as follows: it always wraps a list of XML nodes
   82    * (the "nodelist"). The list can be empty, can have a single element, or can
   83    * have multiple elements. Every operation applied to the model is applied to
   84    * all nodes in its nodelist. You usually start with a single- element nodelist,
   85    * usually the root element node or the document node of the XML tree.
   86    * Additionally, the nodes can contain String objects as a result of certain
   87    * evaluations (getting the names of elements, values of attributes, etc.)</p>
   88    * <p><strong>Implementation note:</strong> If you are using W3C DOM documents
   89    * built by the Crimson XML parser (or you are using the built-in JDK 1.4 XML
   90    * parser, which is essentially Crimson), make sure you call
   91    * <tt>setNamespaceAware(true)</tt> on the 
   92    * <tt>javax.xml.parsers.DocumentBuilderFactory</tt> instance used for document
   93    * building even when your documents don't use XML namespaces. Failing to do so,
   94    * you will experience incorrect behavior when using the documents wrapped with
   95    * this model.</p>
   96    *
   97    * @deprecated Use {@link freemarker.ext.dom.NodeModel} instead.
   98    * @version $Id: NodeListModel.java,v 1.15 2004/01/06 17:06:43 szegedia Exp $
   99    * @author Attila Szegedi
  100    */
  101   public class NodeListModel
  102   implements
  103       TemplateHashModel,
  104       TemplateMethodModel,
  105       TemplateScalarModel,
  106       TemplateSequenceModel,
  107       TemplateNodeModel
  108   {
  109       private static final Logger logger = Logger.getLogger("freemarker.xml");
  110       
  111       private static final Class DOM_NODE_CLASS = getClass("org.w3c.dom.Node");
  112       private static final Class DOM4J_NODE_CLASS = getClass("org.dom4j.Node");
  113       private static final Navigator DOM_NAVIGATOR = getNavigator("Dom");
  114       private static final Navigator DOM4J_NAVIGATOR = getNavigator("Dom4j");
  115       private static final Navigator JDOM_NAVIGATOR = getNavigator("Jdom");
  116       private static final Namespaces.Factory NS_FACTORY = getNamespacesFactory();
  117   
  118       // The navigator object that implements document model-specific behavior.
  119       private final Navigator navigator;
  120       // The contained nodes
  121       private final List nodes;
  122       // The namespaces object (potentially shared by multiple models)
  123       private Namespaces namespaces;
  124   
  125       /**
  126        * Creates a new NodeListModel, wrapping the passed nodes.
  127        * @param nodes you can pass it a single XML node from any supported
  128        * document model, or a Java collection containing any number of nodes.
  129        * Passing null is prohibited. To create an empty model, pass it an empty
  130        * collection. If a collection is passed, all passed nodes must belong to
  131        * the same XML object model, i.e. you can't mix JDOM and dom4j in a single
  132        * instance of NodeListModel. The model itself doesn't check for this condition,
  133        * as it can be time consuming, but will throw spurious
  134        * {@link ClassCastException}s when it encounters mixed objects.
  135        * @throws IllegalArgumentException if you pass null
  136        */
  137       public NodeListModel(Object nodes) {
  138           Object node = nodes;
  139           if(nodes instanceof Collection) {
  140               this.nodes = new ArrayList((Collection)nodes);
  141               node = this.nodes.isEmpty() ? null : this.nodes.get(0);
  142           }
  143           else if(nodes != null) {
  144               this.nodes = Collections12.singletonList(nodes);
  145           }
  146           else {
  147               throw new IllegalArgumentException("nodes == null");
  148           }
  149           if(DOM_NODE_CLASS != null && DOM_NODE_CLASS.isInstance(node)) {
  150               navigator = DOM_NAVIGATOR;
  151           }
  152           else if(DOM4J_NODE_CLASS != null && DOM4J_NODE_CLASS.isInstance(node)) {
  153               navigator = DOM4J_NAVIGATOR;
  154           }
  155           else {
  156               // Assume JDOM
  157               navigator = JDOM_NAVIGATOR;
  158           }
  159           namespaces = NS_FACTORY.create();
  160       }
  161       
  162       private NodeListModel(Navigator navigator, List nodes, Namespaces namespaces) {
  163           this.navigator = navigator;
  164           this.nodes = nodes;
  165           this.namespaces = namespaces;
  166       }
  167   
  168       private NodeListModel deriveModel(List derivedNodes) {
  169           namespaces.markShared();
  170           return new NodeListModel(navigator, derivedNodes, namespaces);
  171       }
  172       
  173       /**
  174        * Returns the number of nodes in this model's nodelist.
  175        * @see freemarker.template.TemplateSequenceModel#size()
  176        */
  177       public int size() {
  178           return nodes.size();
  179       }
  180   
  181       /**
  182        * Evaluates an XPath expression on XML nodes in this model.
  183        * @param arguments the arguments to the method invocation. Expectes exactly
  184        * one argument - the XPath expression.
  185        * @return a new NodeListModel with nodes selected by applying the XPath
  186        * expression to this model's nodelist.
  187        * @see freemarker.template.TemplateMethodModel#exec(List)
  188        */
  189       public Object exec(List arguments) throws TemplateModelException {
  190           if(arguments.size() != 1) {
  191               throw new TemplateModelException(
  192                   "Expecting exactly one argument - an XPath expression");
  193           }
  194           return deriveModel(navigator.applyXPath(nodes, (String)arguments.get(0), namespaces));
  195       }
  196   
  197       /**
  198        * Returns the string representation of the wrapped nodes. String objects in
  199        * the nodelist are rendered as-is (with no XML escaping applied). All other
  200        * nodes are rendered in the default XML serialization format ("plain XML").
  201        * This makes the model quite suited for use as an XML-transformation tool.
  202        * @return the string representation of the wrapped nodes. String objects
  203        * in the nodelist are rendered as-is (with no XML escaping applied). All
  204        * other nodes are rendered in the default XML serialization format ("plain
  205        * XML"). 
  206        * @see freemarker.template.TemplateScalarModel#getAsString()
  207        */
  208       public String getAsString() throws TemplateModelException {
  209           StringWriter sw = new StringWriter(size() * 128);
  210           for (Iterator iter = nodes.iterator(); iter.hasNext();) {
  211               Object o = iter.next();
  212               if(o instanceof String) {
  213                   sw.write((String)o);
  214               }
  215               else {
  216                   navigator.getAsString(o, sw);
  217              }
  218           }
  219           return sw.toString();
  220       }
  221   
  222       /**
  223        * Selects a single node from this model's nodelist by its list index and
  224        * returns a new NodeListModel containing that single node.
  225        * @param index the ordinal number of the selected node 
  226        * @see freemarker.template.TemplateSequenceModel#get(int)
  227        */
  228       public TemplateModel get(int index) {
  229           return deriveModel(Collections12.singletonList(nodes.get(index)));
  230       }
  231   
  232       /**
  233        * Returns a new NodeListModel containing the nodes that result from applying
  234        * an operator to this model's nodes.
  235        * @param key the operator to apply to nodes. Available operators are:
  236        * <table border="1">
  237        *   <thead>
  238        *     <tr>
  239        *       <th align="left">Key name</th>
  240        *       <th align="left">Evaluates to</th>
  241        *     </tr>  
  242        *   </thead>
  243        *   <tbody>
  244        *     <tr>
  245        *       <td><tt>*</tt> or <tt>_children</tt></td>
  246        *       <td>all direct element children of current nodes (non-recursive).
  247        *         Applicable to element and document nodes.</td>
  248        *     </tr>  
  249        *     <tr>
  250        *       <td><tt>@*</tt> or <tt>_attributes</tt></td>
  251        *       <td>all attributes of current nodes. Applicable to elements only.
  252        *         </td>
  253        *     </tr>
  254        *     <tr>
  255        *       <td><tt>@<i>attributeName</i></tt></td>
  256        *       <td>named attributes of current nodes. Applicable to elements, 
  257        *         doctypes and processing instructions. On doctypes it supports 
  258        *         attributes <tt>publicId</tt>, <tt>systemId</tt> and 
  259        *         <tt>elementName</tt>. On processing instructions, it supports 
  260        *         attributes <tt>target</tt> and <tt>data</tt>, as well as any 
  261        *         other attribute name specified in data as 
  262        *         <tt>name=&quot;value&quot;</tt> pair on dom4j or JDOM models. 
  263        *         The attribute nodes for doctype and processing instruction are 
  264        *         synthetic, and as such have no parent. Note, however that 
  265        *         <tt>@*</tt> does NOT operate on doctypes or processing 
  266        *         instructions.</td>
  267        *     </tr>  
  268        * 
  269        *     <tr>
  270        *       <td><tt>_ancestor</tt></td>
  271        *       <td>all ancestors up to root element (recursive) of current nodes.
  272        *         Applicable to same node types as <tt>_parent</tt>.</td>
  273        *     </tr>  
  274        *     <tr>
  275        *       <td><tt>_ancestorOrSelf</tt></td>
  276        *       <td>all ancestors of current nodes plus current nodes. Applicable 
  277        *         to same node types as <tt>_parent</tt>.</td>
  278        *     </tr>  
  279        *     <tr>
  280        *       <td><tt>_cname</tt></td>
  281        *       <td>the canonical names of current nodes (namespace URI + local 
  282        *         name), one string per node (non-recursive). Applicable to 
  283        *         elements and attributes</td>
  284        *     </tr>  
  285        *     <tr>
  286        *       <td><tt>_content</tt></td>
  287        *       <td>the complete content of current nodes, including children 
  288        *         elements, text, entity references, and processing instructions 
  289        *         (non-recursive). Applicable to elements and documents.</td>
  290        *     </tr>  
  291        *     <tr>
  292        *       <td><tt>_descendant</tt></td>
  293        *       <td>all recursive descendant element children of current nodes. 
  294        *         Applicable to document and element nodes.</td>
  295        *     </tr>  
  296        *     <tr>
  297        *       <td><tt>_descendantOrSelf</tt></td>
  298        *       <td>all recursive descendant element children of current nodes 
  299        *         plus current nodes. Applicable to document and element nodes.
  300        *         </td>
  301        *     </tr>
  302        *     <tr>
  303        *       <td><tt>_document</tt></td>
  304        *       <td>all documents the current nodes belong to. Applicable to all 
  305        *       nodes except text.</td>
  306        *     </tr>
  307        *     <tr>
  308        *       <td><tt>_doctype</tt></td>
  309        *       <td>doctypes of the current nodes. Applicable to document nodes 
  310        *       only.</td>
  311        *     </tr>
  312        *     <tr>
  313        *       <td><tt>_filterType</tt></td>
  314        *       <td>is a filter-by-type template method model. When called, it 
  315        *         will yield a node list that contains only those current nodes 
  316        *         whose type matches one of types passed as argument. You can pass
  317        *         as many string arguments as you want, each representing one of
  318        *         the types to select: &quot;attribute&quot;, &quot;cdata&quot;,
  319        *         &quot;comment&quot;, &quot;document&quot;, 
  320        *         &quot;documentType&quot;, &quot;element&quot;, 
  321        *         &quot;entity&quot;, &quot;entityReference&quot;,
  322        *         &quot;namespace&quot;, &quot;processingInstruction&quot;, or
  323        *         &quot;text&quot;.</td>
  324        *     </tr>
  325        *     <tr>
  326        *       <td><tt>_name</tt></td>
  327        *       <td>the names of current nodes, one string per node 
  328        *         (non-recursive). Applicable to elements and attributes 
  329        *         (returns their local names), entity references, processing 
  330        *         instructions (returns its target), doctypes (returns its public
  331        *         ID)</td>
  332        *     </tr>
  333        *     <tr>
  334        *       <td><tt>_nsprefix</tt></td>
  335        *       <td>the namespace prefixes of current nodes, one string per node 
  336        *         (non-recursive). Applicable to elements and attributes</td>
  337        *     </tr>
  338        *     <tr>
  339        *       <td><tt>_nsuri</tt></td>
  340        *       <td>the namespace URIs of current nodes, one string per node 
  341        *       (non-recursive). Applicable to elements and attributes</td>
  342        *     </tr>
  343        *     <tr>
  344        *       <td><tt>_parent</tt></td>
  345        *       <td>parent elements of current nodes. Applicable to element, 
  346        *       attribute, comment, entity, processing instruction.</td>
  347        *     </tr>
  348        *     <tr>
  349        *       <td><tt>_qname</tt></td>
  350        *       <td>the qualified names of current nodes in 
  351        *         <tt>[namespacePrefix:]localName</tt> form, one string per node 
  352        *         (non-recursive). Applicable to elements and attributes</td>
  353        *     </tr>
  354        *     <tr>
  355        *       <td><tt>_registerNamespace(prefix, uri)</tt></td>
  356        *       <td>register a XML namespace with the specified prefix and URI for
  357        *         the current node list and all node lists that are derived from 
  358        *         the current node list. After registering, you can use the
  359        *         <tt>nodelist[&quot;prefix:localname&quot;]</tt> or 
  360        *         <tt>nodelist[&quot;@prefix:localname&quot;]</tt> syntaxes to 
  361        *         reach elements and attributes whose names are namespace-scoped.
  362        *         Note that the namespace prefix need not match the actual prefix 
  363        *         used by the XML document itself since namespaces are compared 
  364        *         solely by their URI.</td>
  365        *     </tr>
  366        *     <tr>
  367        *       <td><tt>_text</tt></td>
  368        *       <td>the text of current nodes, one string per node 
  369        *         (non-recursive). Applicable to elements, attributes, comments, 
  370        *         processing instructions (returns its data) and CDATA sections. 
  371        *         The reserved XML characters ('&lt;' and '&amp;') are NOT 
  372        *         escaped.</td>
  373        *     </tr>
  374        *     <tr>
  375        *       <td><tt>_type</tt></td>
  376        *       <td>Returns a string describing the type of nodes, one
  377        *         string per node. The returned values are &quot;attribute&quot;,
  378        *         &quot;cdata&quot;, &quot;comment&quot;, &quot;document&quot;,
  379        *         &quot;documentType&quot;, &quot;element&quot;, 
  380        *         &quot;entity&quot;, &quot;entityReference&quot;, 
  381        *         &quot;namespace&quot;, &quot;processingInstruction&quot;, 
  382        *         &quot;text&quot;, or &quot;unknown&quot;.</td>
  383        *     </tr>
  384        *     <tr>
  385        *       <td><tt>_unique</tt></td>
  386        *       <td>a copy of the current nodes that keeps only the first 
  387        *         occurrence of every node, eliminating duplicates. Duplicates can
  388        *         occur in the node list by applying uptree-traversals 
  389        *         <tt>_parent</tt>, <tt>_ancestor</tt>, <tt>_ancestorOrSelf</tt>,
  390        *         and <tt>_document</tt> on a node list with multiple elements. 
  391        *         I.e. <tt>foo._children._parent</tt> will return a node list that
  392        *         has duplicates of nodes in foo - each node will have the number 
  393        *         of occurrences equal to the number of its children. In these 
  394        *         cases, use <tt>foo._children._parent._unique</tt> to eliminate 
  395        *         duplicates. Applicable to all node types.</td>
  396        *     </tr>
  397        *     <tr>
  398        *       <td>any other key</td>
  399        *       <td>element children of current nodes with name matching the key. 
  400        *       This allows for convenience child traversal in 
  401        *       <tt>book.chapter.title</tt> style syntax. Applicable to document 
  402        *       and element nodes.</td>
  403        *     </tr>
  404        *   </tbody>
  405        * </table>
  406        * @return a new NodeListModel containing the nodes that result from applying
  407        * the operator to this model's nodes.
  408        * @see freemarker.template.TemplateHashModel#get(String)
  409        */
  410       public TemplateModel get(String key) throws TemplateModelException {
  411           // Try a built-in navigator operator
  412           NodeOperator op = navigator.getOperator(key);
  413           String localName = null;
  414           String namespaceUri = "";
  415           // If not a nav op, then check for special keys.
  416           if(op == null && key.length() > 0 && key.charAt(0) == '_') {
  417               if(key.equals("_unique")) {
  418                   return deriveModel(removeDuplicates(nodes));
  419               }
  420               else if(key.equals("_filterType") || key.equals("_ftype")) {
  421                   return new FilterByType();
  422               }
  423               else if(key.equals("_registerNamespace")) {
  424                   if(namespaces.isShared()) {
  425                       namespaces = (Namespaces)namespaces.clone();
  426                   }
  427               }
  428           }
  429           // Last, do a named child element or attribute lookup 
  430           if(op == null) {
  431               int colon = key.indexOf(':');
  432               if(colon == -1) {
  433                   // No namespace prefix specified
  434                   localName = key;
  435               }
  436               else {
  437                   // Namespace prefix specified
  438                   localName = key.substring(colon + 1);
  439                   String prefix = key.substring(0, colon);
  440                   namespaceUri = namespaces.translateNamespacePrefixToUri(prefix);
  441                   if(namespaceUri == null) {
  442                       throw new TemplateModelException("Namespace prefix " + prefix + " is not registered.");
  443                   }
  444               }
  445               if(localName.charAt(0) == '@') {
  446                   op = navigator.getAttributeOperator();
  447                   localName = localName.substring(1);
  448               }
  449               else {
  450                   op = navigator.getChildrenOperator();
  451               }
  452           }
  453           List result = new ArrayList();
  454           for (Iterator iter = nodes.iterator(); iter.hasNext();) {
  455               try {
  456                   op.process(iter.next(), localName, namespaceUri, result);
  457               }
  458               catch(RuntimeException e) {
  459                   throw new TemplateModelException(e);
  460               }
  461           }
  462           return deriveModel(result);
  463       }
  464   
  465       /**
  466        * Returns true if this NodeListModel contains no nodes. 
  467        * @see freemarker.template.TemplateHashModel#isEmpty()
  468        */
  469       public boolean isEmpty() {
  470           return nodes.isEmpty();
  471       }
  472   
  473       /**
  474        * Registers a namespace prefix-URI pair for subsequent use in {@link
  475        * #get(String)} as well as for use in XPath expressions.
  476        * @param prefix the namespace prefix to use for the namespace
  477        * @param uri the namespace URI that identifies the namespace.
  478        */
  479       public void registerNamespace(String prefix, String uri) {
  480           if(namespaces.isShared()) {
  481               namespaces = (Namespaces)namespaces.clone();
  482           }
  483           namespaces.registerNamespace(prefix, uri);
  484       }
  485       
  486       private class FilterByType
  487       implements
  488           TemplateMethodModel
  489       {
  490           public Object exec(List arguments)
  491           {
  492               List filteredNodes = new ArrayList();
  493               for (Iterator iter = arguments.iterator(); iter.hasNext();)
  494               {
  495                   Object node = iter.next();
  496                   if(arguments.contains(navigator.getType(node))) {
  497                       filteredNodes.add(node);
  498                   }
  499               }
  500               return deriveModel(filteredNodes);
  501           }
  502       }
  503   
  504       private static final List removeDuplicates(List list)
  505       {
  506           int s = list.size();
  507           ArrayList ulist = new ArrayList(s);
  508           Set set = new HashSet(s * 4 / 3, .75f);
  509           Iterator it = list.iterator();
  510           while (it.hasNext()) {
  511               Object o = it.next();
  512               if (set.add(o)) {
  513                   ulist.add(o);
  514               }
  515           }
  516           return ulist;
  517       }
  518   
  519       private static Class getClass(String className) {
  520           try {
  521               return ClassUtil.forName(className);
  522           }
  523           catch(Exception e) {
  524               if(logger.isDebugEnabled()) {
  525                   logger.debug("Couldn't load class " + className, e);
  526               }
  527               return null;
  528           }
  529       }
  530       
  531       private static Namespaces.Factory getNamespacesFactory() {
  532           Namespaces.Factory factory = getNamespacesFactory("JaxenNamespaces");
  533           if(factory == null) {
  534               factory = getNamespacesFactory("Namespaces");
  535           }
  536           return factory;
  537       }
  538       
  539       private static Namespaces.Factory getNamespacesFactory(String clazz) {
  540           try {
  541               return (Namespaces.Factory)
  542                   ClassUtil.forName("freemarker.ext.xml." + clazz)
  543                       .getDeclaredField("FACTORY").get(null);
  544           }
  545           catch(Throwable t) {
  546               if(logger.isDebugEnabled()) {
  547                   logger.debug("Could not load " + clazz, t);
  548               }
  549               return null;
  550           }
  551       }
  552       
  553       private static Navigator getNavigator(String navType) {
  554           try {
  555               Navigator nav =  
  556                   (Navigator) ClassUtil.forName("freemarker.ext.xml." + navType + "Navigator")
  557                       .getDeclaredConstructor(new Class[] {}).newInstance(new Object[] {});
  558               return nav;
  559           }
  560           catch(Throwable t) {
  561               if(logger.isDebugEnabled()) {
  562                   logger.debug("Could not load navigator for " + navType, t);
  563               }
  564               return null;
  565           }
  566       }
  567   
  568       public TemplateSequenceModel getChildNodes() throws TemplateModelException
  569       {
  570           return (TemplateSequenceModel)get("_content");
  571       }
  572   
  573       public String getNodeName() throws TemplateModelException
  574       {
  575           return getUniqueText((NodeListModel)get("_name"), "name");
  576       }
  577   
  578       public String getNodeNamespace() throws TemplateModelException
  579       {
  580           return getUniqueText((NodeListModel)get("_nsuri"), "namespace");
  581       }
  582   
  583       public String getNodeType() throws TemplateModelException
  584       {
  585           return getUniqueText((NodeListModel)get("_type"), "type");
  586       }
  587       public TemplateNodeModel getParentNode() throws TemplateModelException
  588       {
  589           return (TemplateNodeModel)get("_parent"); 
  590       }
  591   
  592       private String getUniqueText(NodeListModel model, String property) throws TemplateModelException {
  593           String s1 = null;
  594           Set s = null;
  595           for(Iterator it = model.nodes.iterator(); it.hasNext();) {
  596               String s2 = (String)it.next();
  597               if(s2 != null) {
  598                   // No text yet, make this text the current text
  599                   if(s1 == null) {
  600                       s1 = s2;
  601                   }
  602                   // else if there's already a text and they differ, start 
  603                   // accumulating them for an error message
  604                   else if(!s1.equals(s2)) {
  605                       if(s == null) {
  606                           s = new HashSet();
  607                           s.add(s1);
  608                       }
  609                       s.add(s2);
  610                   }
  611               }
  612           }
  613           // If the set for the error messages is empty, return the retval
  614           if(s == null) {
  615               return s1;
  616           }
  617           // Else throw an exception signaling ambiguity
  618           throw new TemplateModelException(
  619               "Value for node " + property + " is ambiguos: " + s);
  620       }
  621   }

Save This Page
Home » freemarker-2.3.13 » freemarker.ext.xml » [javadoc | source]