jon 01/08/07 21:30:47
Modified: src/java/org/apache/velocity/anakia AnakiaTask.java
TreeWalker.java XPathTool.java
Added: src/java/org/apache/velocity/anakia AnakiaElement.java
AnakiaJDOMFactory.java NodeList.java
XPathCache.java
Log:
integrate in Attila Szegedi's patches to improve the XPath support within
Anakia
Revision Changes Path
1.31 +31 -17
jakarta-velocity/src/java/org/apache/velocity/anakia/AnakiaTask.java
Index: AnakiaTask.java
===================================================================
RCS file:
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/anakia/AnakiaTask.java,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -r1.30 -r1.31
--- AnakiaTask.java 2001/08/07 22:30:15 1.30
+++ AnakiaTask.java 2001/08/08 04:30:47 1.31
@@ -93,7 +93,8 @@
* <a href="http://jakarta.apache.org/velocity/anakia.html">Website</a>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jon S. Stevens</a>
- * @version $Id: AnakiaTask.java,v 1.30 2001/08/07 22:30:15 geirm Exp $
+ * @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
+ * @version $Id: AnakiaTask.java,v 1.31 2001/08/08 04:30:47 jon Exp $
*/
public class AnakiaTask extends MatchingTask
{
@@ -140,6 +141,7 @@
/** the file to get the velocity properties file */
private File velocityPropertiesFile = null;
+ /** the VelocityEngine instance to use */
private VelocityEngine ve = new VelocityEngine();
/**
@@ -148,6 +150,7 @@
public AnakiaTask()
{
builder = new SAXBuilder(DEFAULT_SAX_DRIVER_CLASS);
+ builder.setFactory(new AnakiaJDOMFactory());
}
/**
@@ -214,7 +217,6 @@
}
}
-
/**
* Allow people to set the path to the velocity.properties file
* This file is found relative to the path where the JVM was run.
@@ -285,7 +287,9 @@
{
projectFile = new File(baseDir, projectAttribute);
if (projectFile.exists())
+ {
projectFileLastModified = projectFile.lastModified();
+ }
else
{
log ("Project file is defined, but could not be located: " +
@@ -293,7 +297,8 @@
projectFile = null;
}
}
-
+
+ Document projectDocument = null;
try
{
if ( velocityPropertiesFile.exists() )
@@ -304,13 +309,17 @@
{
ve.setProperty( RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
templatePath);
-
ve.init();
}
// get the last modification of the VSL stylesheet
styleSheetLastModified = ve.getTemplate( style ).getLastModified();
-
+
+ // Build the Project file document
+ if (projectFile != null)
+ {
+ projectDocument = builder.build(projectFile);
+ }
}
catch (Exception e)
{
@@ -325,14 +334,15 @@
list = scanner.getIncludedFiles();
for (int i = 0;i < list.length; ++i)
{
- process( baseDir, list[i], destDir );
+ process( baseDir, list[i], destDir, projectDocument );
}
}
/**
* Process an XML file using Velocity
*/
- private void process(File baseDir, String xmlFile, File destDir)
+ private void process(File baseDir, String xmlFile, File destDir,
+ Document projectDocument)
throws BuildException
{
File outFile=null;
@@ -360,13 +370,7 @@
// Build the JDOM Document
Document root = builder.build(inFile);
- // Build the Project file document
- // FIXME: this should happen in the execute method since
- // it really only needs to be done once
- Document projectDocument = null;
- if (projectFile != null)
- projectDocument = builder.build(projectFile);
-
+
// Shove things into the Context
VelocityContext context = new VelocityContext();
@@ -394,8 +398,10 @@
// only put this into the context if it exists.
if (projectDocument != null)
+ {
context.put ("project", projectDocument.getRootElement());
-
+ }
+
// Process the VSL template with the context and write out
// the result as the outFile.
writer = new BufferedWriter(new OutputStreamWriter(
@@ -439,7 +445,10 @@
catch (Throwable e)
{
// log("Failed to process " + inFile, Project.MSG_INFO);
- if (outFile != null ) outFile.delete();
+ if (outFile != null)
+ {
+ outFile.delete();
+ }
e.printStackTrace();
}
finally
@@ -475,10 +484,15 @@
{
sb.append ("../");
}
+
if (sb.toString().length() > 0)
+ {
return StringUtils.chop(sb.toString(), 1);
+ }
else
- return ".";
+ {
+ return ".";
+ }
}
/**
1.5 +12 -12
jakarta-velocity/src/java/org/apache/velocity/anakia/TreeWalker.java
Index: TreeWalker.java
===================================================================
RCS file:
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/anakia/TreeWalker.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- TreeWalker.java 2001/03/20 00:47:44 1.4
+++ TreeWalker.java 2001/08/08 04:30:47 1.5
@@ -54,8 +54,9 @@
* <http://www.apache.org/>.
*/
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
-import java.util.Vector;
import org.jdom.Document;
import org.jdom.Element;
@@ -67,16 +68,15 @@
* into allElements() and stores each node of the tree
* in a Vector which allElements() returns as a result of its
* execution. You can then use a #foreach in Velocity to walk
- * over the Vector and visit each Element node.
+ * over the Vector and visit each Element node. However, you can
+ * achieve the same effect by calling <code>element.selectNodes("//*")</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jon S. Stevens</a>
- * @version $Id: TreeWalker.java,v 1.4 2001/03/20 00:47:44 jon Exp $
+ * @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
+ * @version $Id: TreeWalker.java,v 1.5 2001/08/08 04:30:47 jon Exp $
*/
public class TreeWalker
{
- /** the cache of Element objects */
- private Vector theElements = null;
-
/**
* Empty constructor
*/
@@ -91,24 +91,24 @@
* @param Element the starting Element node
* @return Vector a vector of Element nodes
*/
- public Vector allElements(Element e)
+ public NodeList allElements(Element e)
{
- theElements = new Vector();
- treeWalk (e);
- return this.theElements;
+ ArrayList theElements = new ArrayList();
+ treeWalk (e, theElements);
+ return new NodeList(theElements, false);
}
/**
* A recursive method to walk the Element tree.
* @param Element the current Element
*/
- private final void treeWalk(Element e)
+ private final void treeWalk(Element e, Collection theElements )
{
for (Iterator i=e.getChildren().iterator(); i.hasNext(); )
{
Element child = (Element)i.next();
theElements.add(child);
- treeWalk(child);
+ treeWalk(child, theElements);
}
}
}
1.12 +22 -10
jakarta-velocity/src/java/org/apache/velocity/anakia/XPathTool.java
Index: XPathTool.java
===================================================================
RCS file:
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/anakia/XPathTool.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- XPathTool.java 2001/08/07 22:30:15 1.11
+++ XPathTool.java 2001/08/08 04:30:47 1.12
@@ -88,10 +88,25 @@
* $title.getValue()
* #end
* </pre>
+ * <p>
+ * In newer Anakia builds, this class is obsoleted in favor of calling
+ * <code>selectNodes()</code> on the element directly:
+ * <pre>
+ * #set $authors = $root.selectNodes("document/author")
+ * #foreach ($author in $authors)
+ * $author.getValue()
+ * #end
+ * #set $chapterTitles = $root.selectNodes("document/chapter/@title")
+ * #foreach ($title in $chapterTitles)
+ * $title.getValue()
+ * #end
+ * </pre>
+ * <p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">bob mcwhirter</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Jon S. Stevens</a>
- * @version $Id: XPathTool.java,v 1.11 2001/08/07 22:30:15 geirm Exp $
+ * @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
+ * @version $Id: XPathTool.java,v 1.12 2001/08/08 04:30:47 jon Exp $
*/
public class XPathTool
{
@@ -113,12 +128,11 @@
*
* @return A list of selected nodes
*/
- public List applyTo(String xpathSpec,
+ public NodeList applyTo(String xpathSpec,
Document doc)
{
//RuntimeSingleton.info("XPathTool::applyTo(String, Document)");
- XPath xpath = new XPath( xpathSpec );
- return xpath.applyTo( doc );
+ return new NodeList(XPathCache.getXPath(xpathSpec).applyTo( doc ), false);
}
/**
@@ -129,12 +143,11 @@
*
* @return A list of selected nodes
*/
- public List applyTo(String xpathSpec,
+ public NodeList applyTo(String xpathSpec,
Element elem)
{
//RuntimeSingleton.info("XPathTool::applyTo(String, Element)");
- XPath xpath = new XPath(xpathSpec);
- return xpath.applyTo( elem );
+ return new NodeList(XPathCache.getXPath(xpathSpec).applyTo( elem ), false);
}
/**
@@ -145,12 +158,11 @@
*
* @return A list of selected nodes
*/
- public List applyTo(String xpathSpec,
+ public NodeList applyTo(String xpathSpec,
List nodeSet)
{
//RuntimeSingleton.info("XPathTool::applyTo(String, List)");
- XPath xpath = new XPath(xpathSpec);
- return xpath.applyTo( nodeSet );
+ return new NodeList(XPathCache.getXPath(xpathSpec).applyTo( nodeSet ),
false);
}
}
1.1
jakarta-velocity/src/java/org/apache/velocity/anakia/AnakiaElement.java
Index: AnakiaElement.java
===================================================================
package org.apache.velocity.anakia;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.output.XMLOutputter;
import com.werken.xpath.XPath;
import java.util.List;
/**
* A JDOM {@link Element} that is tailored for Anakia needs. It has
* {@link #selectNodes(String}} method as well as a {@link #toString()} that
* outputs the XML serialized form of the element. This way it acts in much the
* same way as a single-element {@link NodeList} would.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
* @version $Id: AnakiaElement.java,v 1.1 2001/08/08 04:30:47 jon Exp $
*/
public class AnakiaElement extends Element
{
private static final XMLOutputter DEFAULT_OUTPUTTER = new XMLOutputter();
/**
* <p>
* This will create a new <code>AnakiaElement</code>
* with the supplied (local) name, and define
* the <code>{@link Namespace}</code> to be used.
* If the provided namespace is null, the element will have
* no namespace.
* </p>
*
* @param name <code>String</code> name of element.
* @namespace <code>Namespace</code> to put element in.
*/
public AnakiaElement(String name, Namespace namespace)
{
super(name, namespace);
}
/**
* <p>
* This will create an <code>AnakiaElement</code> in no
* <code>{@link Namespace}</code>.
* </p>
*
* @param name <code>String</code> name of element.
*/
public AnakiaElement(String name)
{
super(name);
}
/**
* <p>
* This will create a new <code>AnakiaElement</code> with
* the supplied (local) name, and specifies the URI
* of the <code>{@link Namespace}</code> the <code>Element</code>
* should be in, resulting it being unprefixed (in the default
* namespace).
* </p>
*
* @param name <code>String</code> name of element.
* @param uri <code>String</code> URI for <code>Namespace</code> element
* should be in.
*/
public AnakiaElement(String name, String uri)
{
super(name, uri);
}
/**
* <p>
* This will create a new <code>AnakiaElement</code> with
* the supplied (local) name, and specifies the prefix and URI
* of the <code>{@link Namespace}</code> the <code>Element</code>
* should be in.
* </p>
*
* @param name <code>String</code> name of element.
* @param uri <code>String</code> URI for <code>Namespace</code> element
* should be in.
*/
public AnakiaElement(String name, String prefix, String uri)
{
super(name, prefix, uri);
}
/**
* Applies an XPath expression to this element and returns the resulting
* node list. In order for this method to work, your application must have
* access to <a href="http://code.werken.com">werken.xpath</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 template, this ensures that each XPath
* expression is parsed only once during the lifetime of the template that
* first invoked it.
* @param xpathExpression the XPath expression you wish to apply
* @return a NodeList representing the nodes that are the result of
* application of the XPath to the current element. It can be empty.
*/
public NodeList selectNodes(String xpathExpression)
{
return new NodeList(XPathCache.getXPath(xpathExpression).applyTo(this),
false);
}
/**
* Returns the XML serialized form of this element, as produced by the default
* {@link XMLOutputter}.
*/
public String toString()
{
return DEFAULT_OUTPUTTER.outputString(this);
}
/**
* <p>
* This returns the full content of the element as a NodeList which
* may contain objects of type <code>String</code>, <code>Element</code>,
* <code>Comment</code>, <code>ProcessingInstruction</code>,
* <code>CDATA</code>, and <code>EntityRef</code>.
* The List returned is "live" in document order and modifications
* to it affect the element's actual contents. Whitespace content is
* returned in its entirety.
* </p>
*
* @return a <code>List</code> containing the mixed content of the
* element: may contain <code>String</code>,
* <code>{@link Element}</code>, <code>{@link Comment}</code>,
* <code>{@link ProcessingInstruction}</code>,
* <code>{@link CDATA}</code>, and
* <code>{@link EntityRef}</code> objects.
*/
public List getContent()
{
return new NodeList(super.getContent(), false);
}
/**
* <p>
* This returns a <code>NodeList</code> of all the child elements
* nested directly (one level deep) within this element, as
* <code>Element</code> objects. If this target element has no nested
* elements, an empty List is returned. The returned list is "live"
* in document order and changes to it affect the element's actual
* contents.
* </p>
* <p>
* This performs no recursion, so elements nested two levels
* deep would have to be obtained with:
* <pre>
* <code>
* Iterator itr = currentElement.getChildren().iterator();
* while (itr.hasNext()) {
* Element oneLevelDeep = (Element)nestedElements.next();
* List twoLevelsDeep = oneLevelDeep.getChildren();
* // Do something with these children
* }
* </code>
* </pre>
* </p>
*
* @return list of child <code>Element</code> objects for this element
*/
public List getChildren()
{
return new NodeList(super.getChildren(), false);
}
/**
* <p>
* This returns a <code>NodeList</code> of all the child elements
* nested directly (one level deep) within this element with the given
* local name and belonging to no namespace, returned as
* <code>Element</code> objects. If this target element has no nested
* elements with the given name outside a namespace, an empty List
* is returned. The returned list is "live" in document order
* and changes to it affect the element's actual contents.
* </p>
* <p>
* Please see the notes for <code>{@link #getChildren}</code>
* for a code example.
* </p>
*
* @param name local name for the children to match
* @return all matching child elements
*/
public List getChildren(String name)
{
return new NodeList(super.getChildren(name));
}
/**
* <p>
* This returns a <code>NodeList</code> of all the child elements
* nested directly (one level deep) within this element with the given
* local name and belonging to the given Namespace, returned as
* <code>Element</code> objects. If this target element has no nested
* elements with the given name in the given Namespace, an empty List
* is returned. The returned list is "live" in document order
* and changes to it affect the element's actual contents.
* </p>
* <p>
* Please see the notes for <code>{@link #getChildren}</code>
* for a code example.
* </p>
*
* @param name local name for the children to match
* @param ns <code>Namespace</code> to search within
* @return all matching child elements
*/
public List getChildren(String name, Namespace ns)
{
return new NodeList(super.getChildren(name, ns));
}
/**
* <p>
* This returns the complete set of attributes for this element, as a
* <code>NodeList</code> of <code>Attribute</code> objects in no particular
* order, or an empty list if there are none.
* The returned list is "live" and changes to it affect the
* element's actual attributes.
* </p>
*
* @return attributes for the element
*/
public List getAttributes()
{
return new NodeList(super.getAttributes());
}
}
1.1
jakarta-velocity/src/java/org/apache/velocity/anakia/AnakiaJDOMFactory.java
Index: AnakiaJDOMFactory.java
===================================================================
package org.apache.velocity.anakia;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.DefaultJDOMFactory;
/**
* A customized JDOMFactory for Anakia that produces {@link AnakiaElement}
* instances instead of ordinary JDOM {@link Element} instances.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
* @version $Id: AnakiaJDOMFactory.java,v 1.1 2001/08/08 04:30:47 jon Exp $
*/
class AnakiaJDOMFactory extends DefaultJDOMFactory
{
AnakiaJDOMFactory()
{
}
public Element element(String name, Namespace namespace)
{
return new AnakiaElement(name, namespace);
}
public Element element(String name)
{
return new AnakiaElement(name);
}
public Element element(String name, String uri)
{
return new AnakiaElement(name, uri);
}
public Element element(String name, String prefix, String uri)
{
return new AnakiaElement(name, prefix, uri);
}
}
1.1
jakarta-velocity/src/java/org/apache/velocity/anakia/NodeList.java
Index: NodeList.java
===================================================================
package org.apache.velocity.anakia;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import com.werken.xpath.XPath;
import java.io.Writer;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import org.jdom.*;
import org.jdom.output.*;
/**
* Provides a class for wrapping a list of JDOM objects primarily for use in
template
* engines and other kinds of text transformation tools.
* It has a {@link #toString()} method that will output the XML serialized form of
the
* nodes it contains - again focusing on template engine usage, as well as the
* {@link #selectNodes(String)} method that helps selecting a different set of nodes
* starting from the nodes in this list. The class also implements the {@link
java.util.List}
* interface by simply delegating calls to the contained list (the {@link
#subList(int, int)}
* method is implemented by delegating to the contained list and wrapping the
returned
* sublist into a <code>NodeList</code>).
*
* @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
* @version $Id: NodeList.java,v 1.1 2001/08/08 04:30:47 jon Exp $
*/
public class NodeList implements List, Cloneable
{
private static final AttributeXMLOutputter DEFAULT_OUTPUTTER =
new AttributeXMLOutputter();
/** The contained nodes */
private List nodes;
/**
* Creates an empty node list.
*/
public NodeList()
{
nodes = new ArrayList();
}
/**
* Creates a node list that holds a single {@link Document} node.
*/
public NodeList(Document document)
{
this((Object)document);
}
/**
* Creates a node list that holds a single {@link Element} node.
*/
public NodeList(Element element)
{
this((Object)element);
}
private NodeList(Object object)
{
if(object == null)
{
throw new IllegalArgumentException(
"Cannot construct NodeList with null.");
}
nodes = new ArrayList(1);
nodes.add(object);
}
/**
* Creates a node list that holds a list of nodes.
* @param nodes the list of nodes this template should hold. The created
* template will copy the passed nodes list, so changes to the passed list
* will not affect the model.
*/
public NodeList(List nodes)
{
this(nodes, true);
}
/**
* Creates a node list that holds a list of nodes.
* @param nodes the list of nodes this template should hold.
* @param copy if true, the created template will copy the passed nodes
* list, so changes to the passed list will not affect the model. If false,
* the model will reference the passed list and will sense changes in it,
* altough no operations on the list will be synchronized.
*/
public NodeList(List nodes, boolean copy)
{
if(nodes == null)
{
throw new IllegalArgumentException(
"Cannot initialize NodeList with null list");
}
this.nodes = copy ? new ArrayList(nodes) : nodes;
}
/**
* Retrieves the underlying list used to store the nodes. Note however, that
* you can fully use the underlying list through the <code>List</code> interface
* of this class itself. You would probably access the underlying list only for
* synchronization purposes.
*/
public List getList()
{
return nodes;
}
/**
* This method returns the string resulting from concatenation of string
* representations of its nodes. Each node is rendered using its XML
* serialization format. This greatly simplifies creating XML-transformation
* templates, as to output a node contained in variable x as XML fragment,
* you simply write ${x} in the template (or whatever your template engine
* uses as its expression syntax).
*/
public String toString()
{
if(nodes.isEmpty())
{
return "";
}
StringWriter sw = new StringWriter(nodes.size() * 128);
try
{
for(Iterator i = nodes.iterator(); i.hasNext();)
{
Object node = i.next();
if(node instanceof Element)
{
DEFAULT_OUTPUTTER.output((Element)node, sw);
}
else if(node instanceof Attribute)
{
DEFAULT_OUTPUTTER.output((Attribute)node, sw);
}
else if(node instanceof String)
{
DEFAULT_OUTPUTTER.output(node.toString(), sw);
}
else if(node instanceof Text)
{
DEFAULT_OUTPUTTER.output((Text)node, sw);
}
else if(node instanceof Document)
{
DEFAULT_OUTPUTTER.output((Document)node, sw);
}
else if(node instanceof ProcessingInstruction)
{
DEFAULT_OUTPUTTER.output((ProcessingInstruction)node, sw);
}
else if(node instanceof Comment)
{
DEFAULT_OUTPUTTER.output((Comment)node, sw);
}
else if(node instanceof CDATA)
{
DEFAULT_OUTPUTTER.output((CDATA)node, sw);
}
else if(node instanceof DocType)
{
DEFAULT_OUTPUTTER.output((DocType)node, sw);
}
else if(node instanceof EntityRef)
{
DEFAULT_OUTPUTTER.output((EntityRef)node, sw);
}
else
{
throw new IllegalArgumentException(
"Cannot process a " +
(node == null
? "null node"
: "node of class " + node.getClass().getName()));
}
}
}
catch(IOException e)
{
// Cannot happen as we work with a StringWriter in memory
throw new Error();
}
return sw.toString();
}
/**
* Returns a NodeList that contains the same nodes as this node list.
* @throws CloneNotSupportedException if the contained list's class does
* not have an accessible no-arg constructor.
*/
public Object clone()
throws CloneNotSupportedException
{
NodeList clonedList = (NodeList)super.clone();
clonedList.cloneNodes();
return clonedList;
}
private void cloneNodes()
throws CloneNotSupportedException
{
Class listClass = nodes.getClass();
try
{
List clonedNodes = (List)listClass.newInstance();
clonedNodes.addAll(nodes);
nodes = clonedNodes;
}
catch(IllegalAccessException e)
{
throw new CloneNotSupportedException("Cannot clone NodeList since"
+ " there is no accessible no-arg constructor on class "
+ listClass.getName());
}
catch(InstantiationException e)
{
// Cannot happen as listClass represents a concrete, non-primitive,
// non-array, non-void class - there's an instance of it in "nodes"
// which proves these assumptions.
throw new Error();
}
}
/**
* Returns the hash code of the contained list.
*/
public int hashCode()
{
return nodes.hashCode();
}
/**
* Tests for equality with another object.
* @param o the object to test for equality
* @return true if the other object is also a NodeList and their contained
* {@link List} objects evaluate as equals.
*/
public boolean equals(Object o)
{
return o instanceof NodeList
? ((NodeList)o).nodes.equals(nodes)
: false;
}
/**
* 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 to <a href="http://code.werken.com">werken.xpath</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 template, this ensures that each XPath
* expression is parsed only once during the lifetime of the template that
* first invoked it.
* @param xpathExpression the XPath expression you wish to apply
* @return a NodeList representing the nodes that are the result of
* application of the XPath to the current node list. It can be empty.
*/
public NodeList selectNodes(String xpathString)
{
return new NodeList(XPathCache.getXPath(xpathString).applyTo(nodes), false);
}
// List methods implemented hereafter
public boolean add(Object o)
{
return nodes.add(o);
}
public void add(int index, Object o)
{
nodes.add(index, o);
}
public boolean addAll(Collection c)
{
return nodes.addAll(c);
}
public boolean addAll(int index, Collection c)
{
return nodes.addAll(index, c);
}
public void clear()
{
nodes.clear();
}
public boolean contains(Object o)
{
return nodes.contains(o);
}
public boolean containsAll(Collection c)
{
return nodes.containsAll(c);
}
public Object get(int index)
{
return nodes.get(index);
}
public int indexOf(Object o)
{
return nodes.indexOf(o);
}
public boolean isEmpty()
{
return nodes.isEmpty();
}
public Iterator iterator()
{
return nodes.iterator();
}
public int lastIndexOf(Object o)
{
return nodes.lastIndexOf(o);
}
public ListIterator listIterator()
{
return nodes.listIterator();
}
public ListIterator listIterator(int index)
{
return nodes.listIterator(index);
}
public Object remove(int index)
{
return nodes.remove(index);
}
public boolean remove(Object o)
{
return nodes.remove(o);
}
public boolean removeAll(Collection c)
{
return nodes.removeAll(c);
}
public boolean retainAll(Collection c)
{
return nodes.retainAll(c);
}
public Object set(int index, Object o)
{
return nodes.set(index, o);
}
public int size()
{
return nodes.size();
}
public List subList(int fromIndex, int toIndex)
{
return new NodeList(nodes.subList(fromIndex, toIndex));
}
public Object[] toArray()
{
return nodes.toArray();
}
public Object[] toArray(Object[] a)
{
return nodes.toArray(a);
}
/**
* A special subclass of XMLOutputter that will be used to output
* Attribute nodes. As a subclass of XMLOutputter it can use its protected
* method escapeAttributeEntities() to serialize the attribute
* appropriately.
*/
private static final class AttributeXMLOutputter extends XMLOutputter
{
public void output(Attribute attribute, Writer out)
throws IOException
{
out.write(" ");
out.write(attribute.getQualifiedName());
out.write("=");
out.write("\"");
out.write(escapeAttributeEntities(attribute.getValue()));
out.write("\"");
}
}
}
1.1
jakarta-velocity/src/java/org/apache/velocity/anakia/XPathCache.java
Index: XPathCache.java
===================================================================
package org.apache.velocity.anakia;
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Velocity", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import com.werken.xpath.XPath;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Provides a cache for XPath expressions. Used by {@link NodeList} and
* {@link AnakiaElement} to minimize XPath parsing in their
* <code>selectNodes()</code> methods.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Attila Szegedi</a>
* @version $Id: XPathCache.java,v 1.1 2001/08/08 04:30:47 jon Exp $
*/
class XPathCache
{
// Cache of already parsed XPath expressions, keyed by String representations
// of the expression as passed to getXPath().
private static final Map XPATH_CACHE = new WeakHashMap();
private XPathCache()
{
}
/**
* Returns an XPath object representing the requested XPath expression.
* A cached object is returned if it already exists for the requested
expression.
* @param xpathString the XPath expression to parse
* @return the XPath object that represents the parsed XPath expression.
*/
static XPath getXPath(String xpathString)
{
XPath xpath = null;
synchronized(XPATH_CACHE)
{
xpath = (XPath)XPATH_CACHE.get(xpathString);
if(xpath == null)
{
xpath = new XPath(xpathString);
XPATH_CACHE.put(xpathString, xpath);
}
}
return xpath;
}
}