Source Code Cross Referenced for NodeListModel.java in  » Template-Engine » freemarker-2.3.10 » freemarker » ext » jdom » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Template Engine » freemarker 2.3.10 » freemarker.ext.jdom 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2003 The Visigoth Software Society. All rights
0003:         * reserved.
0004:         *
0005:         * Redistribution and use in source and binary forms, with or without
0006:         * modification, are permitted provided that the following conditions
0007:         * are met:
0008:         *
0009:         * 1. Redistributions of source code must retain the above copyright
0010:         *    notice, this list of conditions and the following disclaimer.
0011:         *
0012:         * 2. Redistributions in binary form must reproduce the above copyright
0013:         *    notice, this list of conditions and the following disclaimer in
0014:         *    the documentation and/or other materials provided with the
0015:         *    distribution.
0016:         *
0017:         * 3. The end-user documentation included with the redistribution, if
0018:         *    any, must include the following acknowledgement:
0019:         *       "This product includes software developed by the
0020:         *        Visigoth Software Society (http://www.visigoths.org/)."
0021:         *    Alternately, this acknowledgement may appear in the software itself,
0022:         *    if and wherever such third-party acknowledgements normally appear.
0023:         *
0024:         * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
0025:         *    project contributors may be used to endorse or promote products derived
0026:         *    from this software without prior written permission. For written
0027:         *    permission, please contact visigoths@visigoths.org.
0028:         *
0029:         * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
0030:         *    nor may "FreeMarker" or "Visigoth" appear in their names
0031:         *    without prior written permission of the Visigoth Software Society.
0032:         *
0033:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0034:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0035:         * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0036:         * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
0037:         * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0038:         * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0039:         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0040:         * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0041:         * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0042:         * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0043:         * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0044:         * SUCH DAMAGE.
0045:         * ====================================================================
0046:         *
0047:         * This software consists of voluntary contributions made by many
0048:         * individuals on behalf of the Visigoth Software Society. For more
0049:         * information on the Visigoth Software Society, please see
0050:         * http://www.visigoths.org/
0051:         */
0052:
0053:        package freemarker.ext.jdom;
0054:
0055:        import java.io.FileReader;
0056:        import java.io.IOException;
0057:        import java.io.Writer;
0058:        import java.util.ArrayList;
0059:        import java.util.Collections;
0060:        import java.util.HashMap;
0061:        import java.util.HashSet;
0062:        import java.util.Iterator;
0063:        import java.util.LinkedList;
0064:        import java.util.List;
0065:        import java.util.Map;
0066:        import java.util.Set;
0067:        import java.util.WeakHashMap;
0068:
0069:        import org.jaxen.Context;
0070:        import org.jaxen.JaxenException;
0071:        import org.jaxen.NamespaceContext;
0072:        import org.jaxen.jdom.JDOMXPath;
0073:        import org.jdom.Attribute;
0074:        import org.jdom.CDATA;
0075:        import org.jdom.Comment;
0076:        import org.jdom.DocType;
0077:        import org.jdom.Document;
0078:        import org.jdom.Element;
0079:        import org.jdom.EntityRef;
0080:        import org.jdom.Namespace;
0081:        import org.jdom.ProcessingInstruction;
0082:        import org.jdom.Text;
0083:        import org.jdom.output.XMLOutputter;
0084:        import freemarker.template.SimpleHash;
0085:        import freemarker.template.SimpleScalar;
0086:        import freemarker.template.Template;
0087:        import freemarker.template.TemplateCollectionModel;
0088:        import freemarker.template.TemplateHashModel;
0089:        import freemarker.template.TemplateMethodModel;
0090:        import freemarker.template.TemplateModel;
0091:        import freemarker.template.TemplateModelException;
0092:        import freemarker.template.TemplateModelIterator;
0093:        import freemarker.template.TemplateScalarModel;
0094:        import freemarker.template.TemplateSequenceModel;
0095:        import freemarker.template.utility.Collections12;
0096:
0097:        /**
0098:         * Provides a template for wrapping JDOM objects. It is capable of storing not only
0099:         * a single JDOM node, but a list of JDOM nodes at once (hence the name).
0100:         * Each node is an instance of any of the core JDOM node classes (except namespaces,
0101:         * which are not supported at the moment), or String for representing text.
0102:         * See individual method documentation for exact details on how the class works. In
0103:         * short:
0104:         * <ul>
0105:         * <li>{@link #getAsString()} will render all contained nodes as XML fragment,</tt>
0106:         * <li>{@link #exec(List)} provides full XPath functionality implemented on top of
0107:         * the <a href="http://www.jaxen.org">Jaxen</a> library,</li>
0108:         * <li>{@link #get(String)} provides node traversal, copying and filtering - somewhat
0109:         * less expressive than XPath, however it does not require the external library and
0110:         * it evaluates somewhat faster</li>
0111:         * <li>being a {@link TemplateCollectionModel} allows to iterate the contained node list, and</li>
0112:         * <li>being a {@link TemplateSequenceModel} allows to access the contained nodes by index and query the node count.</li>
0113:         * </ul>
0114:         * 
0115:         * <p><b>Note:</b> There is a JDOM independent re-implementation of this class:
0116:         *   {@link freemarker.ext.xml.NodeListModel freemarker.ext.xml.NodeListModel}
0117:         * 
0118:         * @deprecated Use {@link freemarker.ext.dom.NodeModel} instead.
0119:         * @author Attila Szegedi
0120:         * @version $Id: NodeListModel.java,v 1.52.2.2 2006/11/14 10:39:58 szegedia Exp $
0121:         */
0122:        public class NodeListModel implements  TemplateHashModel,
0123:                TemplateMethodModel, TemplateCollectionModel,
0124:                TemplateSequenceModel, TemplateScalarModel {
0125:            private static final AttributeXMLOutputter OUTPUT = new AttributeXMLOutputter();
0126:            // A convenience singleton for representing a node list without nodes.
0127:            private static final NodeListModel EMPTY = new NodeListModel(null,
0128:                    false);
0129:
0130:            // Cache of already parsed XPath expressions
0131:            private static final Map XPATH_CACHE = new WeakHashMap();
0132:
0133:            private static final NamedNodeOperator NAMED_CHILDREN_OP = new NamedChildrenOp();
0134:            private static final NamedNodeOperator NAMED_ATTRIBUTE_OP = new NamedAttributeOp();
0135:            private static final NodeOperator ALL_ATTRIBUTES_OP = new AllAttributesOp();
0136:            private static final NodeOperator ALL_CHILDREN_OP = new AllChildrenOp();
0137:            private static final Map OPERATIONS = createOperations();
0138:            private static final Map SPECIAL_OPERATIONS = createSpecialOperations();
0139:            private static final int SPECIAL_OPERATION_COPY = 0;
0140:            private static final int SPECIAL_OPERATION_UNIQUE = 1;
0141:            private static final int SPECIAL_OPERATION_FILTER_NAME = 2;
0142:            private static final int SPECIAL_OPERATION_FILTER_TYPE = 3;
0143:            private static final int SPECIAL_OPERATION_QUERY_TYPE = 4;
0144:            private static final int SPECIAL_OPERATION_REGISTER_NAMESPACE = 5;
0145:            private static final int SPECIAL_OPERATION_PLAINTEXT = 6;
0146:
0147:            // The contained nodes
0148:            private final List nodes;
0149:            private final Map namespaces;
0150:
0151:            /**
0152:             * Creates a node list that holds a single {@link Document} node.
0153:             */
0154:            public NodeListModel(Document document) {
0155:                nodes = document == null ? Collections.EMPTY_LIST
0156:                        : Collections12.singletonList(document);
0157:                namespaces = new HashMap();
0158:            }
0159:
0160:            /**
0161:             * Creates a node list that holds a single {@link Element} node.
0162:             */
0163:            public NodeListModel(Element element) {
0164:                nodes = element == null ? Collections.EMPTY_LIST
0165:                        : Collections12.singletonList(element);
0166:                namespaces = new HashMap();
0167:            }
0168:
0169:            private NodeListModel(Object object, Map namespaces) {
0170:                nodes = object == null ? Collections.EMPTY_LIST : Collections12
0171:                        .singletonList(object);
0172:                this .namespaces = namespaces;
0173:            }
0174:
0175:            /**
0176:             * Creates a node list that holds a list of nodes.
0177:             * @param nodes the list of nodes this template should hold. The created template
0178:             * will copy the passed nodes list, so changes to the passed list will not affect
0179:             * the model.
0180:             */
0181:            public NodeListModel(List nodes) {
0182:                this (nodes, true);
0183:            }
0184:
0185:            /**
0186:             * Creates a node list that holds a list of nodes.
0187:             * @param nodes the list of nodes this template should hold.
0188:             * @param copy if true, the created template will copy the passed nodes list,
0189:             * so changes to the passed list will not affect the model. If false, the model
0190:             * will reference the passed list and will sense changes in it, although no
0191:             * operations on the list will be synchronized.
0192:             */
0193:            public NodeListModel(List nodes, boolean copy) {
0194:                this .nodes = copy && nodes != null ? new ArrayList(nodes)
0195:                        : (nodes == null ? Collections.EMPTY_LIST : nodes);
0196:                namespaces = new HashMap();
0197:            }
0198:
0199:            private NodeListModel(List nodes, Map namespaces) {
0200:                this .nodes = nodes == null ? Collections.EMPTY_LIST : nodes;
0201:                this .namespaces = namespaces;
0202:            }
0203:
0204:            private static final NodeListModel createNodeListModel(List list,
0205:                    Map namespaces) {
0206:                if (list == null || list.isEmpty()) {
0207:                    if (namespaces.isEmpty()) {
0208:                        return EMPTY;
0209:                    } else {
0210:                        return new NodeListModel(Collections.EMPTY_LIST,
0211:                                namespaces);
0212:                    }
0213:                }
0214:                if (list.size() == 1)
0215:                    return new NodeListModel(list.get(0), namespaces);
0216:                return new NodeListModel(list, namespaces);
0217:            }
0218:
0219:            /**
0220:             * Returns true if this model contains no nodes.
0221:             */
0222:            public boolean isEmpty() {
0223:                return nodes.isEmpty();
0224:            }
0225:
0226:            /**
0227:             * This method returns the string resulting from concatenation
0228:             * of string representations of its nodes. Each node is rendered using its XML
0229:             * serialization format, while text (String) is rendered as itself. This greatly
0230:             * simplifies creating XML-transformation templates, as to output a node contained
0231:             * in variable x as XML fragment, you simply write ${x} in the template.
0232:             */
0233:            public String getAsString() throws TemplateModelException {
0234:                if (isEmpty())
0235:                    return "";
0236:
0237:                java.io.StringWriter sw = new java.io.StringWriter(
0238:                        nodes.size() * 128);
0239:                try {
0240:                    for (Iterator i = nodes.iterator(); i.hasNext();) {
0241:                        Object node = i.next();
0242:                        if (node instanceof  Element)
0243:                            OUTPUT.output((Element) node, sw);
0244:                        else if (node instanceof  Attribute)
0245:                            OUTPUT.output((Attribute) node, sw);
0246:                        else if (node instanceof  String)
0247:                            sw.write(OUTPUT.escapeElementEntities(node
0248:                                    .toString()));
0249:                        else if (node instanceof  Text)
0250:                            OUTPUT.output((Text) node, sw);
0251:                        else if (node instanceof  Document)
0252:                            OUTPUT.output((Document) node, sw);
0253:                        else if (node instanceof  ProcessingInstruction)
0254:                            OUTPUT.output((ProcessingInstruction) node, sw);
0255:                        else if (node instanceof  Comment)
0256:                            OUTPUT.output((Comment) node, sw);
0257:                        else if (node instanceof  CDATA)
0258:                            OUTPUT.output((CDATA) node, sw);
0259:                        else if (node instanceof  DocType)
0260:                            OUTPUT.output((DocType) node, sw);
0261:                        else if (node instanceof  EntityRef)
0262:                            OUTPUT.output((EntityRef) node, sw);
0263:                        else
0264:                            throw new TemplateModelException(node.getClass()
0265:                                    .getName()
0266:                                    + " is not a core JDOM class");
0267:                    }
0268:                } catch (IOException e) {
0269:                    throw new TemplateModelException(e.getMessage());
0270:                }
0271:                return sw.toString();
0272:            }
0273:
0274:            /**
0275:             * Provides node list traversal as well as special functions: filtering by name,
0276:             * filtering by node type, shallow-copying, and duplicate removal.
0277:             * While not as powerful as the full XPath support built into the
0278:             * {@link #exec(List)} method, it does not require the external Jaxen
0279:             * library to be present at run time. Below are listed the recognized keys.
0280:             * In key descriptions, "applicable to this-and-that node type" means that if
0281:             * a key is applied to a node list that contains a node of non-applicable type
0282:             * a TemplateMethodModel will be thrown. However, you can use <tt>_ftype</tt>
0283:             * key to explicitly filter out undesired node types prior to applying the
0284:             * restricted-applicability key. Also "current nodes" means nodes contained in this
0285:             * set.
0286:             * <ul>
0287:             *    <li><tt>*</tt> or <tt>_children</tt>: all direct element children of current nodes (non-recursive). Applicable
0288:             *  to element and document nodes.</li>
0289:             *    <li><tt>@*</tt> or <tt>_attributes</tt>: all attributes of current nodes. Applicable to elements only.</li>
0290:             *    <li><tt>_content</tt> the complete content of current nodes (non-recursive).
0291:             *  Applicable to elements and documents.</li>
0292:             *    <li><tt>_text</tt>: the text of current nodes, one string per node (non-recursive).
0293:             *  Applicable to elements, attributes, comments, processing instructions (returns its data)
0294:             *  and CDATA sections. The reserved XML characters ('&lt;' and '&amp;') are escaped.</li>
0295:             *    <li><tt>_plaintext</tt>: same as <tt>_text</tt>, but does not escape any characters,
0296:             *  and instead of returning a NodeList returns a SimpleScalar.</li>
0297:             *    <li><tt>_name</tt>: the names of current nodes, one string per node (non-recursive).
0298:             *  Applicable to elements and attributes (returns their local name), 
0299:             *  entities, processing instructions (returns its target), doctypes 
0300:             * (returns its public ID)</li>
0301:             *    <li><tt>_qname</tt>: the qualified names of current nodes in <tt>[namespacePrefix:]localName</tt>
0302:             * form, one string per node (non-recursive). Applicable to elements and attributes</li>
0303:             *    <li><tt>_cname</tt>: the canonical names of current nodes (namespace URI + local name),
0304:             * one string per node (non-recursive). Applicable to elements and attributes</li>
0305:             *    <li><tt>_nsprefix</tt>: namespace prefixes of current nodes,
0306:             * one string per node (non-recursive). Applicable to elements and attributes</li>
0307:             *    <li><tt>_nsuri</tt>: namespace URIs of current nodes,
0308:             * one string per node (non-recursive). Applicable to elements and attributes</li>
0309:             *    <li><tt>_parent</tt>: parent elements of current nodes. Applicable to element, attribute, comment,
0310:             *  entity, processing instruction.</li>
0311:             *    <li><tt>_ancestor</tt>: all ancestors up to root element (recursive) of current nodes. Applicable
0312:             *  to same node types as <tt>_parent</tt>.</li>
0313:             *    <li><tt>_ancestorOrSelf</tt>: all ancestors of current nodes plus current nodes. Applicable
0314:             *  to same node types as <tt>_parent</tt>.</li>
0315:             *    <li><tt>_descendant</tt>: all recursive descendant element children of current nodes. Applicable to
0316:             *  document and element nodes.
0317:             *    <li><tt>_descendantOrSelf</tt>: all recursive descendant element children of current nodes
0318:             *  plus current nodes. Applicable to document and element nodes.
0319:             *    <li><tt>_document</tt>: all documents the current nodes belong to.
0320:             *  Applicable to all nodes except text.
0321:             *    <li><tt>_doctype</tt>: doctypes of the current nodes.
0322:             *  Applicable to document nodes only.
0323:             *    <li><tt>_fname</tt>: is a filter-by-name template method model. When called,
0324:             *  it will yield a node list that contains only those current nodes whose name
0325:             *  matches one of names passed as argument. Attribute names should NOT be prefixed with the
0326:             *  at sign (@). Applicable on all node types, however has no effect on unnamed nodes.</li>
0327:             *    <li><tt>_ftype</tt>: is a filter-by-type template method model. When called,
0328:             *  it will yield a node list that contains only those current nodes whose type matches one
0329:             *  of types passed as argument. You should pass a single string to this method
0330:             *  containing the characters of all types to keep. Valid characters are:
0331:             *  e (Element), a (Attribute), n (Entity), d (Document), t (DocType),
0332:             *  c (Comment), p (ProcessingInstruction), x (text). If the string anywhere contains
0333:             *  the exclamation mark (!), the filter's effect is inverted.</li>
0334:             *    <li><tt>_type</tt>: Returns a one-character String SimpleScalar containing
0335:             *    the typecode of the first node in the node list. Valid characters are:
0336:             *  e (Element), a (Attribute), n (Entity), d (Document), t (DocType),
0337:             *  c (Comment), p (ProcessingInstruction), x (text). If the type of the node
0338:             *  is unknown, returns '?'. If the node list is empty, returns an empty string scalar.</li>
0339:             *    <li><tt>_unique</tt>: a copy of the current nodes that keeps only the
0340:             *  first occurrence of every node, eliminating duplicates. Duplicates can
0341:             *  occur in the node list by applying uptree-traversals <tt>_parent</tt>,
0342:             *  <tt>_ancestor</tt>, <tt>_ancestorOrSelf</tt>, and <tt>_document</tt>.
0343:             *  I.e. <tt>foo._children._parent</tt> will return a node list that has
0344:             *  duplicates of nodes in foo - each node will have the number of occurrences
0345:             *  equal to the number of its children. In these cases, use
0346:             *  <tt>foo._children._parent._unique</tt> to eliminate duplicates. Applicable
0347:             *  to all node types.</li>
0348:             *    <li><tt>_copy</tt>: a copy of the current node list. It is a shallow copy that
0349:             *  shares the underlying node list with this node list, however it has a
0350:             *  separate namespace registry, so it can be used to guarantee that subsequent
0351:             *  changes to the set of registered namespaces does not affect the node lists
0352:             *  that were used to create this node list. Applicable to all node types.</li>
0353:             *    <li><tt>_registerNamespace(prefix, uri)</tt>: register a XML namespace
0354:             *  with the specified prefix and URI for the current node list and all node
0355:             *  lists that are derived from the current node list. After registering,
0356:             *  you can use the <tt>nodelist["prefix:localname"]</tt> or
0357:             *  <tt>nodelist["@prefix:localname"]</tt> syntaxes to reach elements and
0358:             *  attributes whose names are namespace-scoped. Note that the namespace
0359:             *  prefix need not match the actual prefix used by the XML document itself
0360:             *  since namespaces are compared solely by their URI. You can also register
0361:             *  namespaces from Java code using the
0362:             *  {@link #registerNamespace(String, String)} method.
0363:             * </li>
0364:             *    <li><tt>@attributeName</tt>: named attributes of current nodes. Applicable to
0365:             *  elements, doctypes and processing instructions. On doctypes it supports
0366:             *  attributes <tt>publicId</tt>, <tt>systemId</tt> and <tt>elementName</tt>. On processing
0367:             *  instructions, it supports attributes <tt>target</tt> and <tt>data</tt>, as
0368:             *  well as any other attribute name specified in data as <tt>name="value"</tt> pair.
0369:             *  The attribute nodes for doctype and processing instruction are synthetic, and
0370:             *  as such have no parent. Note, however that <tt>@*</tt> does NOT operate on
0371:             *  doctypes or processing instructions.</li>
0372:             *    <li>any other key: element children of current nodes with name matching the key.
0373:             *  This allows for convenience child traversal in <tt>book.chapter.title</tt> style syntax.
0374:             *  Note that <tt>nodeset.childname</tt> is technically equivalent to
0375:             *  <tt>nodeset._children._fname("childname")</tt>, but is both shorter to write
0376:             *  and evaluates faster. Applicable to document and element nodes.</li>
0377:             * </ul>
0378:             * The order of nodes in the resulting set is the order of evaluation of the key
0379:             * on each node in this set from left to right. Evaluation of the key on a single
0380:             * node always yields the results in "natural" order (that of the document preorder
0381:             * traversal), even for uptree traversals. As a consequence, if this node list's nodes
0382:             * are listed in natural order, applying any of the keys will produce a node list that
0383:             * is also naturally ordered. As a special case, all node lists that are directly or
0384:             * indirectly generated from a single Document or Element node through repeated
0385:             * invocations of this method will be naturally ordered.
0386:             * @param key a key that identifies a required set of nodes
0387:             * @return a new NodeListModel that represents the requested set of nodes.
0388:             */
0389:            public TemplateModel get(String key) throws TemplateModelException {
0390:                if (isEmpty())
0391:                    return EMPTY;
0392:
0393:                if (key == null || key.length() == 0)
0394:                    throw new TemplateModelException("Invalid key [" + key
0395:                            + "]");
0396:
0397:                NodeOperator op = null;
0398:                NamedNodeOperator nop = null;
0399:                String name = null;
0400:
0401:                switch (key.charAt(0)) {
0402:                case '@': {
0403:                    if (key.length() != 2 || key.charAt(1) != '*') {
0404:                        // Generic attribute key
0405:                        nop = NAMED_ATTRIBUTE_OP;
0406:                        name = key.substring(1);
0407:                    } else
0408:                        // It is @*
0409:                        op = ALL_ATTRIBUTES_OP;
0410:
0411:                    break;
0412:                }
0413:                case '*': {
0414:                    if (key.length() == 1)
0415:                        op = ALL_CHILDREN_OP;
0416:                    else
0417:                        // Explicitly disallow any other identifier starting with asterisk
0418:                        throw new TemplateModelException("Invalid key [" + key
0419:                                + "]");
0420:
0421:                    break;
0422:                }
0423:                case 'x':
0424:                case '_': {
0425:                    op = (NodeOperator) OPERATIONS.get(key);
0426:                    if (op == null) {
0427:                        // Some special operation?
0428:                        Integer specop = (Integer) SPECIAL_OPERATIONS.get(key);
0429:                        if (specop != null) {
0430:                            switch (specop.intValue()) {
0431:                            case SPECIAL_OPERATION_COPY: {
0432:                                synchronized (namespaces) {
0433:                                    return new NodeListModel(nodes,
0434:                                            (Map) ((HashMap) namespaces)
0435:                                                    .clone());
0436:                                }
0437:                            }
0438:                            case SPECIAL_OPERATION_UNIQUE:
0439:                                return new NodeListModel(
0440:                                        removeDuplicates(nodes), namespaces);
0441:                            case SPECIAL_OPERATION_FILTER_NAME:
0442:                                return new NameFilter();
0443:                            case SPECIAL_OPERATION_FILTER_TYPE:
0444:                                return new TypeFilter();
0445:                            case SPECIAL_OPERATION_QUERY_TYPE:
0446:                                return getType();
0447:                            case SPECIAL_OPERATION_REGISTER_NAMESPACE:
0448:                                return new RegisterNamespace();
0449:                            case SPECIAL_OPERATION_PLAINTEXT:
0450:                                return getPlainText();
0451:                            }
0452:                        }
0453:                    }
0454:                    break;
0455:                }
0456:                }
0457:
0458:                if (op == null && nop == null) {
0459:                    nop = NAMED_CHILDREN_OP;
0460:                    name = key;
0461:                }
0462:
0463:                List list = null;
0464:                if (op != null)
0465:                    list = evaluateElementOperation(op, nodes);
0466:                else {
0467:                    String localName = name;
0468:                    Namespace namespace = Namespace.NO_NAMESPACE;
0469:                    int colon = name.indexOf(':');
0470:                    if (colon != -1) {
0471:                        localName = name.substring(colon + 1);
0472:                        String nsPrefix = name.substring(0, colon);
0473:                        synchronized (namespaces) {
0474:                            namespace = (Namespace) namespaces.get(nsPrefix);
0475:                        }
0476:                        if (namespace == null) {
0477:                            if (nsPrefix.equals("xml"))
0478:                                namespace = Namespace.XML_NAMESPACE;
0479:                            else
0480:                                throw new TemplateModelException(
0481:                                        "Unregistered namespace prefix '"
0482:                                                + nsPrefix + "'");
0483:                        }
0484:                    }
0485:
0486:                    list = evaluateNamedElementOperation(nop, localName,
0487:                            namespace, nodes);
0488:                }
0489:                return createNodeListModel(list, namespaces);
0490:            }
0491:
0492:            private TemplateModel getType() {
0493:                if (nodes.size() == 0)
0494:                    return new SimpleScalar("");
0495:                Object firstNode = nodes.get(0);
0496:                char code;
0497:                if (firstNode instanceof  Element)
0498:                    code = 'e';
0499:                else if (firstNode instanceof  Text
0500:                        || firstNode instanceof  String)
0501:                    code = 'x';
0502:                else if (firstNode instanceof  Attribute)
0503:                    code = 'a';
0504:                else if (firstNode instanceof  EntityRef)
0505:                    code = 'n';
0506:                else if (firstNode instanceof  Document)
0507:                    code = 'd';
0508:                else if (firstNode instanceof  DocType)
0509:                    code = 't';
0510:                else if (firstNode instanceof  Comment)
0511:                    code = 'c';
0512:                else if (firstNode instanceof  ProcessingInstruction)
0513:                    code = 'p';
0514:                else
0515:                    code = '?';
0516:                return new SimpleScalar(new String(new char[] { code }));
0517:            }
0518:
0519:            private SimpleScalar getPlainText() throws TemplateModelException {
0520:                List list = evaluateElementOperation((TextOp) OPERATIONS
0521:                        .get("_text"), nodes);
0522:                StringBuffer buf = new StringBuffer();
0523:                for (Iterator it = list.iterator(); it.hasNext();) {
0524:                    buf.append(it.next());
0525:                }
0526:                return new SimpleScalar(buf.toString());
0527:            }
0528:
0529:            public TemplateModelIterator iterator() {
0530:                return new TemplateModelIterator() {
0531:                    private final Iterator it = nodes.iterator();
0532:
0533:                    public TemplateModel next() {
0534:                        return it.hasNext() ? new NodeListModel(it.next(),
0535:                                namespaces) : null;
0536:                    }
0537:
0538:                    public boolean hasNext() {
0539:                        return it.hasNext();
0540:                    }
0541:                };
0542:            }
0543:
0544:            /**
0545:             * Retrieves the i-th element of the node list.
0546:             */
0547:            public TemplateModel get(int i) throws TemplateModelException {
0548:                try {
0549:                    return new NodeListModel(nodes.get(i), namespaces);
0550:                } catch (IndexOutOfBoundsException e) {
0551:                    throw new TemplateModelException("Index out of bounds: "
0552:                            + e.getMessage());
0553:                }
0554:            }
0555:
0556:            public int size() {
0557:                return nodes.size();
0558:            }
0559:
0560:            /**
0561:             * Applies an XPath expression to the node list and returns the resulting node list.
0562:             * In order for this method to work, your application must have access
0563:             * <a href="http://www.jaxen.org">Jaxen</a> library classes. The
0564:             * implementation does cache the parsed format of XPath expressions in a weak hash
0565:             * map, keyed by the string representation of the XPath expression. As the string
0566:             * object passed as the argument is usually kept in the parsed FreeMarker template,
0567:             * this ensures that each XPath expression is parsed only once during the lifetime
0568:             * of the FreeMarker template that contains it.
0569:             * @param arguments the list of arguments. Must contain exactly one string that is
0570:             * the XPath expression you wish to apply. The XPath expression can use any namespace
0571:             * prefixes that were defined using the {@link #registerNamespace(String, String)}
0572:             * method or the <code>nodelist._registerNamespace(prefix, uri)</code> expression in the
0573:             * template.
0574:             * @return a NodeListModel representing the nodes that are the result of application
0575:             * of the XPath to the current node list.
0576:             */
0577:            public Object exec(List arguments) throws TemplateModelException {
0578:                if (arguments == null || arguments.size() != 1)
0579:                    throw new TemplateModelException(
0580:                            "Exactly one argument required for execute() on NodeTemplate");
0581:
0582:                String xpathString = (String) arguments.get(0);
0583:                JDOMXPathEx xpath = null;
0584:                try {
0585:                    synchronized (XPATH_CACHE) {
0586:                        xpath = (JDOMXPathEx) XPATH_CACHE.get(xpathString);
0587:                        if (xpath == null) {
0588:                            xpath = new JDOMXPathEx(xpathString);
0589:                            XPATH_CACHE.put(xpathString, xpath);
0590:                        }
0591:                    }
0592:                    return createNodeListModel(xpath.selectNodes(nodes,
0593:                            namespaces), namespaces);
0594:                } catch (Exception e) {
0595:                    throw new TemplateModelException(
0596:                            "Could not evaulate XPath expression "
0597:                                    + xpathString, e);
0598:                }
0599:            }
0600:
0601:            /**
0602:             * Registers an XML namespace with this node list. Once registered, you can
0603:             * refer to the registered namespace using its prefix in the
0604:             * {@link #get(String)} method from this node list and all other
0605:             * node lists that are derived from this node list. Use the
0606:             * <tt>nodelist["prefix:localname"]</tt> or the
0607:             * <tt>nodelist["@prefix:localname"]</tt> syntax to reach elements and
0608:             *  attributes whose names are namespace-scoped. Note that the namespace
0609:             * prefix need not match the actual prefix used by the XML document itself
0610:             * since namespaces are compared solely by their URI. You can also register
0611:             * namespaces during template evaluation using the
0612:             * <tt>nodelist._registerNamespace(prefix, uri)</tt> syntax in the template.
0613:             * This mechanism is completely independent from the namespace declarations
0614:             * in the XML document itself; its purpose is to give you an easy way
0615:             * to refer to namespace-scoped elements in {@link #get(String)} and
0616:             * in XPath expressions passed to {@link #exec(List)}. Note also that
0617:             * the namespace prefix registry is shared among all node lists that
0618:             * are created from a single node list - modifying the registry in one
0619:             * affects all others as well. If you want to obtain a namespace 
0620:             * "detached" copy of the node list, use the <code>_copy</code> key on
0621:             * it (or call <code>nodeList.get("_copy")</code> directly from your
0622:             * Java code. The returned node list has all the namespaces that the
0623:             * original node list has, but they can be manipulated independently
0624:             * thereon.
0625:             */
0626:            public void registerNamespace(String prefix, String uri) {
0627:                synchronized (namespaces) {
0628:                    namespaces.put(prefix, Namespace.getNamespace(prefix, uri));
0629:                }
0630:            }
0631:
0632:            private interface NodeOperator {
0633:                List operate(Object node) throws TemplateModelException;
0634:            }
0635:
0636:            private interface NamedNodeOperator {
0637:                List operate(Object node, String localName, Namespace namespace)
0638:                        throws TemplateModelException;
0639:            }
0640:
0641:            private static final class AllChildrenOp implements  NodeOperator {
0642:                public List operate(Object node) {
0643:                    if (node instanceof  Element)
0644:                        return ((Element) node).getChildren();
0645:                    else if (node instanceof  Document) {
0646:                        Element root = ((Document) node).getRootElement();
0647:                        return root == null ? Collections.EMPTY_LIST
0648:                                : Collections12.singletonList(root);
0649:                    }
0650:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0651:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0652:                    return null;
0653:                    /*            
0654:                     else
0655:                     throw new TemplateModelException("_allChildren can not be applied on " + node.getClass());
0656:                     */
0657:                }
0658:            }
0659:
0660:            private static final class NamedChildrenOp implements 
0661:                    NamedNodeOperator {
0662:                public List operate(Object node, String localName,
0663:                        Namespace namespace) {
0664:                    if (node instanceof  Element) {
0665:                        return ((Element) node).getChildren(localName,
0666:                                namespace);
0667:                    } else if (node instanceof  Document) {
0668:                        Element root = ((Document) node).getRootElement();
0669:                        if (root != null
0670:                                && root.getName().equals(localName)
0671:                                && root.getNamespaceURI().equals(
0672:                                        namespace.getURI())) {
0673:                            return Collections12.singletonList(root);
0674:                        } else
0675:                            return Collections.EMPTY_LIST;
0676:                    }
0677:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0678:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0679:                    return null;
0680:                    /*           
0681:                               else
0682:                                   throw new TemplateModelException("_namedChildren can not be applied on " + node.getClass());
0683:                     */
0684:                }
0685:            }
0686:
0687:            private static final class AllAttributesOp implements  NodeOperator {
0688:                public List operate(Object node) {
0689:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0690:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0691:                    if (!(node instanceof  Element)) {
0692:                        return null;
0693:                    }
0694:                    return ((Element) node).getAttributes();
0695:                    /*
0696:                     else
0697:                     throw new TemplateModelException("_allAttributes can not be applied on " + node.getClass());
0698:                     */
0699:                }
0700:            }
0701:
0702:            private static final class NamedAttributeOp implements 
0703:                    NamedNodeOperator {
0704:                public List operate(Object node, String localName,
0705:                        Namespace namespace) {
0706:                    Attribute attr = null;
0707:                    if (node instanceof  Element) {
0708:                        Element element = (Element) node;
0709:                        attr = element.getAttribute(localName, namespace);
0710:                    } else if (node instanceof  ProcessingInstruction) {
0711:                        ProcessingInstruction pi = (ProcessingInstruction) node;
0712:                        if ("target".equals(localName))
0713:                            attr = new Attribute("target", pi.getTarget());
0714:                        else if ("data".equals(localName))
0715:                            attr = new Attribute("data", pi.getData());
0716:                        else
0717:                            attr = new Attribute(localName, pi
0718:                                    .getValue(localName));
0719:                    } else if (node instanceof  DocType) {
0720:                        DocType doctype = (DocType) node;
0721:                        if ("publicId".equals(localName))
0722:                            attr = new Attribute("publicId", doctype
0723:                                    .getPublicID());
0724:                        else if ("systemId".equals(localName))
0725:                            attr = new Attribute("systemId", doctype
0726:                                    .getSystemID());
0727:                        else if ("elementName".equals(localName))
0728:                            attr = new Attribute("elementName", doctype
0729:                                    .getElementName());
0730:                    }
0731:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0732:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0733:                    else {
0734:                        return null;
0735:                    }
0736:                    /*            
0737:                     else
0738:                     throw new TemplateModelException("_allAttributes can not be applied on " + node.getClass());
0739:                     */
0740:                    return attr == null ? Collections.EMPTY_LIST
0741:                            : Collections12.singletonList(attr);
0742:                }
0743:            }
0744:
0745:            private static final class NameOp implements  NodeOperator {
0746:                public List operate(Object node) {
0747:                    if (node instanceof  Element)
0748:                        return Collections12.singletonList(((Element) node)
0749:                                .getName());
0750:                    else if (node instanceof  Attribute)
0751:                        return Collections12.singletonList(((Attribute) node)
0752:                                .getName());
0753:                    else if (node instanceof  EntityRef)
0754:                        return Collections12.singletonList(((EntityRef) node)
0755:                                .getName());
0756:                    else if (node instanceof  ProcessingInstruction)
0757:                        return Collections12
0758:                                .singletonList(((ProcessingInstruction) node)
0759:                                        .getTarget());
0760:                    else if (node instanceof  DocType)
0761:                        return Collections12.singletonList(((DocType) node)
0762:                                .getPublicID());
0763:                    else
0764:                        return null;
0765:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0766:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0767:                    //                throw new TemplateModelException("_name can not be applied on " + node.getClass());
0768:                }
0769:            }
0770:
0771:            private static final class QNameOp implements  NodeOperator {
0772:                public List operate(Object node) {
0773:                    if (node instanceof  Element)
0774:                        return Collections12.singletonList(((Element) node)
0775:                                .getQualifiedName());
0776:                    else if (node instanceof  Attribute)
0777:                        return Collections12.singletonList(((Attribute) node)
0778:                                .getQualifiedName());
0779:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0780:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0781:                    return null;
0782:                    //            throw new TemplateModelException("_qname can not be applied on " + node.getClass());
0783:                }
0784:            }
0785:
0786:            private static final class NamespaceUriOp implements  NodeOperator {
0787:                public List operate(Object node) {
0788:                    if (node instanceof  Element)
0789:                        return Collections12.singletonList(((Element) node)
0790:                                .getNamespace().getURI());
0791:                    else if (node instanceof  Attribute)
0792:                        return Collections12.singletonList(((Attribute) node)
0793:                                .getNamespace().getURI());
0794:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0795:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0796:                    return null;
0797:                    //            throw new TemplateModelException("_nsuri can not be applied on " + node.getClass());
0798:                }
0799:            }
0800:
0801:            private static final class NamespacePrefixOp implements 
0802:                    NodeOperator {
0803:                public List operate(Object node) {
0804:                    if (node instanceof  Element)
0805:                        return Collections12.singletonList(((Element) node)
0806:                                .getNamespace().getPrefix());
0807:                    else if (node instanceof  Attribute)
0808:                        return Collections12.singletonList(((Attribute) node)
0809:                                .getNamespace().getPrefix());
0810:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0811:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0812:                    return null;
0813:                    //            throw new TemplateModelException("_nsprefix can not be applied on " + node.getClass());
0814:                }
0815:            }
0816:
0817:            private static final class CanonicalNameOp implements  NodeOperator {
0818:                public List operate(Object node) {
0819:                    if (node instanceof  Element) {
0820:                        Element element = (Element) node;
0821:                        return Collections12.singletonList(element
0822:                                .getNamespace().getURI()
0823:                                + element.getName());
0824:                    } else if (node instanceof  Attribute) {
0825:                        Attribute attribute = (Attribute) node;
0826:                        return Collections12.singletonList(attribute
0827:                                .getNamespace().getURI()
0828:                                + attribute.getName());
0829:                    }
0830:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0831:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0832:                    return null;
0833:                    //            throw new TemplateModelException("_cname can not be applied on " + node.getClass());
0834:                }
0835:            }
0836:
0837:            private static final Element getParent(Object node) {
0838:                if (node instanceof  Element)
0839:                    return ((Element) node).getParent();
0840:                else if (node instanceof  Attribute)
0841:                    return ((Attribute) node).getParent();
0842:                else if (node instanceof  Text)
0843:                    return ((Text) node).getParent();
0844:                else if (node instanceof  ProcessingInstruction)
0845:                    return ((ProcessingInstruction) node).getParent();
0846:                else if (node instanceof  Comment)
0847:                    return ((Comment) node).getParent();
0848:                else if (node instanceof  EntityRef)
0849:                    return ((EntityRef) node).getParent();
0850:                else
0851:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0852:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0853:                    return null;
0854:                //            throw new TemplateModelException("_parent can not be applied on " + node.getClass());
0855:            }
0856:
0857:            private static final class ParentOp implements  NodeOperator {
0858:                public List operate(Object node) {
0859:                    Element parent = getParent(node);
0860:                    return parent == null ? Collections.EMPTY_LIST
0861:                            : Collections12.singletonList(parent);
0862:                }
0863:            }
0864:
0865:            private static final class AncestorOp implements  NodeOperator {
0866:                public List operate(Object node) {
0867:                    Element parent = getParent(node);
0868:                    if (parent == null)
0869:                        return Collections.EMPTY_LIST;
0870:                    LinkedList list = new LinkedList();
0871:                    do {
0872:                        list.addFirst(parent);
0873:                        parent = parent.getParent();
0874:                    } while (parent != null);
0875:                    return list;
0876:                }
0877:            }
0878:
0879:            private static final class AncestorOrSelfOp implements  NodeOperator {
0880:                public List operate(Object node) {
0881:                    Element parent = getParent(node);
0882:                    if (parent == null)
0883:                        return Collections12.singletonList(node);
0884:                    LinkedList list = new LinkedList();
0885:                    list.addFirst(node);
0886:                    do {
0887:                        list.addFirst(parent);
0888:                        parent = parent.getParent();
0889:                    } while (parent != null);
0890:                    return list;
0891:                }
0892:            }
0893:
0894:            private static class DescendantOp implements  NodeOperator {
0895:                public List operate(Object node) {
0896:                    LinkedList list = new LinkedList();
0897:                    if (node instanceof  Element) {
0898:                        addChildren((Element) node, list);
0899:                    } else if (node instanceof  Document) {
0900:                        Element root = ((Document) node).getRootElement();
0901:                        list.add(root);
0902:                        addChildren(root, list);
0903:                    } else
0904:                        // With 2.1 semantics it  makes more sense to just return a null and let the core 
0905:                        // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0906:                        return null;
0907:                    //                throw new TemplateModelException("_descendant can not be applied on " + node.getClass());
0908:
0909:                    return list;
0910:                }
0911:
0912:                private void addChildren(Element element, List list) {
0913:                    List children = element.getChildren();
0914:                    Iterator it = children.iterator();
0915:                    while (it.hasNext()) {
0916:                        Element child = (Element) it.next();
0917:                        list.add(child);
0918:                        addChildren(child, list);
0919:                    }
0920:                }
0921:            }
0922:
0923:            private static final class DescendantOrSelfOp extends DescendantOp {
0924:                public List operate(Object node) {
0925:                    LinkedList list = (LinkedList) super .operate(node);
0926:                    list.addFirst(node);
0927:                    return list;
0928:                }
0929:            }
0930:
0931:            private static final class DocumentOp implements  NodeOperator {
0932:                public List operate(Object node) {
0933:                    Document doc = null;
0934:                    if (node instanceof  Element)
0935:                        doc = ((Element) node).getDocument();
0936:                    else if (node instanceof  Attribute) {
0937:                        Element parent = ((Attribute) node).getParent();
0938:                        doc = parent == null ? null : parent.getDocument();
0939:                    } else if (node instanceof  Text) {
0940:                        Element parent = ((Text) node).getParent();
0941:                        doc = parent == null ? null : parent.getDocument();
0942:                    } else if (node instanceof  Document)
0943:                        doc = (Document) node;
0944:                    else if (node instanceof  ProcessingInstruction)
0945:                        doc = ((ProcessingInstruction) node).getDocument();
0946:                    else if (node instanceof  EntityRef)
0947:                        doc = ((EntityRef) node).getDocument();
0948:                    else if (node instanceof  Comment)
0949:                        doc = ((Comment) node).getDocument();
0950:                    else
0951:                        // With 2.1 semantics it  makes more sense to just return a null and let the core 
0952:                        // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0953:                        return null;
0954:                    //                throw new TemplateModelException("_document can not be applied on " + node.getClass());
0955:
0956:                    return doc == null ? Collections.EMPTY_LIST : Collections12
0957:                            .singletonList(doc);
0958:                }
0959:            }
0960:
0961:            private static final class DocTypeOp implements  NodeOperator {
0962:                public List operate(Object node) {
0963:                    if (node instanceof  Document) {
0964:                        DocType doctype = ((Document) node).getDocType();
0965:                        return doctype == null ? Collections.EMPTY_LIST
0966:                                : Collections12.singletonList(doctype);
0967:                    } else
0968:                        // With 2.1 semantics it  makes more sense to just return a null and let the core 
0969:                        // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0970:                        return null;
0971:                    //                throw new TemplateModelException("_doctype can not be applied on " + node.getClass());
0972:                }
0973:            }
0974:
0975:            private static final class ContentOp implements  NodeOperator {
0976:                public List operate(Object node) {
0977:                    if (node instanceof  Element)
0978:                        return ((Element) node).getContent();
0979:                    else if (node instanceof  Document)
0980:                        return ((Document) node).getContent();
0981:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
0982:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
0983:                    return null;
0984:                    //            throw new TemplateModelException("_content can not be applied on " + node.getClass());
0985:                }
0986:            }
0987:
0988:            private static final class TextOp implements  NodeOperator {
0989:                public List operate(Object node) {
0990:                    if (node instanceof  Element)
0991:                        return Collections12.singletonList(((Element) node)
0992:                                .getTextTrim());
0993:                    if (node instanceof  Attribute)
0994:                        return Collections12.singletonList(((Attribute) node)
0995:                                .getValue());
0996:                    if (node instanceof  CDATA)
0997:                        return Collections12.singletonList(((CDATA) node)
0998:                                .getText());
0999:                    if (node instanceof  Comment)
1000:                        return Collections12.singletonList(((Comment) node)
1001:                                .getText());
1002:                    if (node instanceof  ProcessingInstruction)
1003:                        return Collections12
1004:                                .singletonList(((ProcessingInstruction) node)
1005:                                        .getData());
1006:                    // With 2.1 semantics it  makes more sense to just return a null and let the core 
1007:                    // throw an InvalidReferenceException and the template writer can use ?exists etcetera. (JR)
1008:                    return null;
1009:                    //            throw new TemplateModelException("_text can not be applied on " + node.getClass());
1010:                }
1011:            }
1012:
1013:            private static final List evaluateElementOperation(NodeOperator op,
1014:                    List nodes) throws TemplateModelException {
1015:                int s = nodes.size();
1016:                List[] lists = new List[s];
1017:                int l = 0;
1018:                {
1019:                    int i = 0;
1020:                    Iterator it = nodes.iterator();
1021:                    while (it.hasNext()) {
1022:                        List list = op.operate(it.next());
1023:                        if (list != null) {
1024:                            lists[i++] = list;
1025:                            l += list.size();
1026:                        }
1027:                    }
1028:                }
1029:                List retval = new ArrayList(l);
1030:                for (int i = 0; i < s; ++i) {
1031:                    if (lists[i] != null) {
1032:                        retval.addAll(lists[i]);
1033:                    }
1034:                }
1035:                return retval;
1036:            }
1037:
1038:            private static final List evaluateNamedElementOperation(
1039:                    NamedNodeOperator op, String localName,
1040:                    Namespace namespace, List nodes)
1041:                    throws TemplateModelException {
1042:                int s = nodes.size();
1043:                List[] lists = new List[s];
1044:                int l = 0;
1045:                {
1046:                    int i = 0;
1047:                    Iterator it = nodes.iterator();
1048:                    while (it.hasNext()) {
1049:                        List list = op.operate(it.next(), localName, namespace);
1050:                        lists[i++] = list;
1051:                        l += list.size();
1052:                    }
1053:                }
1054:                List retval = new ArrayList(l);
1055:                for (int i = 0; i < s; ++i)
1056:                    retval.addAll(lists[i]);
1057:                return retval;
1058:            }
1059:
1060:            private static final List removeDuplicates(List list) {
1061:                int s = list.size();
1062:                ArrayList ulist = new ArrayList(s);
1063:                Set set = new HashSet(s * 4 / 3, .75f);
1064:                Iterator it = list.iterator();
1065:                while (it.hasNext()) {
1066:                    Object o = it.next();
1067:                    if (set.add(o))
1068:                        ulist.add(o);
1069:                }
1070:                ulist.trimToSize();
1071:                return ulist;
1072:            }
1073:
1074:            private static final Map createOperations() {
1075:                Map map = new HashMap();
1076:
1077:                map.put("_ancestor", new AncestorOp());
1078:                map.put("_ancestorOrSelf", new AncestorOrSelfOp());
1079:                map.put("_attributes", ALL_ATTRIBUTES_OP);
1080:                map.put("_children", ALL_CHILDREN_OP);
1081:                map.put("_cname", new CanonicalNameOp());
1082:                map.put("_content", new ContentOp());
1083:                map.put("_descendant", new DescendantOp());
1084:                map.put("_descendantOrSelf", new DescendantOrSelfOp());
1085:                map.put("_document", new DocumentOp());
1086:                map.put("_doctype", new DocTypeOp());
1087:                map.put("_name", new NameOp());
1088:                map.put("_nsprefix", new NamespacePrefixOp());
1089:                map.put("_nsuri", new NamespaceUriOp());
1090:                map.put("_parent", new ParentOp());
1091:                map.put("_qname", new QNameOp());
1092:                map.put("_text", new TextOp());
1093:
1094:                return map;
1095:            }
1096:
1097:            private static final Map createSpecialOperations() {
1098:                Map map = new HashMap();
1099:
1100:                Integer copy = new Integer(SPECIAL_OPERATION_COPY);
1101:                Integer unique = new Integer(SPECIAL_OPERATION_UNIQUE);
1102:                Integer fname = new Integer(SPECIAL_OPERATION_FILTER_NAME);
1103:                Integer ftype = new Integer(SPECIAL_OPERATION_FILTER_TYPE);
1104:                Integer type = new Integer(SPECIAL_OPERATION_QUERY_TYPE);
1105:                Integer regns = new Integer(
1106:                        SPECIAL_OPERATION_REGISTER_NAMESPACE);
1107:                Integer plaintext = new Integer(SPECIAL_OPERATION_PLAINTEXT);
1108:
1109:                map.put("_copy", copy);
1110:                map.put("_unique", unique);
1111:                map.put("_fname", fname);
1112:                map.put("_ftype", ftype);
1113:                map.put("_type", type);
1114:                map.put("_registerNamespace", regns);
1115:                map.put("_plaintext", plaintext);
1116:
1117:                // These are in for backward compatibility
1118:                map.put("x_copy", copy);
1119:                map.put("x_unique", unique);
1120:                map.put("x_fname", fname);
1121:                map.put("x_ftype", ftype);
1122:                map.put("x_type", type);
1123:
1124:                return map;
1125:            }
1126:
1127:            private final class RegisterNamespace implements 
1128:                    TemplateMethodModel {
1129:                public boolean isEmpty() {
1130:                    return false;
1131:                }
1132:
1133:                public Object exec(List arguments)
1134:                        throws TemplateModelException {
1135:                    if (arguments.size() != 2)
1136:                        throw new TemplateModelException(
1137:                                "_registerNamespace(prefix, uri) requires two arguments");
1138:
1139:                    registerNamespace((String) arguments.get(0),
1140:                            (String) arguments.get(1));
1141:
1142:                    return TemplateScalarModel.EMPTY_STRING;
1143:                }
1144:            }
1145:
1146:            private final class NameFilter implements  TemplateMethodModel {
1147:                public boolean isEmpty() {
1148:                    return false;
1149:                }
1150:
1151:                public Object exec(List arguments) {
1152:                    Set names = new HashSet(arguments);
1153:                    List list = new LinkedList(nodes);
1154:                    Iterator it = list.iterator();
1155:                    while (it.hasNext()) {
1156:                        Object node = it.next();
1157:                        String name = null;
1158:                        if (node instanceof  Element)
1159:                            name = ((Element) node).getName();
1160:                        else if (node instanceof  Attribute)
1161:                            name = ((Attribute) node).getName();
1162:                        else if (node instanceof  ProcessingInstruction)
1163:                            name = ((ProcessingInstruction) node).getTarget();
1164:                        else if (node instanceof  EntityRef)
1165:                            name = ((EntityRef) node).getName();
1166:                        else if (node instanceof  DocType)
1167:                            name = ((DocType) node).getPublicID();
1168:
1169:                        if (name == null || !names.contains(name))
1170:                            it.remove();
1171:                    }
1172:                    return createNodeListModel(list, namespaces);
1173:                }
1174:            }
1175:
1176:            private final class TypeFilter implements  TemplateMethodModel {
1177:                public boolean isEmpty() {
1178:                    return false;
1179:                }
1180:
1181:                public Object exec(List arguments)
1182:                        throws TemplateModelException {
1183:                    if (arguments == null || arguments.size() == 0)
1184:                        throw new TemplateModelException(
1185:                                "_type expects exactly one argument");
1186:                    String arg = (String) arguments.get(0);
1187:                    boolean invert = arg.indexOf('!') != -1;
1188:                    // NOTE: true in each of these variables means 'remove', not 'keep'
1189:                    // This is so we don't invert their values in the loop. So,
1190:                    // a is true <--> (a is not present in the string) xor invert.
1191:                    boolean a = invert != (arg.indexOf('a') == -1);
1192:                    boolean c = invert != (arg.indexOf('c') == -1);
1193:                    boolean d = invert != (arg.indexOf('d') == -1);
1194:                    boolean e = invert != (arg.indexOf('e') == -1);
1195:                    boolean n = invert != (arg.indexOf('n') == -1);
1196:                    boolean p = invert != (arg.indexOf('p') == -1);
1197:                    boolean t = invert != (arg.indexOf('t') == -1);
1198:                    boolean x = invert != (arg.indexOf('x') == -1);
1199:
1200:                    LinkedList list = new LinkedList(nodes);
1201:                    Iterator it = list.iterator();
1202:                    while (it.hasNext()) {
1203:                        Object node = it.next();
1204:                        if ((node instanceof  Element && e)
1205:                                || (node instanceof  Attribute && a)
1206:                                || (node instanceof  String && x)
1207:                                || (node instanceof  Text && x)
1208:                                || (node instanceof  ProcessingInstruction && p)
1209:                                || (node instanceof  Comment && c)
1210:                                || (node instanceof  EntityRef && n)
1211:                                || (node instanceof  Document && d)
1212:                                || (node instanceof  DocType && t))
1213:                            it.remove();
1214:                    }
1215:                    return createNodeListModel(list, namespaces);
1216:                }
1217:            }
1218:
1219:            /**
1220:             * Loads a template from a file passed as the first argument, loads an XML
1221:             * document from the standard input, passes it to the template as variable
1222:             * <tt>document</tt> and writes the result of template processing to
1223:             * standard output.
1224:             */
1225:            public static void main(String[] args) throws Exception {
1226:                org.jdom.input.SAXBuilder builder = new org.jdom.input.SAXBuilder();
1227:                Document document = builder.build(System.in);
1228:                SimpleHash model = new SimpleHash();
1229:                model.put("document", new NodeListModel(document));
1230:                FileReader fr = new FileReader(args[0]);
1231:                Template template = new Template(args[0], fr);
1232:                Writer w = new java.io.OutputStreamWriter(System.out);
1233:                template.process(model, w);
1234:                w.flush();
1235:                w.close();
1236:            }
1237:
1238:            private static final class AttributeXMLOutputter extends
1239:                    XMLOutputter {
1240:                public void output(Attribute attribute, Writer out)
1241:                        throws IOException {
1242:                    out.write(" ");
1243:                    out.write(attribute.getQualifiedName());
1244:                    out.write("=");
1245:
1246:                    out.write("\"");
1247:                    out.write(escapeAttributeEntities(attribute.getValue()));
1248:                    out.write("\"");
1249:                }
1250:            }
1251:
1252:            private static final class JDOMXPathEx extends JDOMXPath {
1253:                JDOMXPathEx(String path) throws JaxenException {
1254:                    super (path);
1255:                }
1256:
1257:                public List selectNodes(Object object, Map namespaces)
1258:                        throws JaxenException {
1259:                    Context context = getContext(object);
1260:                    context.getContextSupport().setNamespaceContext(
1261:                            new NamespaceContextImpl(namespaces));
1262:                    return selectNodesForContext(context);
1263:                }
1264:
1265:                private static final class NamespaceContextImpl implements 
1266:                        NamespaceContext {
1267:                    private final Map namespaces;
1268:
1269:                    NamespaceContextImpl(Map namespaces) {
1270:                        this .namespaces = namespaces;
1271:                    }
1272:
1273:                    public String translateNamespacePrefixToUri(String prefix) {
1274:                        // Empty prefix always maps to empty URL in XPath
1275:                        if (prefix.length() == 0) {
1276:                            return prefix;
1277:                        }
1278:                        synchronized (namespaces) {
1279:                            Namespace ns = (Namespace) namespaces.get(prefix);
1280:                            return ns == null ? null : ns.getURI();
1281:                        }
1282:                    }
1283:                }
1284:            }
1285:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.