KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > ext > jdom > 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.jdom;
54
55 import java.io.FileReader JavaDoc;
56 import java.io.IOException JavaDoc;
57 import java.io.Writer JavaDoc;
58 import java.util.ArrayList JavaDoc;
59 import java.util.Collections JavaDoc;
60 import java.util.HashMap JavaDoc;
61 import java.util.HashSet JavaDoc;
62 import java.util.Iterator JavaDoc;
63 import java.util.LinkedList JavaDoc;
64 import java.util.List JavaDoc;
65 import java.util.Map JavaDoc;
66 import java.util.Set JavaDoc;
67 import java.util.WeakHashMap JavaDoc;
68
69 import org.jaxen.Context;
70 import org.jaxen.JaxenException;
71 import org.jaxen.NamespaceContext;
72 import org.jaxen.jdom.JDOMXPath;
73 import org.jdom.Attribute;
74 import org.jdom.CDATA;
75 import org.jdom.Comment;
76 import org.jdom.DocType;
77 import org.jdom.Document;
78 import org.jdom.Element;
79 import org.jdom.EntityRef;
80 import org.jdom.Namespace;
81 import org.jdom.ProcessingInstruction;
82 import org.jdom.Text;
83 import org.jdom.output.XMLOutputter;
84 import freemarker.template.SimpleHash;
85 import freemarker.template.SimpleScalar;
86 import freemarker.template.Template;
87 import freemarker.template.TemplateCollectionModel;
88 import freemarker.template.TemplateHashModel;
89 import freemarker.template.TemplateMethodModel;
90 import freemarker.template.TemplateModel;
91 import freemarker.template.TemplateModelException;
92 import freemarker.template.TemplateModelIterator;
93 import freemarker.template.TemplateScalarModel;
94 import freemarker.template.TemplateSequenceModel;
95 import freemarker.template.utility.Collections12;
96
97 /**
98  * Provides a template for wrapping JDOM objects. It is capable of storing not only
99  * a single JDOM node, but a list of JDOM nodes at once (hence the name).
100  * Each node is an instance of any of the core JDOM node classes (except namespaces,
101  * which are not supported at the moment), or String for representing text.
102  * See individual method documentation for exact details on how the class works. In
103  * short:
104  * <ul>
105  * <li>{@link #getAsString()} will render all contained nodes as XML fragment,</tt>
106  * <li>{@link #exec(List)} provides full XPath functionality implemented on top of
107  * the <a HREF="http://www.jaxen.org">Jaxen</a> library,</li>
108  * <li>{@link #get(String)} provides node traversal, copying and filtering - somewhat
109  * less expressive than XPath, however it does not require the external library and
110  * it evaluates somewhat faster</li>
111  * <li>being a {@link TemplateCollectionModel} allows to iterate the contained node list, and</li>
112  * <li>being a {@link TemplateSequenceModel} allows to access the contained nodes by index and query the node count.</li>
113  * </ul>
114  *
115  * <p><b>Note:</b> There is a JDOM independent re-implementation of this class:
116  * {@link freemarker.ext.xml.NodeListModel freemarker.ext.xml.NodeListModel}
117  *
118  * @deprecated Use {@link freemarker.ext.dom.NodeModel} instead.
119  * @author Attila Szegedi
120  * @version $Id: NodeListModel.java,v 1.52 2004/01/06 17:06:42 szegedia Exp $
121  */

