KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > ext > xml > NodeListModel


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 JavaDoc;
56 import java.util.ArrayList JavaDoc;
57 import java.util.Collection JavaDoc;
58 import java.util.HashSet JavaDoc;
59 import java.util.Iterator JavaDoc;
60 import java.util.List JavaDoc;
61 import java.util.Set JavaDoc;
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 JavaDoc DOM_NODE_CLASS = getClass("org.w3c.dom.Node");
112     private static final Class JavaDoc 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 JavaDoc 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 JavaDoc nodes) {
138         Object JavaDoc node = nodes;
139         if(nodes instanceof Collection JavaDoc) {
140             this.nodes = new ArrayList JavaDoc((Collection JavaDoc)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 JavaDoc("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 JavaDoc nodes, Namespaces namespaces) {
163         this.navigator = navigator;
164         this.nodes = nodes;
165         this.namespaces = namespaces;
166     }
167
168     private NodeListModel deriveModel(List JavaDoc 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 JavaDoc exec(List JavaDoc 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 JavaDoc)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 JavaDoc getAsString() throws TemplateModelException {
209         StringWriter JavaDoc sw = new StringWriter JavaDoc(size() * 128);
210         for (Iterator JavaDoc iter = nodes.iterator(); iter.hasNext();) {
211             Object JavaDoc o = iter.next();
212             if(o instanceof String JavaDoc) {
213                 sw.write((String JavaDoc)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 JavaDoc key) throws TemplateModelException {
411         // Try a built-in navigator operator
412
NodeOperator op = navigator.getOperator(key);
413         String JavaDoc localName = null;
414         String JavaDoc 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 JavaDoc 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 JavaDoc result = new ArrayList JavaDoc();
454         for (Iterator JavaDoc iter = nodes.iterator(); iter.hasNext();) {
455             try {
456                 op.process(iter.next(), localName, namespaceUri, result);
457             }
458             catch(RuntimeException JavaDoc 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 JavaDoc prefix, String JavaDoc 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 JavaDoc exec(List JavaDoc arguments)
491         {
492             List JavaDoc filteredNodes = new ArrayList JavaDoc();
493             for (Iterator JavaDoc iter = arguments.iterator(); iter.hasNext();)
494             {
495                 Object JavaDoc 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 JavaDoc removeDuplicates(List JavaDoc list)
505     {
506         int s = list.size();
507         ArrayList JavaDoc ulist = new ArrayList JavaDoc(s);
508         Set JavaDoc set = new HashSet JavaDoc(s * 4 / 3, .75f);
509         Iterator JavaDoc it = list.iterator();
510         while (it.hasNext()) {
511             Object JavaDoc o = it.next();
512             if (set.add(o)) {
513                 ulist.add(o);
514             }
515         }
516         return ulist;
517     }
518
519     private static Class JavaDoc getClass(String JavaDoc className) {
520         try {
521             return ClassUtil.forName(className);
522         }
523         catch(Exception JavaDoc 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 JavaDoc clazz) {
540         try {
541             return (Namespaces.Factory)
542                 ClassUtil.forName("freemarker.ext.xml." + clazz)
543                     .getDeclaredField("FACTORY").get(null);
544         }
545         catch(Throwable JavaDoc 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 JavaDoc navType) {
554         try {
555             Navigator nav =
556                 (Navigator) ClassUtil.forName("freemarker.ext.xml." + navType + "Navigator")
557                     .getDeclaredConstructor(new Class JavaDoc[] {}).newInstance(new Object JavaDoc[] {});
558             return nav;
559         }
560         catch(Throwable JavaDoc 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 JavaDoc getNodeName() throws TemplateModelException
574     {
575         return getUniqueText((NodeListModel)get("_name"), "name");
576     }
577
578     public String JavaDoc getNodeNamespace() throws TemplateModelException
579     {
580         return getUniqueText((NodeListModel)get("_nsuri"), "namespace");
581     }
582
583     public String JavaDoc 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 JavaDoc getUniqueText(NodeListModel model, String JavaDoc property) throws TemplateModelException {
593         String JavaDoc s1 = null;
594         Set JavaDoc s = null;
595         for(Iterator JavaDoc it = model.nodes.iterator(); it.hasNext();) {
596             String JavaDoc s2 = (String JavaDoc)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 JavaDoc();
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 }
622
Popular Tags