You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2008/07/10 01:55:50 UTC
svn commit: r675383 - in /velocity/tools/trunk: ./ examples/showcase/
examples/showcase/WEB-INF/ examples/showcase/WEB-INF/src/
src/main/java/org/apache/velocity/tools/generic/ src/test/java/
src/test/java/org/apache/velocity/tools/
Author: nbubna
Date: Wed Jul 9 16:55:50 2008
New Revision: 675383
URL: http://svn.apache.org/viewvc?rev=675383&view=rev
Log:
VELTOOLS-99 - create an XmlTool (inspired by Philippe Collignon's XmlGen in API and DVSL's Dom4jNodeImpl in implementation)
Added:
velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml (with props)
velocity/tools/trunk/examples/showcase/xml.vm (with props)
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java (with props)
velocity/tools/trunk/src/test/java/file.xml (with props)
velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java (with props)
Modified:
velocity/tools/trunk/build.properties
velocity/tools/trunk/download.xml
velocity/tools/trunk/examples/showcase/WEB-INF/src/resources.properties
velocity/tools/trunk/examples/showcase/WEB-INF/tools.xml
velocity/tools/trunk/examples/showcase/toolmenu.vm
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml
velocity/tools/trunk/test.xml
Modified: velocity/tools/trunk/build.properties
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/build.properties?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/build.properties (original)
+++ velocity/tools/trunk/build.properties Wed Jul 9 16:55:50 2008
@@ -169,6 +169,7 @@
commons-lang.jar=${lib.dir}/commons-lang-${jar.commons-lang.version}.jar
commons-logging.jar=${lib.dir}/commons-logging-${jar.commons-logging.version}.jar
commons-validator.jar=${lib.dir}/commons-validator-${jar.commons-validator.version}.jar
+dom4j.jar=${lib.dir}/dom4j-${jar.dom4j.version}.jar
servlet.jar=${lib.dir}/servletapi-${jar.servletapi.version}.jar
struts-core.jar=${lib.dir}/struts-core-${jar.struts-core.version}.jar
struts-taglib.jar=${lib.dir}/struts-taglib-${jar.struts-taglib.version}.jar
Modified: velocity/tools/trunk/download.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/download.xml?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/download.xml (original)
+++ velocity/tools/trunk/download.xml Wed Jul 9 16:55:50 2008
@@ -49,6 +49,7 @@
<antcall target="commons-lang-download" />
<antcall target="commons-logging-download" />
<antcall target="commons-validator-download" />
+ <antcall target="dom4j-download" />
<antcall target="oro-download" />
<antcall target="servletapi-download" />
<antcall target="sslext-download" />
@@ -60,7 +61,6 @@
<target name="docs-download"
depends="base-download"
description="Download dependencies needed to render VelocityTools documentation from the central repository">
- <antcall target="dom4j-download" />
<antcall target="velocity-dvsl-download" />
</target>
Added: velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml?rev=675383&view=auto
==============================================================================
--- velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml (added)
+++ velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml Wed Jul 9 16:55:50 2008
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<foo x="true" this="that">
+ <a name="test" baz="true"/>
+ <bar>woogie</bar>
+ <bar>wiggie</bar>
+</foo>
\ No newline at end of file
Propchange: velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/examples/showcase/WEB-INF/src/file.xml
------------------------------------------------------------------------------
svn:mime-type = text/xml
Modified: velocity/tools/trunk/examples/showcase/WEB-INF/src/resources.properties
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/examples/showcase/WEB-INF/src/resources.properties?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/examples/showcase/WEB-INF/src/resources.properties (original)
+++ velocity/tools/trunk/examples/showcase/WEB-INF/src/resources.properties Wed Jul 9 16:55:50 2008
@@ -68,6 +68,7 @@
tools.text = ResourceTool
tools.search = SearchTool
tools.sorter = SortTool
+tools.xml = XmlTool
## demo common resources
demo.function = Function
@@ -204,3 +205,68 @@
determine if you are on the first or last iteration, get the number of iterations \
completed, automatically stop before or exclude particular elements and easily \
do all the above even with complex, nested loops.
+
+# xml.vm resources
+xml.intro = Tool for reading/navigating XML files. This uses dom4j under the \
+covers to provide complete XPath support for traversing XML files. \
+Most of the methods below will do nothing, as the default tool does not \
+have any XML content to work with. For demo's sake, there is a <code>file.xml</code> \
+in the classpath that is automatically loaded for you to work with, \
+and we have turned of safe mode, so you can do things like \
+<code>$xml.read('tools.xml')</code>. \
+Still, the full demo at the bottom will likely be the most useful for you.
+xml.read_Object.param1 = ''file.xml''
+xml.read_Object = Returns <code>null</code> if safe mode is on; otherwise \
+this will accept url pointing to an XML document. It will then parse that document \
+and return a new instance with that document's root element as its node.
+xml.parse_Object.param1 = ''<test><this>that</this></test>''
+xml.parse_Object = Accepts XML strings. If the XML is valid, it will return a \
+new XmlTool instance with the XML's root as its internal node.
+xml.get_Object.param1 = ''bar[2]''
+xml.get_Object = This will first attempt to find an attribute with the \
+specified name and return its value. If no such attribute \
+exists or its value is <code>null</code>, this will attempt to convert \
+the given value to a Number and get the result of get(Number). If the number conversion fails, \
+then this will convert the object to a string. If that string \
+does not start contain a '/', it appends the result of getPath() and a '/' to the front of it. \
+Finally, it delegates the string to the find(String) method and returns the result of that.
+xml.getName = Asks <code>get(Object)</code> for "name". If none, this will return the result of getNodeName().
+xml.getNodeName = Returns the name of the root node. If the internal Node \
+list has more than one Node, it will only return the name of the first node in the list.
+xml.attr_Object.param1 = ''x''
+xml.attr_Object = Returns the value of the specified attribute for the first/sole \
+Node in the internal Node list for this instance, if that Node is an Element. If it is a non-Element node type or \
+there is no value for that attribute in this element, then this will return <code>null</code>.
+xml.attributes = Returns a Map of all attributes for the first/sole \
+Node held internally by this instance. If that Node is not an Element, this will return null.
+xml.isEmpty = Returns <code>true</code> if there are no Nodes internally held by this instance.
+xml.size = Returns the number of Nodes internally held by this instance.
+xml.iterator = Returns an Iterator that returns new XmlTool instances for each Node held internally by this instance.
+xml.getFirst = Returns a new instance that wraps only the first Node from this instance's internal Node list.
+xml.getLast = Returns a new instance that wraps only the last Node from this instance's internal Node list.
+xml.get_Number.param1 = 0
+xml.get_Number = Returns a new instance that wraps the specified Node from this instance's internal Node list.
+xml.node = Returns the first/sole Node from this instance's internal Node list, if any.
+xml.find_Object.param1 = ''/*/a[@*]''
+xml.find_Object = Converts the specified object to a String and calls <code>find(String)</code> with that.
+xml.find_String.param1 = ''@baz''
+xml.find_String = Performs an XPath selection on the current set of \
+Nodes held by this instance and returns a new instance that wraps those results. \
+If the specified value is null or this instance does not currently hold any nodes, then this will return \
+<code>null</code>. If the specified value, when converted to a string, does not contain a '/' character, then \
+it has "//" prepended to it. This means that a call to <code>$xml.find("a")</code> is equivalent to calling \
+<code>$xml.find("//a")</code>. The full range of XPath selectors is supported here.
+xml.getParent = Returns a new XmlTool instance that wraps the parent Element of the first/sole Node \
+being wrapped by this instance.
+xml.getPath = Returns the XPath that identifies the first/sole Node represented by this instance.
+xml.parents = Returns a new XmlTool instance that wraps the parent Elements of each of the Nodes \
+being wrapped by this instance. This does not return all ancestors, just the immediate parents.
+xml.children = Returns a new XmlTool instance that wraps all the \
+child Elements of all the current internally held nodes that are Elements themselves.
+xml.getText = Returns the concatenated text content of all the internally held \
+nodes. Obviously, this is most useful when only one node is held.
+xml.toString = If this instance has no XML Nodes, then this returns the result of <code>super.toString()</code>. Otherwise, \
+it returns the XML (as a string) of all the internally held nodes \
+that are not Attributes. For attributes, only the value is used.
+xml.custom = $xml.find(''*[@name]'').attributes()
+
Modified: velocity/tools/trunk/examples/showcase/WEB-INF/tools.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/examples/showcase/WEB-INF/tools.xml?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/examples/showcase/WEB-INF/tools.xml (original)
+++ velocity/tools/trunk/examples/showcase/WEB-INF/tools.xml Wed Jul 9 16:55:50 2008
@@ -32,5 +32,6 @@
<tool key="date" format="yyyy-MM-dd" depth="2" skip="month"/>
<tool key="convert" dateFormat="yyyy-MM-dd"/>
<tool key="number" format="#0.0"/>
+ <tool key="xml" file="file.xml" safeMode="false"/>
</toolbox>
</tools>
Modified: velocity/tools/trunk/examples/showcase/toolmenu.vm
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/examples/showcase/toolmenu.vm?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/examples/showcase/toolmenu.vm (original)
+++ velocity/tools/trunk/examples/showcase/toolmenu.vm Wed Jul 9 16:55:50 2008
@@ -44,4 +44,5 @@
#toolMenuItem( $llink 'text' )
#toolMenuItem( $llink 'search' )
#toolMenuItem( $llink 'sorter' )
+#toolMenuItem( $llink 'xml' )
</ul>
Added: velocity/tools/trunk/examples/showcase/xml.vm
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/examples/showcase/xml.vm?rev=675383&view=auto
==============================================================================
--- velocity/tools/trunk/examples/showcase/xml.vm (added)
+++ velocity/tools/trunk/examples/showcase/xml.vm Wed Jul 9 16:55:50 2008
@@ -0,0 +1,36 @@
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+#title( 'XmlTool' )
+<p>
+$text.demo.thisPage.insert("#doclink( 'XmlTool' true )").
+$text.get('xml.intro')
+</p>
+
+#set( $toolname = 'xml' )
+#set( $toolclass = $xml.class )
+#set( $toollink = $doclink )
+#set( $toolDemo =
+"Document: ${esc.d}esc.xml(${esc.d}xml)
+
+bar text: ${esc.d}xml.bar.text
+
+baz attr value: ${esc.d}xml.a.baz
+
+last bar xml: ${esc.d}esc.xml(${esc.d}xml.bar.last)"
+)
+
+#parse( 'demo.vm' )
Propchange: velocity/tools/trunk/examples/showcase/xml.vm
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/examples/showcase/xml.vm
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/examples/showcase/xml.vm
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java?rev=675383&view=auto
==============================================================================
--- velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java (added)
+++ velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java Wed Jul 9 16:55:50 2008
@@ -0,0 +1,677 @@
+package org.apache.velocity.tools.generic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.dom4j.Attribute;
+import org.dom4j.Node;
+import org.dom4j.Element;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.io.XMLWriter;
+import org.dom4j.io.SAXReader;
+import org.apache.velocity.runtime.log.Log;
+import org.apache.velocity.tools.ClassUtils;
+import org.apache.velocity.tools.ConversionUtils;
+import org.apache.velocity.tools.ToolContext;
+import org.apache.velocity.tools.config.DefaultKey;
+
+/**
+ * <p>Tool for reading/navigating XML files. This uses dom4j under the
+ * covers to provide complete XPath support for traversing XML files.</p>
+ * <p>Here's a short example:<pre>
+ * XML file:
+ * <foo><bar>woogie</bar><a name="test"/></foo>
+ *
+ * Template:
+ * $doc.foo.bar
+ * $doc.find('a')
+ * $doc.a.name
+ *
+ * Output:
+ * woogie
+ * <a name="test"/>
+ * test
+ *
+ * Configuration:
+ * <tools>
+ * <toolbox scope="application">
+ * <tool class="org.apache.velocity.tools.generic.XmlTool"
+ * key="doc" file="doc.xml"/>
+ * </toolbox>
+ * </tools>
+ * </pre></p>
+ * <p>Note that this tool is included in the default GenericTools configuration
+ * under the key "xml", but unless you set safeMode="false" for it, you will
+ * only be able to parse XML strings. Safe mode is on by default and blocks
+ * access to the {@link #read(Object)} method.</p>
+ *
+ * @author Nathan Bubna
+ * @version $Revision$ $Date: 2006-11-27 10:49:37 -0800 (Mon, 27 Nov 2006) $
+ * @since VelocityTools 2.0
+ */
+@DefaultKey("xml")
+public class XmlTool extends SafeConfig
+{
+ public static final String FILE_KEY = "file";
+
+ protected Log LOG;
+
+ private List<Node> nodes;
+
+ public XmlTool() {}
+
+ public XmlTool(Node node)
+ {
+ this(Collections.singletonList(node));
+ }
+
+ public XmlTool(List<Node> nodes)
+ {
+ this.nodes = nodes;
+ }
+
+
+ /**
+ * Looks for the "file" parameter and automatically uses
+ * {@link #read(String)} to parse the file and set the
+ * resulting {@link Document} as the root node for this
+ * instance.
+ */
+ protected void configure(ValueParser parser)
+ {
+ this.LOG = (Log)parser.get(ToolContext.LOG_KEY);
+
+ String file = parser.getString(FILE_KEY);
+ if (file != null)
+ {
+ try
+ {
+ read(file);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Could not read XML file at: "+file, e);
+ }
+ }
+ }
+
+ /**
+ * Sets a singular root {@link Node} for this instance.
+ */
+ protected void setRoot(Node node)
+ {
+ if (node instanceof Document)
+ {
+ node = ((Document)node).getRootElement();
+ }
+ this.nodes = new ArrayList<Node>(1);
+ this.nodes.add(node);
+ }
+
+ //FIXME: dupe of FileFactoryConfiguration; move to one place
+ private URL getURL(String name) throws Exception
+ {
+ //TODO: grab the VelocityEngine so we can read files from there?
+ File file = new File(name);
+ if (file.exists())
+ {
+ return file.toURI().toURL();
+ }
+ URL url = ClassUtils.getResource(name, this);
+ if (url != null)
+ {
+ return url;
+ }
+ return new URL(name);
+ }
+
+ private void log(Object o, Throwable t)
+ {
+ if (LOG != null)
+ {
+ LOG.debug("XmlTool - "+o, t);
+ }
+ }
+
+ /**
+ * Creates a {@link URL} from the string and passes it to {@link #read(URL)}.
+ */
+ protected void read(String file) throws Exception
+ {
+ read(getURL(file));
+ }
+
+ /**
+ * Reads, parses and creates a {@link Document} from the
+ * given {@link URL} and uses it as the root {@link Node} for this instance.
+ */
+ protected void read(URL url) throws Exception
+ {
+ SAXReader reader = new SAXReader();
+ setRoot(reader.read(url));
+ }
+
+ /**
+ * Parses the given XML string and uses the resulting {@link Document}
+ * as the root {@link Node}.
+ */
+ protected void parse(String xml) throws Exception
+ {
+ setRoot(DocumentHelper.parseText(xml));
+ }
+
+
+ /**
+ * If safe mode is explicitly turned off for this tool, then
+ * this will accept either a {@link URL} or the string representation
+ * thereof. If valid, it will return a new {@link XmlTool} instance
+ * with that document as the root {@link Node}. If reading the URL
+ * or parsing its content fails or if safe mode is on (the default),
+ * this will return {@code null}.
+ */
+ public XmlTool read(Object o)
+ {
+ if (isSafeMode() || o == null)
+ {
+ return null;
+ }
+ try
+ {
+ XmlTool xml = new XmlTool();
+ if (o instanceof URL)
+ {
+ xml.read((URL)o);
+ }
+ else
+ {
+ String file = String.valueOf(o);
+ xml.read(file);
+ }
+ return xml;
+ }
+ catch (Exception e)
+ {
+ log("Failed to read XML from : "+o, e);
+ return null;
+ }
+ }
+
+ /**
+ * This accepts XML in form. If the XML is valid, it will return a
+ * new {@link XmlTool} instance with the resulting XML document
+ * as the root {@link Node}. If parsing the content fails,
+ * this will return {@code null}.
+ */
+ public XmlTool parse(Object o)
+ {
+ if (o == null)
+ {
+ return null;
+ }
+ String s = String.valueOf(o);
+ try
+ {
+ XmlTool xml = new XmlTool();
+ xml.parse(s);
+ return xml;
+ }
+ catch (Exception e)
+ {
+ log("Failed to parse XML from : "+o, e);
+ return null;
+ }
+ }
+
+
+ /**
+ * This will first attempt to find an attribute with the
+ * specified name and return its value. If no such attribute
+ * exists or its value is {@code null}, this will attempt to convert
+ * the given value to a {@link Number} and get the result of
+ * {@link #get(Number)}. If the number conversion fails,
+ * then this will convert the object to a string. If that string
+ * does not contain a '/', it appends the result of {@link #getPath()}
+ * and a '/' to the front of it. Finally, it delegates the string to the
+ * {@link #find(String)} method and returns the result of that.
+ */
+ public Object get(Object o)
+ {
+ if (isEmpty() || o == null)
+ {
+ return null;
+ }
+ String attr = attr(o);
+ if (attr != null)
+ {
+ return attr;
+ }
+ Number i = ConversionUtils.toNumber(o);
+ if (i != null)
+ {
+ return get(i);
+ }
+ String s = String.valueOf(o);
+ if (s.length() == 0)
+ {
+ return null;
+ }
+ if (s.indexOf('/') < 0)
+ {
+ s = getPath()+'/'+s;
+ }
+ return find(s);
+ }
+
+
+ /**
+ * Asks {@link #get(Object)} for a "name" result.
+ * If none, this will return the result of {@link #getNodeName()}.
+ */
+ public Object getName()
+ {
+ // give attributes and child elements priority
+ Object name = get("name");
+ if (name != null)
+ {
+ return name;
+ }
+ return getNodeName();
+ }
+
+ /**
+ * Returns the name of the root node. If the internal {@link Node}
+ * list has more than one {@link Node}, it will only return the name
+ * of the first node in the list.
+ */
+ public String getNodeName()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ return node().getName();
+ }
+
+ /**
+ * Returns the XPath that identifies the first/sole {@link Node}
+ * represented by this instance.
+ */
+ public String getPath()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ return node().getPath();
+ }
+
+ /**
+ * Returns the value of the specified attribute for the first/sole
+ * {@link Node} in the internal Node list for this instance, if that
+ * Node is an {@link Element}. If it is a non-Element node type or
+ * there is no value for that attribute in this element, then this
+ * will return {@code null}.
+ */
+ public String attr(Object o)
+ {
+ if (o == null)
+ {
+ return null;
+ }
+ String key = String.valueOf(o);
+ Node node = node();
+ if (node instanceof Element)
+ {
+ return ((Element)node).attributeValue(key);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a {@link Map} of all attributes for the first/sole
+ * {@link Node} held internally by this instance. If that Node is
+ * not an {@link Element}, this will return null.
+ */
+ public Map<String,String> attributes()
+ {
+ Node node = node();
+ if (node instanceof Element)
+ {
+ Map<String,String> attrs = new HashMap<String,String>();
+ for (Iterator i = ((Element)node).attributeIterator(); i.hasNext();)
+ {
+ Attribute a = (Attribute)i.next();
+ attrs.put(a.getName(), a.getValue());
+ }
+ return attrs;
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns {@code true} if there are no {@link Node}s internally held
+ * by this instance.
+ */
+ public boolean isEmpty()
+ {
+ return (nodes == null || nodes.isEmpty());
+ }
+
+ /**
+ * Returns the number of {@link Node}s internally held by this instance.
+ */
+ public int size()
+ {
+ if (isEmpty())
+ {
+ return 0;
+ }
+ return nodes.size();
+ }
+
+ /**
+ * Returns an {@link Iterator} that returns new {@link XmlTool}
+ * instances for each {@link Node} held internally by this instance.
+ */
+ public Iterator<XmlTool> iterator()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ return new NodeIterator(nodes.iterator());
+ }
+
+ /**
+ * Returns an {@link XmlTool} that wraps only the
+ * first {@link Node} from this instance's internal Node list.
+ */
+ public XmlTool getFirst()
+ {
+ if (size() == 1)
+ {
+ return this;
+ }
+ return new XmlTool(node());
+ }
+
+ /**
+ * Returns an {@link XmlTool} that wraps only the
+ * last {@link Node} from this instance's internal Node list.
+ */
+ public XmlTool getLast()
+ {
+ if (size() == 1)
+ {
+ return this;
+ }
+ return new XmlTool(nodes.get(size() - 1));
+ }
+
+ /**
+ * Returns an {@link XmlTool} that wraps the specified
+ * {@link Node} from this instance's internal Node list.
+ */
+ public XmlTool get(Number n)
+ {
+ if (n == null)
+ {
+ return null;
+ }
+ int i = n.intValue();
+ if (i < 0 || i > size() - 1)
+ {
+ return null;
+ }
+ return new XmlTool(nodes.get(i));
+ }
+
+ /**
+ * Returns the first/sole {@link Node} from this
+ * instance's internal Node list, if any.
+ */
+ public Node node()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ return nodes.get(0);
+ }
+
+
+ /**
+ * Converts the specified object to a String and calls
+ * {@link #find(String)} with that.
+ */
+ public XmlTool find(Object o)
+ {
+ if (o == null || isEmpty())
+ {
+ return null;
+ }
+ return find(String.valueOf(o));
+ }
+
+ /**
+ * Performs an XPath selection on the current set of
+ * {@link Node}s held by this instance and returns a new
+ * {@link XmlTool} instance that wraps those results.
+ * If the specified value is null or this instance does
+ * not currently hold any nodes, then this will return
+ * {@code null}. If the specified value, when converted
+ * to a string, does not contain a '/' character, then
+ * it has "//" prepended to it. This means that a call to
+ * {@code $xml.find("a")} is equivalent to calling
+ * {@code $xml.find("//a")}. The full range of XPath
+ * selectors is supported here.
+ */
+ public XmlTool find(String xpath)
+ {
+ if (xpath == null || xpath.length() == 0)
+ {
+ return null;
+ }
+ if (xpath.indexOf('/') < 0)
+ {
+ xpath = "//"+xpath;
+ }
+ List<Node> found = new ArrayList<Node>();
+ for (Node n : nodes)
+ {
+ found.addAll((List<Node>)n.selectNodes(xpath));
+ }
+ if (found.isEmpty())
+ {
+ return null;
+ }
+ return new XmlTool(found);
+ }
+
+ /**
+ * Returns a new {@link XmlTool} instance that wraps
+ * the parent {@link Element} of the first/sole {@link Node}
+ * being wrapped by this instance.
+ */
+ public XmlTool getParent()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ Element parent = node().getParent();
+ if (parent == null)
+ {
+ return null;
+ }
+ return new XmlTool(parent);
+ }
+
+ /**
+ * Returns a new {@link XmlTool} instance that wraps
+ * the parent {@link Element}s of each of the {@link Node}s
+ * being wrapped by this instance. This does not return
+ * all ancestors, just the immediate parents.
+ */
+ public XmlTool parents()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ if (size() == 1)
+ {
+ return getParent();
+ }
+ List<Node> parents = new ArrayList<Node>(size());
+ for (Node n : nodes)
+ {
+ Element parent = n.getParent();
+ if (parent != null && !parents.contains(parent))
+ {
+ parents.add(parent);
+ }
+ }
+ if (parents.isEmpty())
+ {
+ return null;
+ }
+ return new XmlTool(parents);
+ }
+
+ /**
+ * Returns a new {@link XmlTool} instance that wraps all the
+ * child {@link Element}s of all the current internally held nodes
+ * that are {@link Element}s themselves.
+ */
+ public XmlTool children()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ List<Node> kids = new ArrayList<Node>();
+ for (Node n : nodes)
+ {
+ if (n instanceof Element)
+ {
+ kids.addAll((List<Node>)((Element)n).elements());
+ }
+ }
+ return new XmlTool(kids);
+ }
+
+ /**
+ * Returns the concatenated text content of all the internally held
+ * nodes. Obviously, this is most useful when only one node is held.
+ */
+ public String getText()
+ {
+ if (isEmpty())
+ {
+ return null;
+ }
+ StringBuilder out = new StringBuilder();
+ for (Node n : nodes)
+ {
+ String text = n.getText();
+ if (text != null)
+ {
+ out.append(text);
+ }
+ }
+ String result = out.toString().trim();
+ if (result.length() > 0)
+ {
+ return result;
+ }
+ return null;
+ }
+
+
+ /**
+ * If this instance has no XML {@link Node}s, then this
+ * returns the result of {@code super.toString()}. Otherwise, it
+ * returns the XML (as a string) of all the internally held nodes
+ * that are not {@link Attribute}s. For attributes, only the value
+ * is used.
+ */
+ public String toString()
+ {
+ if (isEmpty())
+ {
+ return super.toString();
+ }
+ StringBuilder out = new StringBuilder();
+ for (Node n : nodes)
+ {
+ if (n instanceof Attribute)
+ {
+ out.append(n.getText().trim());
+ }
+ else
+ {
+ out.append(n.asXML());
+ }
+ }
+ return out.toString();
+ }
+
+
+ /**
+ * Iterator implementation that wraps a Node list iterator
+ * to return new XmlTool instances for each item in the wrapped
+ * iterator.s
+ */
+ public static class NodeIterator implements Iterator<XmlTool>
+ {
+ private Iterator<Node> i;
+
+ public NodeIterator(Iterator<Node> i)
+ {
+ this.i = i;
+ }
+
+ public boolean hasNext()
+ {
+ return i.hasNext();
+ }
+
+ public XmlTool next()
+ {
+ return new XmlTool(i.next());
+ }
+
+ public void remove()
+ {
+ i.remove();
+ }
+ }
+}
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml (original)
+++ velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml Wed Jul 9 16:55:50 2008
@@ -35,6 +35,7 @@
<tool class="org.apache.velocity.tools.generic.NumberTool"/>
<tool class="org.apache.velocity.tools.generic.ResourceTool"/>
<tool class="org.apache.velocity.tools.generic.SortTool"/>
+ <tool class="org.apache.velocity.tools.generic.XmlTool"/>
</toolbox>
<toolbox scope="request">
<tool class="org.apache.velocity.tools.generic.ContextTool"/>
Added: velocity/tools/trunk/src/test/java/file.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/test/java/file.xml?rev=675383&view=auto
==============================================================================
--- velocity/tools/trunk/src/test/java/file.xml (added)
+++ velocity/tools/trunk/src/test/java/file.xml Wed Jul 9 16:55:50 2008
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<foo>
+ <bar name="a"/>
+ <baz>woogie</baz>
+ <baz>wiggie</baz>
+</foo>
\ No newline at end of file
Propchange: velocity/tools/trunk/src/test/java/file.xml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/src/test/java/file.xml
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/src/test/java/file.xml
------------------------------------------------------------------------------
svn:mime-type = text/xml
Added: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java?rev=675383&view=auto
==============================================================================
--- velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java (added)
+++ velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java Wed Jul 9 16:55:50 2008
@@ -0,0 +1,304 @@
+package org.apache.velocity.tools.generic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.junit.*;
+import static org.junit.Assert.*;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.velocity.tools.generic.ValueParser;
+import org.dom4j.Node;
+
+/**
+ * <p>Tests for XmlTool</p>
+ *
+ * @author Nathan Bubna
+ * @since VelocityTools 2.0
+ * @version $Id$
+ */
+public class XmlToolTests {
+
+ private static final String XML_FILE = "@test.file.dir@/file.xml";
+
+ private static final String XML_STRING =
+"<foo>\n <bar name=\"a\"/>\n <baz>woogie</baz>\n <baz>wiggie</baz>\n</foo>";
+
+ public @Test void ctorXmlTool() throws Exception
+ {
+ try
+ {
+ new XmlTool();
+ }
+ catch (Exception e)
+ {
+ fail("Constructor 'XmlTool()' failed due to: " + e);
+ }
+ }
+
+ private XmlTool stringBased() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ xml.parse(XML_STRING);
+ return xml;
+ }
+
+ private XmlTool fileBased() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ xml.read(XML_FILE);
+ return xml;
+ }
+
+ public @Test void testStringFileEquals() throws Exception
+ {
+ String string = stringBased().toString();
+ String file = fileBased().toString();
+ //System.out.println("string:\n"+string+"\nfile:\n"+file);
+ assertEquals(string, file);
+ }
+
+ public @Test void methodAttr_Object() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertNull(xml.attr("href"));
+ xml = xml.find("bar");
+ assertNotNull(xml.attr("name"));
+ assertEquals("a", xml.attr("name"));
+ }
+
+ public @Test void methodAttributes() throws Exception
+ {
+ XmlTool xml = stringBased();
+ Map<String,String> result = xml.attributes();
+ assertTrue(result.isEmpty());
+ xml = xml.find("bar");
+ result = xml.attributes();
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("a", result.get("name"));
+ }
+
+ public @Test void methodChildren() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertEquals(1, xml.size());
+ XmlTool result = xml.children();
+ assertEquals(3, result.size());
+ }
+
+ public @Test void methodGetParent() throws Exception
+ {
+ XmlTool xml = stringBased().find("bar");
+ assertNotNull(xml);
+ XmlTool foo = xml.getParent();
+ assertNotNull(foo);
+ assertEquals(1, foo.size());
+ assertNull(foo.getParent());
+ }
+
+ public @Test void methodParents() throws Exception
+ {
+ XmlTool foo = stringBased();
+ XmlTool xml = foo.find("bar");
+ assertEquals(foo.toString(), xml.parents().toString());
+ xml = foo.children();
+ assertEquals(3, xml.size());
+ foo = xml.parents();
+ assertEquals(1, foo.size());
+ }
+
+ public @Test void methodConfigure_ValueParser() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ Map<String,String> params = new HashMap<String,String>();
+ assertEquals("file", XmlTool.FILE_KEY);
+ params.put(XmlTool.FILE_KEY, XML_FILE);
+ xml.configure(params);
+ assertEquals(1, xml.size());
+ assertEquals("foo", xml.getName());
+ }
+
+ public @Test void methodFind_Object() throws Exception
+ {
+ XmlTool xml = stringBased();
+ XmlTool result = xml.find((Object)null);
+ assertNull(result);
+ assertEquals(xml.find("bar").toString(), xml.find("//bar").toString());
+ //TODO: test more xpath expressions?
+ //TODO: test expressions with no results
+ }
+
+ public @Test void methodGetFirst() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertSame(xml, xml.getFirst());
+ xml = xml.children();
+ assertEquals(3, xml.size());
+ xml = xml.getFirst();
+ assertEquals(1, xml.size());
+ assertEquals("a", xml.getName());
+ }
+
+ public @Test void methodGetLast() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertSame(xml, xml.getLast());
+ xml = xml.children();
+ assertEquals(3, xml.size());
+ xml = xml.getLast();
+ assertEquals(1, xml.size());
+ assertEquals("baz", xml.getName());
+ assertEquals("wiggie", xml.getText());
+ }
+
+ public @Test void methodGetName() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertEquals(1, xml.size());
+ assertEquals("foo", xml.getName());
+ xml = xml.find("bar");
+ assertEquals("a", xml.getName());
+ }
+
+ public @Test void methodGetNodeName() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertEquals("foo", xml.getNodeName());
+ xml = xml.find("baz");
+ assertEquals("baz", xml.getNodeName());
+ }
+
+ public @Test void methodGetText() throws Exception
+ {
+ XmlTool xml = stringBased();
+ //TODO: prepare the instance for testing
+ String result = xml.getText();
+ assertEquals((String)null, result);
+ }
+
+ public @Test void methodGet_Number() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertEquals(xml.toString(), xml.get(0).toString());
+ xml = xml.children();
+ assertEquals("bar", xml.get(0).getNodeName());
+ assertEquals("baz", xml.get(1).getName());
+ assertEquals("baz", xml.get(2).getName());
+ assertNull(xml.get(3));
+ assertNull(xml.get(-1));
+ }
+
+ public @Test void methodGet_Object() throws Exception
+ {
+ XmlTool xml = stringBased();
+ assertNull(xml.get(null));
+ assertNull(xml.get(""));
+ assertNull(xml.get("null"));
+ Object result = xml.get("bar");
+ assertNotNull(result);
+ assertTrue(result instanceof XmlTool);
+ xml = (XmlTool)result;
+ result = null;
+ assertNull(result);
+ result = xml.get("0");
+ assertNotNull(result);
+ assertEquals(result.toString(), xml.toString());
+ result = null;
+ assertNull(result);
+ result = xml.get("name");
+ assertNotNull(result);
+ assertEquals("a", result);
+ }
+
+ public @Test void methodIsEmpty() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ assertTrue(xml.isEmpty());
+ xml.parse(XML_STRING);
+ assertFalse(xml.isEmpty());
+ }
+
+ public @Test void methodIterator() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ assertNull(xml.iterator());
+ xml.parse(XML_STRING);
+ Iterator<XmlTool> i = xml.iterator();
+ assertNotNull(i);
+ XmlTool foo = i.next();
+ assertNotNull(foo);
+ assertEquals(foo.toString(), xml.toString());
+ xml = xml.children();
+ i = xml.iterator();
+ assertEquals("a", i.next().attr("name"));
+ assertEquals("baz", i.next().getName());
+ assertEquals("wiggie", i.next().getText());
+ assertFalse(i.hasNext());
+ }
+
+ public @Test void methodNode() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ assertNull(xml.node());
+ xml.parse(XML_STRING);
+ Node n = xml.node();
+ assertNotNull(n);
+ }
+
+ public @Test void methodParse_Object() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ assertNull(xml.parse((Object)null));
+ assertNull(xml.parse((Object)"><S asdf8 ~$"));
+ assertNotNull(xml.parse((Object)XML_STRING));
+ //TODO: test other strings?
+ }
+
+ public @Test void methodSize() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ assertEquals(0, xml.size());
+ xml.parse(XML_STRING);
+ assertEquals(1, xml.size());
+ xml = xml.children();
+ assertEquals(3, xml.size());
+ xml = xml.getLast();
+ assertEquals(1, xml.size());
+ }
+
+ public @Test void methodToString() throws Exception
+ {
+ XmlTool xml = new XmlTool();
+ assertTrue(xml.toString().startsWith(XmlTool.class.getName()));
+ xml.read(XML_FILE);
+ assertTrue(xml.toString().startsWith("<foo>"));
+ assertTrue(xml.toString().endsWith("</foo>"));
+ XmlTool bar = xml.find("bar");
+ assertEquals("<bar name=\"a\"/>", bar.toString());
+ XmlTool baz = (XmlTool)xml.get("baz");
+ assertEquals("<baz>woogie</baz><baz>wiggie</baz>", baz.toString());
+ }
+
+
+}
+
\ No newline at end of file
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/XmlToolTests.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: velocity/tools/trunk/test.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/test.xml?rev=675383&r1=675382&r2=675383&view=diff
==============================================================================
--- velocity/tools/trunk/test.xml (original)
+++ velocity/tools/trunk/test.xml Wed Jul 9 16:55:50 2008
@@ -63,6 +63,10 @@
<pathconvert property="test.conf.dir.java" targetos="unix">
<path location="${test.conf.dir}"/>
</pathconvert>
+ <pathconvert property="test.file.dir" targetos="unix">
+ <path location="${test.src.dir}"/>
+ </pathconvert>
+ <filter token="test.file.dir" value="${test.file.dir}"/>
<filter token="test.conf.dir" value="${test.conf.dir.java}"/>
<filter token="test.log.dir" value="${test.log.dir}"/>
<filter token="test.webcontainer.port" value="${test.webcontainer.port}"/>