122 public class NodeListModel
123 implements
124     TemplateHashModel,
125     TemplateMethodModel,
126     TemplateCollectionModel,
127     TemplateSequenceModel,
128     TemplateScalarModel
129 {
130     private static final AttributeXMLOutputter OUTPUT = new AttributeXMLOutputter();
131     // A convenience singleton for representing a node list without nodes.
132
private static final NodeListModel EMPTY = new NodeListModel(null, false);
133
134     // Cache of already parsed XPath expressions
135
private static final Map JavaDoc XPATH_CACHE = new WeakHashMap JavaDoc();
136
137     private static final NamedNodeOperator NAMED_CHILDREN_OP = new NamedChildrenOp();
138     private static final NamedNodeOperator NAMED_ATTRIBUTE_OP = new NamedAttributeOp();
139     private static final NodeOperator ALL_ATTRIBUTES_OP = new AllAttributesOp();
140     private static final NodeOperator ALL_CHILDREN_OP = new AllChildrenOp();
141     private static final Map JavaDoc OPERATIONS = createOperations();
142     private static final Map JavaDoc SPECIAL_OPERATIONS = createSpecialOperations();
143     private static final int SPECIAL_OPERATION_COPY = 0;
144     private static final int SPECIAL_OPERATION_UNIQUE = 1;
145     private static final int SPECIAL_OPERATION_FILTER_NAME = 2;
146     private static final int SPECIAL_OPERATION_FILTER_TYPE = 3;
147     private static final int SPECIAL_OPERATION_QUERY_TYPE = 4;
148     private static final int SPECIAL_OPERATION_REGISTER_NAMESPACE = 5;
149     private static final int SPECIAL_OPERATION_PLAINTEXT = 6;
150
151     // The contained nodes
152
private final List JavaDoc nodes;
153     private final Map JavaDoc namespaces;
154
155     /**
156      * Creates a node list that holds a single {@link Document} node.
157      */

158     public NodeListModel(Document document)
159     {
160         nodes = document == null ? Collections.EMPTY_LIST : Collections12.singletonList(document);
161         namespaces = new HashMap JavaDoc();
162     }
163
164     /**
165      * Creates a node list that holds a single {@link Element} node.
166      */

167     public NodeListModel(Element element)
168     {
169         nodes = element == null ? Collections.EMPTY_LIST : Collections12.singletonList(element);
170         namespaces = new HashMap JavaDoc();
171     }
172
173     private NodeListModel(Object JavaDoc object, Map JavaDoc namespaces)
174     {
175         nodes = object == null ? Collections.EMPTY_LIST : Collections12.singletonList(object);
176         this.namespaces = namespaces;
177     }
178
179     /**
180      * Creates a node list that holds a list of nodes.
181      * @param nodes the list of nodes this template should hold. The created template
182      * will copy the passed nodes list, so changes to the passed list will not affect
183      * the model.
184      */

185     public NodeListModel(List JavaDoc nodes)
186     {
187         this(nodes, true);
188     }
189
190     /**
191      * Creates a node list that holds a list of nodes.
192      * @param nodes the list of nodes this template should hold.
193      * @param copy if true, the created template will copy the passed nodes list,
194      * so changes to the passed list will not affect the model. If false, the model
195      * will reference the passed list and will sense changes in it, although no
196      * operations on the list will be synchronized.
197      */

198     public NodeListModel(List JavaDoc nodes, boolean copy)
199     {
200         this.nodes = copy && nodes != null ? new ArrayList JavaDoc(nodes) : (nodes == null ? Collections.EMPTY_LIST : nodes);
201         namespaces = new HashMap JavaDoc();
202     }
203
204     private NodeListModel(List JavaDoc nodes, Map JavaDoc namespaces)
205     {
206         this.nodes = nodes == null ? Collections.EMPTY_LIST : nodes;
207         this.namespaces = namespaces;
208     }
209
210     private static final NodeListModel createNodeListModel(List JavaDoc list, Map JavaDoc namespaces)
211     {
212         if (list == null || list.isEmpty()) {
213             if (namespaces.isEmpty()) {
214                 return EMPTY;
215             } else {
216                 return new NodeListModel(Collections.EMPTY_LIST, namespaces);
217             }
218         }
219         if (list.size() == 1) return new NodeListModel(list.get(0), namespaces);
220         return new NodeListModel(list, namespaces);
221     }
222
223     /**
224      * Returns true if this model contains no nodes.
225      */

226     public boolean isEmpty()
227     {
228         return nodes.isEmpty();
229     }
230
231     /**
232      * This method returns the string resulting from concatenation
233      * of string representations of its nodes. Each node is rendered using its XML
234      * serialization format, while text (String) is rendered as itself. This greatly
235      * simplifies creating XML-transformation templates, as to output a node contained
236      * in variable x as XML fragment, you simply write ${x} in the template.
237      */

238     public String JavaDoc getAsString()
239     throws
240     TemplateModelException
241     {
242         if (isEmpty())
243             return "";
244
245         java.io.StringWriter JavaDoc sw = new java.io.StringWriter JavaDoc(nodes.size() * 128);
246         try {
247             for (Iterator JavaDoc i = nodes.iterator(); i.hasNext();) {
248                 Object JavaDoc node = i.next();
249                 if (node instanceof Element)
250                     OUTPUT.output((Element)node, sw);
251                 else if (node instanceof Attribute)
252                     OUTPUT.output((Attribute)node, sw);
253                 else if (node instanceof String JavaDoc)
254                     sw.write(OUTPUT.escapeElementEntities(node.toString()));
255                 else if (node instanceof Text)
256                     OUTPUT.output((Text)node, sw);
257                 else if (node instanceof Document)
258                     OUTPUT.output((Document)node, sw);
259                 else if (node instanceof ProcessingInstruction)
260                     OUTPUT.output((ProcessingInstruction)node, sw);
261                 else if (node instanceof Comment)
262                     OUTPUT.output((Comment)node, sw);
263                 else if (node instanceof CDATA)
264                     OUTPUT.output((CDATA)node, sw);
265                 else if (node instanceof DocType)
266                     OUTPUT.output((DocType)node, sw);
267                 else if (node instanceof EntityRef)
268                     OUTPUT.output((EntityRef)node, sw);
269                 else
270                     throw new TemplateModelException(node.getClass().getName() + " is not a core JDOM class");
271             }
272         } catch (IOException JavaDoc e) {
273             throw new TemplateModelException(e.getMessage());
274         }
275         return sw.toString();
276     }
277
278
279     /**
280      * Provides node list traversal as well as special functions: filtering by name,
281      * filtering by node type, shallow-copying, and duplicate removal.
282      * While not as powerful as the full XPath support built into the
283      * {@link #exec(List)} method, it does not require the external Jaxen
284      * library to be present at run time. Below are listed the recognized keys.
285      * In key descriptions, "applicable to this-and-that node type" means that if
286      * a key is applied to a node list that contains a node of non-applicable type
287      * a TemplateMethodModel will be thrown. However, you can use <tt>_ftype</tt>
288      * key to explicitly filter out undesired node types prior to applying the
289      * restricted-applicability key. Also "current nodes" means nodes contained in this
290      * set.
291      * <ul>
292      * <li><tt>*</tt> or <tt>_children</tt>: all direct element children of current nodes (non-recursive). Applicable
293      * to element and document nodes.</li>
294      * <li><tt>@*</tt> or <tt>_attributes</tt>: all attributes of current nodes. Applicable to elements only.</li>
295      * <li><tt>_content</tt> the complete content of current nodes (non-recursive).
296      * Applicable to elements and documents.</li>
297      * <li><tt>_text</tt>: the text of current nodes, one string per node (non-recursive).
298      * Applicable to elements, attributes, comments, processing instructions (returns its data)
299      * and CDATA sections. The reserved XML characters ('&lt;' and '&amp;') are escaped.</li>
300      * <li><tt>_plaintext</tt>: same as <tt>_text</tt>, but does not escape any characters,
301      * and instead of returning a NodeList returns a SimpleScalar.</li>
302      * <li><tt>_name</tt>: the names of current nodes, one string per node (non-recursive).
303      * Applicable to elements and attributes (returns their local name),
304      * entities, processing instructions (returns its target), doctypes
305      * (returns its public ID)</li>
306      * <li><tt>_qname</tt>: the qualified names of current nodes in <tt>[namespacePrefix:]localName</tt>
307      * form, one string per node (non-recursive). Applicable to elements and attributes</li>
308      * <li><tt>_cname</tt>: the canonical names of current nodes (namespace URI + local name),
309      * one string per node (non-recursive). Applicable to elements and attributes</li>
310      * <li><tt>_nsprefix</tt>: namespace prefixes of current nodes,
311      * one string per node (non-recursive). Applicable to elements and attributes</li>
312      * <li><tt>_nsuri</tt>: namespace URIs of current nodes,
313      * one string per node (non-recursive). Applicable to elements and attributes</li>
314      * <li><tt>_parent</tt>: parent elements of current nodes. Applicable to element, attribute, comment,
315      * entity, processing instruction.</li>
316      * <li><tt>_ancestor</tt>: all ancestors up to root element (recursive) of current nodes. Applicable
317      * to same node types as <tt>_parent</tt>.</li>
318      * <li><tt>_ancestorOrSelf</tt>: all ancestors of current nodes plus current nodes. Applicable
319      * to same node types as <tt>_parent</tt>.</li>
320      * <li><tt>_descendant</tt>: all recursive descendant element children of current nodes. Applicable to
321      * document and element nodes.
322      * <li><tt>_descendantOrSelf</tt>: all recursive descendant element children of current nodes
323      * plus current nodes. Applicable to document and element nodes.
324      * <li><tt>_document</tt>: all documents the current nodes belong to.
325      * Applicable to all nodes except text.
326      * <li><tt>_doctype</tt>: doctypes of the current nodes.
327      * Applicable to document nodes only.
328      * <li><tt>_fname</tt>: is a filter-by-name template method model. When called,
329      * it will yield a node list that contains only those current nodes whose name
330      * matches one of names passed as argument. Attribute names should NOT be prefixed with the
331      * at sign (@). Applicable on all node types, however has no effect on unnamed nodes.</li>
332      * <li><tt>_ftype</tt>: is a filter-by-type template method model. When called,
333      * it will yield a node list that contains only those current nodes whose type matches one
334      * of types passed as argument. You should pass a single string to this method
335      * containing the characters of all types to keep. Valid characters are:
336      * e (Element), a (Attribute), n (Entity), d (Document), t (DocType),
337      * c (Comment), p (ProcessingInstruction), x (text). If the string anywhere contains
338      * the exclamation mark (!), the filter's effect is inverted.</li>
339      * <li><tt>_type</tt>: Returns a one-character String SimpleScalar containing
340      * the typecode of the first node in the node list. Valid characters are:
341      * e (Element), a (Attribute), n (Entity), d (Document), t (DocType),
342      * c (Comment), p (ProcessingInstruction), x (text). If the type of the node
343      * is unknown, returns '?'. If the node list is empty, returns an empty string scalar.</li>
344      * <li><tt>_unique</tt>: a copy of the current nodes that keeps only the
345      * first occurrence of every node, eliminating duplicates. Duplicates can
346      * occur in the node list by applying uptree-traversals <tt>_parent</tt>,
347      * <tt>_ancestor</tt>, <tt>_ancestorOrSelf</tt>, and <tt>_document</tt>.
348      * I.e. <tt>foo._children._parent</tt> will return a node list that has
349      * duplicates of nodes in foo - each node will have the number of occurrences
350      * equal to the number of its children. In these cases, use
351      * <tt>foo._children._parent._unique</tt> to eliminate duplicates. Applicable
352      * to all node types.</li>
353      * <li><tt>_copy</tt>: a copy of the current node list. It is a shallow copy that
354      * shares the underlying node list with this node list, however it has a
355      * separate namespace registry, so it can be used to guarantee that subsequent
356      * changes to the set of registered namespaces does not affect the node lists
357      * that were used to create this node list. Applicable to all node types.</li>
358      * <li><tt>_registerNamespace(prefix, uri)</tt>: register a XML namespace
359      * with the specified prefix and URI for the current node list and all node
360      * lists that are derived from the current node list. After registering,
361      * you can use the <tt>nodelist["prefix:localname"]</tt> or
362      * <tt>nodelist["@prefix:localname"]</tt> syntaxes to reach elements and
363      * attributes whose names are namespace-scoped. Note that the namespace
364      * prefix need not match the actual prefix used by the XML document itself
365      * since namespaces are compared solely by their URI. You can also register
366      * namespaces from Java code using the
367      * {@link #registerNamespace(String, String)} method.
368      * </li>
369      * <li><tt>@attributeName</tt>: named attributes of current nodes. Applicable to
370      * elements, doctypes and processing instructions. On doctypes it supports
371      * attributes <tt>publicId</tt>, <tt>systemId</tt> and <tt>elementName</tt>. On processing
372      * instructions, it supports attributes <tt>target</tt> and <tt>data</tt>, as
373      * well as any other attribute name specified in data as <tt>name="value"</tt> pair.
374      * The attribute nodes for doctype and processing instruction are synthetic, and
375      * as such have no parent. Note, however that <tt>@*</tt> does NOT operate on
376      * doctypes or processing instructions.</li>
377      * <li>any other key: element children of current nodes with name matching the key.
378      * This allows for convenience child traversal in <tt>book.chapter.title</tt> style syntax.
379      * Note that <tt>nodeset.childname</tt> is technically equivalent to
380      * <tt>nodeset._children._fname("childname")</tt>, but is both shorter to write
381      * and evaluates faster. Applicable to document and element nodes.</li>
382      * </ul>
383      * The order of nodes in the resulting set is the order of evaluation of the key
384      * on each node in this set from left to right. Evaluation of the key on a single
385      * node always yields the results in "natural" order (that of the document preorder
386      * traversal), even for uptree traversals. As a consequence, if this node list's nodes
387      * are listed in natural order, applying any of the keys will produce a node list that
388      * is also naturally ordered. As a special case, all node lists that are directly or
389      * indirectly generated from a single Document or Element node through repeated
390      * invocations of this method will be naturally ordered.
391      * @param key a key that identifies a required set of nodes
392      * @return a new NodeListModel that represents the requested set of nodes.
393      */

394     public TemplateModel get(String JavaDoc key)
395     throws
396     TemplateModelException
397     {
398         if (isEmpty())
399             return EMPTY;
400
401         if (key == null || key.length() == 0)
402             throw new TemplateModelException("Invalid key [" + key + "]");
403
404         NodeOperator op = null;
405         NamedNodeOperator nop = null;
406         String JavaDoc name = null;
407
408         switch (key.charAt(0)) {
409             case '@':
410                 {
411                     if (key.length() != 2 || key.charAt(1) != '*') {
412                         // Generic attribute key
413
nop = NAMED_ATTRIBUTE_OP;
414                         name = key.substring(1);
415                     } else
416                         // It is @*
417
op = ALL_ATTRIBUTES_OP;
418
419                     break;
420                 }
421             case '*':
422                 {
423                     if (key.length() == 1)
424                         op = ALL_CHILDREN_OP;
425                     else
426                         // Explicitly disallow any other identifier starting with asterisk
427
throw new TemplateModelException("Invalid key [" + key + "]");
428
429                     break;
430                 }
431             case 'x':
432             case '_':
433                 {
434                     op = (NodeOperator)OPERATIONS.get(key);
435                     if (op == null) {
436                         // Some special operation?
437
Integer JavaDoc specop = (Integer JavaDoc)SPECIAL_OPERATIONS.get(key);
438                         if (specop != null) {
439                             switch (specop.intValue()) {
440                                 case SPECIAL_OPERATION_COPY:
441                                 {
442                                     synchronized(namespaces)
443                                     {
444                                         return new NodeListModel(nodes, (Map JavaDoc)((HashMap JavaDoc)namespaces).clone());
445                                     }
446                                 }
447                                 case SPECIAL_OPERATION_UNIQUE:
448                                     return new NodeListModel(removeDuplicates(nodes), namespaces);
449                                 case SPECIAL_OPERATION_FILTER_NAME:
450                                     return new NameFilter();
451                                 case SPECIAL_OPERATION_FILTER_TYPE:
452                                     return new TypeFilter();
453                                 case SPECIAL_OPERATION_QUERY_TYPE:
454                                     return getType();
455                                 case SPECIAL_OPERATION_REGISTER_NAMESPACE:
456                                     return new RegisterNamespace();
457                                 case SPECIAL_OPERATION_PLAINTEXT:
458                                     return getPlainText();
459                             }
460                         }
461                     }
462                     break;
463                 }
464         }
465
466         if (op == null && nop == null) {
467             nop = NAMED_CHILDREN_OP;
468             name = key;
469         }
470
471         List JavaDoc list = null;
472         if (op != null)
473             list = evaluateElementOperation(op, nodes);
474         else {
475             String JavaDoc localName = name;
476             Namespace namespace = Namespace.NO_NAMESPACE;
477             int colon = name.indexOf(':');
478             if (colon != -1) {
479                 localName = name.substring(colon + 1);
480                 String JavaDoc nsPrefix = name.substring(0, colon);
481                 synchronized(namespaces)
482                 {
483                     namespace = (Namespace)namespaces.get(nsPrefix);
484                 }
485                 if (namespace == null) {
486                     if (nsPrefix.equals("xml"))
487                         namespace = Namespace.XML_NAMESPACE;
488                     else
489                         throw new TemplateModelException("Unregistered namespace prefix '" + nsPrefix + "'");
490                 }
491             }
492
493             list = evaluateNamedElementOperation(nop, localName, namespace, nodes);
494         }
495         return createNodeListModel(list, namespaces);
496     }
497
498     private TemplateModel getType()
499     {
500         if (nodes.size() == 0)
501             return new SimpleScalar("");
502         Object JavaDoc firstNode = nodes.get(0);
503         char code;
504         if (firstNode instanceof Element)
505             code = 'e';
506         else if (firstNode instanceof Text || firstNode instanceof String JavaDoc)
507             code = 'x';
508         else if (firstNode instanceof Attribute)
509             code = 'a';
510         else if (firstNode instanceof EntityRef)
511             code = 'n';
512         else if (firstNode instanceof Document)
513             code = 'd';
514         else if (firstNode instanceof DocType)
515             code = 't';
516         else if (firstNode instanceof Comment)
517             code = 'c';
518         else if (firstNode instanceof ProcessingInstruction)
519             code = 'p';
520         else
521             code = '?';
522         return new SimpleScalar(new String JavaDoc(new char[] { code}));
523     }
524
525     private SimpleScalar getPlainText()
526     throws
527     TemplateModelException
528     {
529         List JavaDoc list = evaluateElementOperation((TextOp)OPERATIONS.get("_text"), nodes);
530         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
531         for (Iterator JavaDoc it = list.iterator(); it.hasNext();) {
532             buf.append(it.next());
533         }
534         return new SimpleScalar(buf.toString());
535     }
536
537     public TemplateModelIterator iterator()
538     {
539         return new TemplateModelIterator()
540         {
541             private final Iterator JavaDoc it = nodes.iterator();
542
543             public TemplateModel next()
544             {
545                 return it.hasNext() ? new NodeListModel(it.next(), namespaces) : null;
546             }
547
548             public boolean hasNext()
549             {
550                 return it.hasNext();
551             }
552         };
553     }
554
555     /**
556      * Retrieves the i-th element of the node list.
557      */

558     public TemplateModel get(int i)
559     throws
560     TemplateModelException
561     {
562         try {
563             return new NodeListModel(nodes.get(i), namespaces);
564         } catch (IndexOutOfBoundsException JavaDoc e) {
565             throw new TemplateModelException("Index out of bounds: " + e.getMessage());
566         }
567     }
568     
569     public int size()
570     {
571         return nodes.size();
572     }
573
574     /**
575      * Applies an XPath expression to the node list and returns the resulting node list.
576      * In order for this method to work, your application must have access
577      * <a HREF="http://www.jaxen.org">Jaxen</a> library classes. The
578      * implementation does cache the parsed format of XPath expressions in a weak hash
579      * map, keyed by the string representation of the XPath expression. As the string
580      * object passed as the argument is usually kept in the parsed FreeMarker template,
581      * this ensures that each XPath expression is parsed only once during the lifetime
582      * of the FreeMarker template that contains it.
583      * @param arguments the list of arguments. Must contain exactly one string that is
584      * the XPath expression you wish to apply. The XPath expression can use any namespace
585      * prefixes that were defined using the {@link #registerNamespace(String, String)}
586      * method or the <code>nodelist._registerNamespace(prefix, uri)</code> expression in the
587      * template.
588      * @return a NodeListModel representing the nodes that are the result of application
589      * of the XPath to the current node list.
590      */

591     public Object JavaDoc exec(List JavaDoc arguments)
592     throws
593     TemplateModelException
594     {
595         if (arguments == null || arguments.size() != 1)
596             throw new TemplateModelException("Exactly one argument required for execute() on NodeTemplate");
597
598         String JavaDoc xpathString = (String JavaDoc)arguments.get(0);
599         JDOMXPathEx xpath = null;
600         try
601         {
602             synchronized(XPATH_CACHE)
603             {
604                 xpath = (JDOMXPathEx)XPATH_CACHE.get(xpathString);
605                 if (xpath == null)
606                 {
607                     xpath = new JDOMXPathEx(xpathString);
608                     XPATH_CACHE.put(xpathString, xpath);
609                 }
610             }
611             return createNodeListModel(xpath.selectNodes(nodes, namespaces), namespaces);
612         }
613         catch(Exception JavaDoc e)
614         {
615             throw new TemplateModelException("Could not evaulate XPath expression " + xpathString, e);
616         }
617     }
618
619     /**
620      * Registers an XML namespace with this node list. Once registered, you can
621      * refer to the registered namespace using its prefix in the
622      * {@link #get(String)} method from this node list and all other
623      * node lists that are derived from this node list. Use the
624      * <tt>nodelist["prefix:localname"]</tt> or the
625      * <tt>nodelist["@prefix:localname"]</tt> syntax to reach elements and
626      * attributes whose names are namespace-scoped. Note that the namespace
627      * prefix need not match the actual prefix used by the XML document itself
628      * since namespaces are compared solely by their URI. You can also register
629      * namespaces during template evaluation using the
630      * <tt>nodelist._registerNamespace(prefix, uri)</tt> syntax in the template.
631      * This mechanism is completely independent from the namespace declarations
632      * in the XML document itself; its purpose is to give you an easy way
633      * to refer to namespace-scoped elements in {@link #get(String)} and
634      * in XPath expressions passed to {@link #exec(List)}. Note also that
635      * the namespace prefix registry is shared among all node lists that
636      * are created from a single node list - modifying the registry in one
637      * affects all others as well. If you want to obtain a namespace
638      * "detached" copy of the node list, use the <code>_copy</code> key on
639      * it (or call <code>nodeList.get("_copy")</code> directly from your
640      * Java code. The returned node list has all the namespaces that the
641      * original node list has, but they can be manipulated independently
642      * thereon.
643      */

644     public void registerNamespace(String JavaDoc prefix, String JavaDoc uri)
645     {
646         synchronized(namespaces)
647         {
648             namespaces.put(prefix, Namespace.getNamespace(prefix, uri));
649         }
650     }
651
652     private interface NodeOperator {
653         List JavaDoc operate(Object JavaDoc node)
654         throws
655         TemplateModelException;
656     }
657
658     private interface NamedNodeOperator {
659         List JavaDoc operate(Object JavaDoc node, String JavaDoc localName, Namespace namespace)
660         throws
661         TemplateModelException;
662     }
663
664     private static final class AllChildrenOp implements NodeOperator {
665         public List JavaDoc operate(Object JavaDoc node)
666         {
667             if (node instanceof Element)
668                 return((Element)node).getChildren();
669             else if (node instanceof Document) {
670                 Element root = ((Document)node).getRootElement();
671                 return root == null ? Collections.EMPTY_LIST : Collections12.singletonList(root);
672             }
673  // With 2.1 semantics it makes more sense to just return a null and let the core
674
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
675
return null;
676 /*
677             else
678                 throw new TemplateModelException("_allChildren can not be applied on " + node.getClass());
679 */

680         }
681     }
682
683     private static final class NamedChildrenOp implements NamedNodeOperator {
684         public List JavaDoc operate(Object JavaDoc node, String JavaDoc localName, Namespace namespace)
685         {
686             if (node instanceof Element) {
687                 return((Element)node).getChildren(localName, namespace);
688             } else if (node instanceof Document) {
689                 Element root = ((Document)node).getRootElement();
690                 if (root != null &&
691                     root.getName().equals(localName) &&
692                     root.getNamespaceURI().equals(namespace.getURI())) {
693                     return Collections12.singletonList(root);
694                 } else
695                     return Collections.EMPTY_LIST;
696             }
697  // With 2.1 semantics it makes more sense to just return a null and let the core
698
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
699
return null;
700  /*
701             else
702                 throw new TemplateModelException("_namedChildren can not be applied on " + node.getClass());
703 */

704         }
705     }
706
707     private static final class AllAttributesOp implements NodeOperator {
708         public List JavaDoc operate(Object JavaDoc node)
709         {
710  // With 2.1 semantics it makes more sense to just return a null and let the core
711
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
712
if (!(node instanceof Element)) {
713                 return null;
714             }
715             return ((Element)node).getAttributes();
716 /*
717             else
718                 throw new TemplateModelException("_allAttributes can not be applied on " + node.getClass());
719 */

720         }
721     }
722
723     private static final class NamedAttributeOp implements NamedNodeOperator {
724         public List JavaDoc operate(Object JavaDoc node, String JavaDoc localName, Namespace namespace)
725         {
726             Attribute attr = null;
727             if (node instanceof Element) {
728                 Element element = (Element)node;
729                 attr = element.getAttribute(localName, namespace);
730             } else if (node instanceof ProcessingInstruction) {
731                 ProcessingInstruction pi = (ProcessingInstruction)node;
732                 if ("target".equals(localName))
733                     attr = new Attribute("target", pi.getTarget());
734                 else if ("data".equals(localName))
735                     attr = new Attribute("data", pi.getData());
736                 else
737                     attr = new Attribute(localName, pi.getValue(localName));
738             } else if (node instanceof DocType) {
739                 DocType doctype = (DocType)node;
740                 if ("publicId".equals(localName))
741                     attr = new Attribute("publicId", doctype.getPublicID());
742                 else if ("systemId".equals(localName))
743                     attr = new Attribute("systemId", doctype.getSystemID());
744                 else if ("elementName".equals(localName))
745                     attr = new Attribute("elementName", doctype.getElementName());
746             }
747             // With 2.1 semantics it makes more sense to just return a null and let the core
748
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
749
else {
750                 return null;
751             }
752 /*
753             else
754                 throw new TemplateModelException("_allAttributes can not be applied on " + node.getClass());
755 */

756             return attr == null ? Collections.EMPTY_LIST : Collections12.singletonList(attr);
757         }
758     }
759
760     private static final class NameOp implements NodeOperator {
761         public List JavaDoc operate(Object JavaDoc node)
762         {
763             if (node instanceof Element)
764                 return Collections12.singletonList(((Element)node).getName());
765             else if (node instanceof Attribute)
766                 return Collections12.singletonList(((Attribute)node).getName());
767             else if (node instanceof EntityRef)
768                 return Collections12.singletonList(((EntityRef)node).getName());
769             else if (node instanceof ProcessingInstruction)
770                 return Collections12.singletonList(((ProcessingInstruction)node).getTarget());
771             else if (node instanceof DocType)
772                 return Collections12.singletonList(((DocType)node).getPublicID());
773             else
774                 return null;
775             // With 2.1 semantics it makes more sense to just return a null and let the core
776
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
777
// throw new TemplateModelException("_name can not be applied on " + node.getClass());
778
}
779     }
780
781     private static final class QNameOp implements NodeOperator {
782         public List JavaDoc operate(Object JavaDoc node)
783         {
784             if (node instanceof Element)
785                 return Collections12.singletonList(((Element)node).getQualifiedName());
786             else if (node instanceof Attribute)
787                 return Collections12.singletonList(((Attribute)node).getQualifiedName());
788             // With 2.1 semantics it makes more sense to just return a null and let the core
789
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
790
return null;
791 // throw new TemplateModelException("_qname can not be applied on " + node.getClass());
792
}
793     }
794
795     private static final class NamespaceUriOp implements NodeOperator {
796         public List JavaDoc operate(Object JavaDoc node)
797         {
798             if (node instanceof Element)
799                 return Collections12.singletonList(((Element)node).getNamespace().getURI());
800             else if (node instanceof Attribute)
801                 return Collections12.singletonList(((Attribute)node).getNamespace().getURI());
802             // With 2.1 semantics it makes more sense to just return a null and let the core
803
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
804
return null;
805 // throw new TemplateModelException("_nsuri can not be applied on " + node.getClass());
806
}
807     }
808
809     private static final class NamespacePrefixOp implements NodeOperator {
810         public List JavaDoc operate(Object JavaDoc node)
811         {
812             if (node instanceof Element)
813                 return Collections12.singletonList(((Element)node).getNamespace().getPrefix());
814             else if (node instanceof Attribute)
815                 return Collections12.singletonList(((Attribute)node).getNamespace().getPrefix());
816             // With 2.1 semantics it makes more sense to just return a null and let the core
817
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
818
return null;
819 // throw new TemplateModelException("_nsprefix can not be applied on " + node.getClass());
820
}
821     }
822
823     private static final class CanonicalNameOp implements NodeOperator {
824         public List JavaDoc operate(Object JavaDoc node)
825         {
826             if (node instanceof Element)
827             {
828                 Element element = (Element)node;
829                 return Collections12.singletonList(element.getNamespace().getURI() + element.getName());
830             }
831             else if (node instanceof Attribute)
832             {
833                 Attribute attribute = (Attribute)node;
834                 return Collections12.singletonList(attribute.getNamespace().getURI() + attribute.getName());
835             }
836             // With 2.1 semantics it makes more sense to just return a null and let the core
837
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
838
return null;
839 // throw new TemplateModelException("_cname can not be applied on " + node.getClass());
840
}
841     }
842
843
844     private static final Element getParent(Object JavaDoc node)
845     {
846         if (node instanceof Element)
847             return((Element)node).getParent();
848         else if (node instanceof Attribute)
849             return((Attribute)node).getParent();
850         else if (node instanceof Text)
851             return((Text)node).getParent();
852         else if (node instanceof ProcessingInstruction)
853             return((ProcessingInstruction)node).getParent();
854         else if (node instanceof Comment)
855             return((Comment)node).getParent();
856         else if (node instanceof EntityRef)
857             return((EntityRef)node).getParent();
858         else
859             // With 2.1 semantics it makes more sense to just return a null and let the core
860
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
861
return null;
862 // throw new TemplateModelException("_parent can not be applied on " + node.getClass());
863
}
864
865     private static final class ParentOp implements NodeOperator {
866         public List JavaDoc operate(Object JavaDoc node)
867         {
868             Element parent = getParent(node);
869             return parent == null ? Collections.EMPTY_LIST : Collections12.singletonList(parent);
870         }
871     }
872
873     private static final class AncestorOp implements NodeOperator {
874         public List JavaDoc operate(Object JavaDoc node)
875         {
876             Element parent = getParent(node);
877             if (parent == null) return Collections.EMPTY_LIST;
878             LinkedList JavaDoc list = new LinkedList JavaDoc();
879             do {
880                 list.addFirst(parent);
881                 parent = parent.getParent();
882             }
883             while (parent != null);
884             return list;
885         }
886     }
887
888     private static final class AncestorOrSelfOp implements NodeOperator {
889         public List JavaDoc operate(Object JavaDoc node)
890         {
891             Element parent = getParent(node);
892             if (parent == null) return Collections12.singletonList(node);
893             LinkedList JavaDoc list = new LinkedList JavaDoc();
894             list.addFirst(node);
895             do {
896                 list.addFirst(parent);
897                 parent = parent.getParent();
898             }
899             while (parent != null);
900             return list;
901         }
902     }
903
904     private static class DescendantOp implements NodeOperator {
905         public List JavaDoc operate(Object JavaDoc node)
906         {
907             LinkedList JavaDoc list = new LinkedList JavaDoc();
908             if (node instanceof Element) {
909                 addChildren((Element)node, list);
910             }
911             else if (node instanceof Document) {
912                 Element root = ((Document)node).getRootElement();
913                 list.add(root);
914                 addChildren(root, list);
915             }
916             else
917                 // With 2.1 semantics it makes more sense to just return a null and let the core
918
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
919
return null;
920 // throw new TemplateModelException("_descendant can not be applied on " + node.getClass());
921

922             return list;
923         }
924
925         private void addChildren(Element element, List JavaDoc list)
926         {
927             List JavaDoc children = element.getChildren();
928             Iterator JavaDoc it = children.iterator();
929             while (it.hasNext()) {
930                 Element child = (Element)it.next();
931                 list.add(child);
932                 addChildren(child, list);
933             }
934         }
935     }
936
937     private static final class DescendantOrSelfOp extends DescendantOp {
938         public List JavaDoc operate(Object JavaDoc node)
939         {
940             LinkedList JavaDoc list = (LinkedList JavaDoc)super.operate(node);
941             list.addFirst(node);
942             return list;
943         }
944     }
945
946     private static final class DocumentOp implements NodeOperator {
947         public List JavaDoc operate(Object JavaDoc node)
948         {
949             Document doc = null;
950             if (node instanceof Element)
951                 doc = ((Element)node).getDocument();
952             else if (node instanceof Attribute) {
953                 Element parent = ((Attribute)node).getParent();
954                 doc = parent == null ? null : parent.getDocument();
955             } else if (node instanceof Text) {
956                 Element parent = ((Text)node).getParent();
957                 doc = parent == null ? null : parent.getDocument();
958             } else if (node instanceof Document)
959                 doc = (Document)node;
960             else if (node instanceof ProcessingInstruction)
961                 doc = ((ProcessingInstruction)node).getDocument();
962             else if (node instanceof EntityRef)
963                 doc = ((EntityRef)node).getDocument();
964             else if (node instanceof Comment)
965                 doc = ((Comment)node).getDocument();
966             else
967                 // With 2.1 semantics it makes more sense to just return a null and let the core
968
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
969
return null;
970 // throw new TemplateModelException("_document can not be applied on " + node.getClass());
971

972             return doc == null ? Collections.EMPTY_LIST : Collections12.singletonList(doc);
973         }
974     }
975
976     private static final class DocTypeOp implements NodeOperator {
977         public List JavaDoc operate(Object JavaDoc node)
978         {
979             if (node instanceof Document) {
980                 DocType doctype = ((Document)node).getDocType();
981                 return doctype == null ? Collections.EMPTY_LIST : Collections12.singletonList(doctype);
982             } else
983                 // With 2.1 semantics it makes more sense to just return a null and let the core
984
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
985
return null;
986 // throw new TemplateModelException("_doctype can not be applied on " + node.getClass());
987
}
988     }
989
990     private static final class ContentOp implements NodeOperator {
991         public List JavaDoc operate(Object JavaDoc node)
992         {
993             if (node instanceof Element)
994                 return((Element)node).getContent();
995             else if (node instanceof Document)
996                 return((Document)node).getContent();
997             // With 2.1 semantics it makes more sense to just return a null and let the core
998
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
999
return null;
1000// throw new TemplateModelException("_content can not be applied on " + node.getClass());
1001
}
1002    }
1003
1004    private static final class TextOp implements NodeOperator {
1005        public List JavaDoc operate(Object JavaDoc node)
1006        {
1007            if (node instanceof Element)
1008                return Collections12.singletonList(((Element)node).getTextTrim());
1009            if (node instanceof Attribute)
1010                return Collections12.singletonList(((Attribute)node).getValue());
1011            if (node instanceof CDATA)
1012                return Collections12.singletonList(((CDATA)node).getText());
1013            if (node instanceof Comment)
1014                return Collections12.singletonList(((Comment)node).getText());
1015            if (node instanceof ProcessingInstruction)
1016                return Collections12.singletonList(((ProcessingInstruction)node).getData());
1017            // With 2.1 semantics it makes more sense to just return a null and let the core
1018
// throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
1019
return null;
1020// throw new TemplateModelException("_text can not be applied on " + node.getClass());
1021
}
1022    }
1023
1024    private static final List JavaDoc evaluateElementOperation(NodeOperator op, List JavaDoc nodes)
1025    throws
1026    TemplateModelException
1027    {
1028        int s = nodes.size();
1029        List JavaDoc[] lists = new List JavaDoc[s];
1030        int l = 0;
1031        {
1032            int i = 0;
1033            Iterator JavaDoc it = nodes.iterator();
1034            while (it.hasNext()) {
1035                List JavaDoc list = op.operate(it.next());
1036                if (list != null) {
1037                    lists[i++] = list;
1038                    l += list.size();
1039                }
1040            }
1041        }
1042        List JavaDoc retval = new ArrayList JavaDoc(l);
1043        for (int i = 0; i < s; ++i) {
1044            if (lists[i] != null) {
1045                retval.addAll(lists[i]);
1046            }
1047        }
1048        return retval;
1049    }
1050
1051    private static final List JavaDoc evaluateNamedElementOperation(NamedNodeOperator op, String JavaDoc localName, Namespace namespace, List JavaDoc nodes)
1052    throws
1053    TemplateModelException
1054    {
1055        int s = nodes.size();
1056        List JavaDoc[] lists = new List JavaDoc[s];
1057        int l = 0;
1058        {
1059            int i = 0;
1060            Iterator JavaDoc it = nodes.iterator();
1061            while (it.hasNext()) {
1062                List JavaDoc list = op.operate(it.next(), localName, namespace);
1063                lists[i++] = list;
1064                l += list.size();
1065            }
1066        }
1067        List JavaDoc retval = new ArrayList JavaDoc(l);
1068        for (int i = 0; i < s; ++i)
1069            retval.addAll(lists[i]);
1070        return retval;
1071    }
1072
1073    private static final List JavaDoc removeDuplicates(List JavaDoc list)
1074    {
1075        int s = list.size();
1076        ArrayList JavaDoc ulist = new ArrayList JavaDoc(s);
1077        Set JavaDoc set = new HashSet JavaDoc(s * 4 / 3, .75f);
1078        Iterator JavaDoc it = list.iterator();
1079        while (it.hasNext()) {
1080            Object JavaDoc o = it.next();
1081            if (set.add(o))
1082                ulist.add(o);
1083        }
1084        ulist.trimToSize();
1085        return ulist;
1086    }
1087
1088    private static final Map JavaDoc createOperations()
1089    {
1090        Map JavaDoc map = new HashMap JavaDoc();
1091
1092        map.put("_ancestor", new AncestorOp());
1093        map.put("_ancestorOrSelf", new AncestorOrSelfOp());
1094        map.put("_attributes", ALL_ATTRIBUTES_OP);
1095        map.put("_children", ALL_CHILDREN_OP);
1096        map.put("_cname", new CanonicalNameOp());
1097        map.put("_content", new ContentOp());
1098        map.put("_descendant", new DescendantOp());
1099        map.put("_descendantOrSelf", new DescendantOrSelfOp());
1100        map.put("_document", new DocumentOp());
1101        map.put("_doctype", new DocTypeOp());
1102        map.put("_name", new NameOp());
1103        map.put("_nsprefix", new NamespacePrefixOp());
1104        map.put("_nsuri", new NamespaceUriOp());
1105        map.put("_parent", new ParentOp());
1106        map.put("_qname", new QNameOp());
1107        map.put("_text", new TextOp());
1108
1109        return map;
1110    }
1111
1112    private static final Map JavaDoc createSpecialOperations()
1113    {
1114        Map JavaDoc map = new HashMap JavaDoc();
1115
1116        Integer JavaDoc copy = new Integer JavaDoc(SPECIAL_OPERATION_COPY);
1117        Integer JavaDoc unique = new Integer JavaDoc(SPECIAL_OPERATION_UNIQUE);
1118        Integer JavaDoc fname = new Integer JavaDoc(SPECIAL_OPERATION_FILTER_NAME);
1119        Integer JavaDoc ftype = new Integer JavaDoc(SPECIAL_OPERATION_FILTER_TYPE);
1120        Integer JavaDoc type = new Integer JavaDoc(SPECIAL_OPERATION_QUERY_TYPE);
1121        Integer JavaDoc regns = new Integer JavaDoc(SPECIAL_OPERATION_REGISTER_NAMESPACE);
1122        Integer JavaDoc plaintext = new Integer JavaDoc(SPECIAL_OPERATION_PLAINTEXT);
1123
1124        map.put("_copy", copy);
1125        map.put("_unique", unique);
1126        map.put("_fname", fname);
1127        map.put("_ftype", ftype);
1128        map.put("_type", type);
1129        map.put("_registerNamespace", regns);
1130        map.put("_plaintext", plaintext);
1131
1132        // These are in for backward compatibility
1133
map.put("x_copy", copy);
1134        map.put("x_unique", unique);
1135        map.put("x_fname", fname);
1136        map.put("x_ftype", ftype);
1137        map.put("x_type", type);
1138
1139        return map;
1140    }
1141
1142    private final class RegisterNamespace implements TemplateMethodModel {
1143        public boolean isEmpty()
1144        {
1145            return false;
1146        }
1147
1148        public Object JavaDoc exec(List JavaDoc arguments)
1149        throws
1150        TemplateModelException
1151        {
1152            if (arguments.size() != 2)
1153                throw new TemplateModelException("_registerNamespace(prefix, uri) requires two arguments");
1154
1155            registerNamespace((String JavaDoc)arguments.get(0), (String JavaDoc)arguments.get(1));
1156
1157            return TemplateScalarModel.EMPTY_STRING;
1158        }
1159    }
1160
1161    private final class NameFilter implements TemplateMethodModel {
1162        public boolean isEmpty()
1163        {
1164            return false;
1165        }
1166
1167        public Object JavaDoc exec(List JavaDoc arguments)
1168        {
1169            Set JavaDoc names = new HashSet JavaDoc(arguments);
1170            List JavaDoc list = new LinkedList JavaDoc(nodes);
1171            Iterator JavaDoc it = list.iterator();
1172            while (it.hasNext()) {
1173                Object JavaDoc node = it.next();
1174                String JavaDoc name = null;
1175                if (node instanceof Element)
1176                    name = ((Element)node).getName();
1177                else if (node instanceof Attribute)
1178                    name = ((Attribute)node).getName();
1179                else if (node instanceof ProcessingInstruction)
1180                    name = ((ProcessingInstruction)node).getTarget();
1181                else if (node instanceof EntityRef)
1182                    name = ((EntityRef)node).getName();
1183                else if (node instanceof DocType)
1184                    name = ((DocType)node).getPublicID();
1185
1186                if (name == null || !names.contains(name))
1187                    it.remove();
1188            }
1189            return createNodeListModel(list, namespaces);
1190        }
1191    }
1192
1193    private final class TypeFilter implements TemplateMethodModel {
1194        public boolean isEmpty()
1195        {
1196            return false;
1197        }
1198
1199        public Object JavaDoc exec(List JavaDoc arguments)
1200        throws
1201        TemplateModelException
1202        {
1203            if (arguments == null || arguments.size() == 0)
1204                throw new TemplateModelException("_type expects exactly one argument");
1205            String JavaDoc arg = (String JavaDoc)arguments.get(0);
1206            boolean invert = arg.indexOf('!') != -1;
1207            // NOTE: true in each of these variables means 'remove', not 'keep'
1208
// This is so we don't invert their values in the loop. So,
1209
// a is true <--> (a is not present in the string) xor invert.
1210
boolean a = invert != (arg.indexOf('a') == -1);
1211            boolean c = invert != (arg.indexOf('c') == -1);
1212            boolean d = invert != (arg.indexOf('d') == -1);
1213            boolean e = invert != (arg.indexOf('e') == -1);
1214            boolean n = invert != (arg.indexOf('n') == -1);
1215            boolean p = invert != (arg.indexOf('p') == -1);
1216            boolean t = invert != (arg.indexOf('t') == -1);
1217            boolean x = invert != (arg.indexOf('x') == -1);
1218
1219            LinkedList JavaDoc list = new LinkedList JavaDoc(nodes);
1220            Iterator JavaDoc it = list.iterator();
1221            while (it.hasNext()) {
1222                Object JavaDoc node = it.next();
1223                if ((node instanceof Element && e)
1224                    || (node instanceof Attribute && a)
1225                    || (node instanceof String JavaDoc && x)
1226                    || (node instanceof Text && x)
1227                    || (node instanceof ProcessingInstruction && p)
1228                    || (node instanceof Comment && c)
1229                    || (node instanceof EntityRef && n)
1230                    || (node instanceof Document && d)
1231                    || (node instanceof DocType && t))
1232                    it.remove();
1233            }
1234            return createNodeListModel(list, namespaces);
1235        }
1236    }
1237
1238    /**
1239     * Loads a template from a file passed as the first argument, loads an XML
1240     * document from the standard input, passes it to the template as variable
1241     * <tt>document</tt> and writes the result of template processing to
1242     * standard output.
1243     */

1244    public static void main(String JavaDoc[] args)
1245    throws
1246    Exception JavaDoc
1247    {
1248        org.jdom.input.SAXBuilder builder = new org.jdom.input.SAXBuilder();
1249        Document document = builder.build(System.in);
1250        SimpleHash model = new SimpleHash();
1251        model.put("document", new NodeListModel(document));
1252        FileReader JavaDoc fr = new FileReader JavaDoc(args[0]);
1253        Template template = new Template(args[0], fr);
1254        Writer JavaDoc w = new java.io.OutputStreamWriter JavaDoc(System.out);
1255        template.process(model, w);
1256        w.flush();
1257        w.close();
1258    }
1259
1260    private static final class AttributeXMLOutputter extends XMLOutputter {
1261        public void output(Attribute attribute, Writer JavaDoc out)
1262        throws
1263        IOException JavaDoc
1264        {
1265            out.write(" ");
1266            out.write(attribute.getQualifiedName());
1267            out.write("=");
1268
1269            out.write("\"");
1270            out.write(escapeAttributeEntities(attribute.getValue()));
1271            out.write("\"");
1272        }
1273    }
1274
1275    private static final class JDOMXPathEx
1276    extends
1277        JDOMXPath
1278    {
1279        JDOMXPathEx(String JavaDoc path)
1280        throws
1281            JaxenException
1282        {
1283            super(path);
1284        }
1285
1286        public List JavaDoc selectNodes(Object JavaDoc object, Map JavaDoc namespaces)
1287        throws
1288            JaxenException
1289        {
1290            Context context = getContext(object);
1291            context.getContextSupport().setNamespaceContext(new NamespaceContextImpl(namespaces));
1292            return selectNodesForContext(context);
1293        }
1294
1295        private static final class NamespaceContextImpl
1296        implements
1297            NamespaceContext
1298        {
1299            private final Map JavaDoc namespaces;
1300            
1301            NamespaceContextImpl(Map JavaDoc namespaces)
1302            {
1303                this.namespaces = namespaces;
1304            }
1305            
1306            public String JavaDoc translateNamespacePrefixToUri(String JavaDoc prefix)
1307            {
1308                // Empty prefix always maps to empty URL in XPath
1309
if(prefix.length() == 0)
1310                {
1311                    return prefix;
1312                }
1313                synchronized(namespaces)
1314                {
1315                    Namespace ns = (Namespace)namespaces.get(prefix);
1316                    return ns == null ? null : ns.getURI();
1317                }
1318            }
1319        }
1320    }
1321}
1322
Popular Tags