📄 nodelistmodel.java
字号:
* <li><tt>_ftype</tt>: is a filter-by-type template method model. When called,
* it will yield a node list that contains only those current nodes whose type matches one
* of types passed as argument. You should pass a single string to this method
* containing the characters of all types to keep. Valid characters are:
* e (Element), a (Attribute), n (Entity), d (Document), t (DocType),
* c (Comment), p (ProcessingInstruction), x (text). If the string anywhere contains
* the exclamation mark (!), the filter's effect is inverted.</li>
* <li><tt>_type</tt>: Returns a one-character String SimpleScalar containing
* the typecode of the first node in the node list. Valid characters are:
* e (Element), a (Attribute), n (Entity), d (Document), t (DocType),
* c (Comment), p (ProcessingInstruction), x (text). If the type of the node
* is unknown, returns '?'. If the node list is empty, returns an empty string scalar.</li>
* <li><tt>_unique</tt>: a copy of the current nodes that keeps only the
* first occurrence of every node, eliminating duplicates. Duplicates can
* occur in the node list by applying uptree-traversals <tt>_parent</tt>,
* <tt>_ancestor</tt>, <tt>_ancestorOrSelf</tt>, and <tt>_document</tt>.
* I.e. <tt>foo._children._parent</tt> will return a node list that has
* duplicates of nodes in foo - each node will have the number of occurrences
* equal to the number of its children. In these cases, use
* <tt>foo._children._parent._unique</tt> to eliminate duplicates. Applicable
* to all node types.</li>
* <li><tt>_copy</tt>: a copy of the current node list. It is a shallow copy that
* shares the underlying node list with this node list, however it has a
* separate namespace registry, so it can be used to guarantee that subsequent
* changes to the set of registered namespaces does not affect the node lists
* that were used to create this node list. Applicable to all node types.</li>
* <li><tt>_registerNamespace(prefix, uri)</tt>: register a XML namespace
* with the specified prefix and URI for the current node list and all node
* lists that are derived from the current node list. After registering,
* you can use the <tt>nodelist["prefix:localname"]</tt> or
* <tt>nodelist["@prefix:localname"]</tt> syntaxes to reach elements and
* attributes whose names are namespace-scoped. Note that the namespace
* prefix need not match the actual prefix used by the XML document itself
* since namespaces are compared solely by their URI. You can also register
* namespaces from Java code using the
* {@link #registerNamespace(String, String)} method.
* </li>
* <li><tt>@attributeName</tt>: named attributes of current nodes. Applicable to
* elements, doctypes and processing instructions. On doctypes it supports
* attributes <tt>publicId</tt>, <tt>systemId</tt> and <tt>elementName</tt>. On processing
* instructions, it supports attributes <tt>target</tt> and <tt>data</tt>, as
* well as any other attribute name specified in data as <tt>name="value"</tt> pair.
* The attribute nodes for doctype and processing instruction are synthetic, and
* as such have no parent. Note, however that <tt>@*</tt> does NOT operate on
* doctypes or processing instructions.</li>
* <li>any other key: element children of current nodes with name matching the key.
* This allows for convenience child traversal in <tt>book.chapter.title</tt> style syntax.
* Note that <tt>nodeset.childname</tt> is technically equivalent to
* <tt>nodeset._children._fname("childname")</tt>, but is both shorter to write
* and evaluates faster. Applicable to document and element nodes.</li>
* </ul>
* The order of nodes in the resulting set is the order of evaluation of the key
* on each node in this set from left to right. Evaluation of the key on a single
* node always yields the results in "natural" order (that of the document preorder
* traversal), even for uptree traversals. As a consequence, if this node list's nodes
* are listed in natural order, applying any of the keys will produce a node list that
* is also naturally ordered. As a special case, all node lists that are directly or
* indirectly generated from a single Document or Element node through repeated
* invocations of this method will be naturally ordered.
* @param key a key that identifies a required set of nodes
* @return a new NodeListModel that represents the requested set of nodes.
*/
public TemplateModel get(String key)
throws
TemplateModelException
{
if (isEmpty())
return EMPTY;
if (key == null || key.length() == 0)
throw new TemplateModelException("Invalid key [" + key + "]");
NodeOperator op = null;
NamedNodeOperator nop = null;
String name = null;
switch (key.charAt(0)) {
case '@':
{
if (key.length() != 2 || key.charAt(1) != '*') {
// Generic attribute key
nop = NAMED_ATTRIBUTE_OP;
name = key.substring(1);
} else
// It is @*
op = ALL_ATTRIBUTES_OP;
break;
}
case '*':
{
if (key.length() == 1)
op = ALL_CHILDREN_OP;
else
// Explicitly disallow any other identifier starting with asterisk
throw new TemplateModelException("Invalid key [" + key + "]");
break;
}
case 'x':
case '_':
{
op = (NodeOperator)OPERATIONS.get(key);
if (op == null) {
// Some special operation?
Integer specop = (Integer)SPECIAL_OPERATIONS.get(key);
if (specop != null) {
switch (specop.intValue()) {
case SPECIAL_OPERATION_COPY:
{
synchronized(namespaces)
{
return new NodeListModel(nodes, (Map)((HashMap)namespaces).clone());
}
}
case SPECIAL_OPERATION_UNIQUE:
return new NodeListModel(removeDuplicates(nodes), namespaces);
case SPECIAL_OPERATION_FILTER_NAME:
return new NameFilter();
case SPECIAL_OPERATION_FILTER_TYPE:
return new TypeFilter();
case SPECIAL_OPERATION_QUERY_TYPE:
return getType();
case SPECIAL_OPERATION_REGISTER_NAMESPACE:
return new RegisterNamespace();
case SPECIAL_OPERATION_PLAINTEXT:
return getPlainText();
}
}
}
break;
}
}
if (op == null && nop == null) {
nop = NAMED_CHILDREN_OP;
name = key;
}
List list = null;
if (op != null)
list = evaluateElementOperation(op, nodes);
else {
String localName = name;
Namespace namespace = Namespace.NO_NAMESPACE;
int colon = name.indexOf(':');
if (colon != -1) {
localName = name.substring(colon + 1);
String nsPrefix = name.substring(0, colon);
synchronized(namespaces)
{
namespace = (Namespace)namespaces.get(nsPrefix);
}
if (namespace == null) {
if (nsPrefix.equals("xml"))
namespace = Namespace.XML_NAMESPACE;
else
throw new TemplateModelException("Unregistered namespace prefix '" + nsPrefix + "'");
}
}
list = evaluateNamedElementOperation(nop, localName, namespace, nodes);
}
return createNodeListModel(list, namespaces);
}
private TemplateModel getType()
{
if (nodes.size() == 0)
return new SimpleScalar("");
Object firstNode = nodes.get(0);
char code;
if (firstNode instanceof Element)
code = 'e';
else if (firstNode instanceof Text || firstNode instanceof String)
code = 'x';
else if (firstNode instanceof Attribute)
code = 'a';
else if (firstNode instanceof EntityRef)
code = 'n';
else if (firstNode instanceof Document)
code = 'd';
else if (firstNode instanceof DocType)
code = 't';
else if (firstNode instanceof Comment)
code = 'c';
else if (firstNode instanceof ProcessingInstruction)
code = 'p';
else
code = '?';
return new SimpleScalar(new String(new char[] { code}));
}
private SimpleScalar getPlainText()
throws
TemplateModelException
{
List list = evaluateElementOperation((TextOp)OPERATIONS.get("_text"), nodes);
StringBuffer buf = new StringBuffer();
for (Iterator it = list.iterator(); it.hasNext();) {
buf.append(it.next());
}
return new SimpleScalar(buf.toString());
}
public TemplateModelIterator iterator()
{
return new TemplateModelIterator()
{
private final Iterator it = nodes.iterator();
public TemplateModel next()
{
return it.hasNext() ? new NodeListModel(it.next(), namespaces) : null;
}
public boolean hasNext()
{
return it.hasNext();
}
};
}
/**
* Retrieves the i-th element of the node list.
*/
public TemplateModel get(int i)
throws
TemplateModelException
{
try {
return new NodeListModel(nodes.get(i), namespaces);
} catch (IndexOutOfBoundsException e) {
throw new TemplateModelException("Index out of bounds: " + e.getMessage());
}
}
public int size()
{
return nodes.size();
}
/**
* Applies an XPath expression to the node list and returns the resulting node list.
* In order for this method to work, your application must have access
* <a href="http://www.jaxen.org">Jaxen</a> library classes. The
* implementation does cache the parsed format of XPath expressions in a weak hash
* map, keyed by the string representation of the XPath expression. As the string
* object passed as the argument is usually kept in the parsed FreeMarker template,
* this ensures that each XPath expression is parsed only once during the lifetime
* of the FreeMarker template that contains it.
* @param arguments the list of arguments. Must contain exactly one string that is
* the XPath expression you wish to apply. The XPath expression can use any namespace
* prefixes that were defined using the {@link #registerNamespace(String, String)}
* method or the <code>nodelist._registerNamespace(prefix, uri)</code> expression in the
* template.
* @return a NodeListModel representing the nodes that are the result of application
* of the XPath to the current node list.
*/
public Object exec(List arguments)
throws
TemplateModelException
{
if (arguments == null || arguments.size() != 1)
throw new TemplateModelException("Exactly one argument required for execute() on NodeTemplate");
String xpathString = (String)arguments.get(0);
JDOMXPathEx xpath = null;
try
{
synchronized(XPATH_CACHE)
{
xpath = (JDOMXPathEx)XPATH_CACHE.get(xpathString);
if (xpath == null)
{
xpath = new JDOMXPathEx(xpathString);
XPATH_CACHE.put(xpathString, xpath);
}
}
return createNodeListModel(xpath.selectNodes(nodes, namespaces), namespaces);
}
catch(Exception e)
{
throw new TemplateModelException("Could not evaulate XPath expression " + xpathString, e);
}
}
/**
* Registers an XML namespace with this node list. Once registered, you can
* refer to the registered namespace using its prefix in the
* {@link #get(String)} method from this node list and all other
* node lists that are derived from this node list. Use the
* <tt>nodelist["prefix:localname"]</tt> or the
* <tt>nodelist["@prefix:localname"]</tt> syntax to reach elements and
* attributes whose names are namespace-scoped. Note that the namespace
* prefix need not match the actual prefix used by the XML document itself
* since namespaces are compared solely by their URI. You can also register
* namespaces during template evaluation using the
* <tt>nodelist._registerNamespace(prefix, uri)</tt> syntax in the template.
* This mechanism is completely independent from the namespace declarations
* in the XML document itself; its purpose is to give you an easy way
* to refer to namespace-scoped elements in {@link #get(String)} and
* in XPath expressions passed to {@link #exec(List)}. Note also that
* the namespace prefix registry is shared among all node lists that
* are created from a single node list - modifying the registry in one
* affects all others as well. If you want to obtain a namespace
* "detached" copy of the node list, use the <code>_copy</code> key on
* it (or call <code>nodeList.get("_copy")</code> directly from your
* Java code. The returned node list has all the namespaces that the
* original node list has, but they can be manipulated independently
* thereon.
*/
public void registerNamespace(String prefix, String uri)
{
synchronized(namespaces)
{
namespaces.put(prefix, Namespace.getNamespace(prefix, uri));
}
}
private interface NodeOperator {
List operate(Object node)
throws
TemplateModelException;
}
private interface NamedNodeOperator {
List operate(Object node, String localName, Namespace namespace)
throws
TemplateModelException;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -