You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by wg...@apache.org on 2007/03/31 22:51:52 UTC

svn commit: r524478 [2/3] - in /velocity/anakia/trunk: ./ build/ src/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/anakia/ src/test/ src/test/org/ src/test/org/apache/ src/test/org/apache/anakia/ src/test/org/apache/anakia/test/ tes...

Added: velocity/anakia/trunk/src/java/org/apache/anakia/AnakiaTask.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/AnakiaTask.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/AnakiaTask.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/AnakiaTask.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,645 @@
+package org.apache.anakia;
+
+/*
+ * 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.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.util.StringUtils;
+
+import org.jdom.Document;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.jdom.output.Format;
+import org.xml.sax.SAXParseException;
+
+/**
+ * The purpose of this Ant Task is to allow you to use
+ * Velocity as an XML transformation tool like XSLT is.
+ * So, instead of using XSLT, you will be able to use this
+ * class instead to do your transformations. It works very
+ * similar in concept to Ant's <style> task.
+ * <p>
+ * You can find more documentation about this class on the
+ * Velocity
+ * <a href="http://velocity.apache.org/engine/devel/docs/anakia.html">Website</a>.
+ *
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @version $Id$
+ */
+public class AnakiaTask extends MatchingTask
+{
+    /** <code>{@link SAXBuilder}</code> instance to use */
+    SAXBuilder builder;
+
+    /** the destination directory */
+    private File destDir = null;
+
+    /** the base directory */
+    File baseDir = null;
+
+    /** the style= attribute */
+    private String style = null;
+
+    /** last modified of the style sheet */
+    private long styleSheetLastModified = 0;
+
+    /** the projectFile= attribute */
+    private String projectAttribute = null;
+
+    /** the File for the project.xml file */
+    private File projectFile = null;
+
+    /** last modified of the project file if it exists */
+    private long projectFileLastModified = 0;
+
+    /** check the last modified date on files. defaults to true */
+    private boolean lastModifiedCheck = true;
+
+    /** the default output extension is .html */
+    private String extension = ".html";
+
+    /** the template path */
+    private String templatePath = null;
+
+    /** the file to get the velocity properties file */
+    private File velocityPropertiesFile = null;
+
+    /** the VelocityEngine instance to use */
+    private VelocityEngine ve = new VelocityEngine();
+
+    /** the Velocity subcontexts */
+    private List contexts = new LinkedList();
+
+    /**
+     * Constructor creates the SAXBuilder.
+     */
+    public AnakiaTask()
+    {
+        builder = new SAXBuilder();
+        builder.setFactory(new AnakiaJDOMFactory());
+    }
+
+    /**
+     * Set the base directory.
+     * @param dir
+     */
+    public void setBasedir(File dir)
+    {
+        baseDir = dir;
+    }
+
+    /**
+     * Set the destination directory into which the VSL result
+     * files should be copied to
+     * @param dir the name of the destination directory
+     */
+    public void setDestdir(File dir)
+    {
+        destDir = dir;
+    }
+
+    /**
+     * Allow people to set the default output file extension
+     * @param extension
+     */
+    public void setExtension(String extension)
+    {
+        this.extension = extension;
+    }
+
+    /**
+     * Allow people to set the path to the .vsl file
+     * @param style
+     */
+    public void setStyle(String style)
+    {
+        this.style = style;
+    }
+
+    /**
+     * Allow people to set the path to the project.xml file
+     * @param projectAttribute
+     */
+    public void setProjectFile(String projectAttribute)
+    {
+        this.projectAttribute = projectAttribute;
+    }
+
+    /**
+     * Set the path to the templates.
+     * The way it works is this:
+     * If you have a Velocity.properties file defined, this method
+     * will <strong>override</strong> whatever is set in the
+     * Velocity.properties file. This allows one to not have to define
+     * a Velocity.properties file, therefore using Velocity's defaults
+     * only.
+     * @param templatePath
+     */
+
+    public void setTemplatePath(File templatePath)
+     {
+         try
+         {
+             this.templatePath = templatePath.getCanonicalPath();
+         }
+         catch (java.io.IOException ioe)
+         {
+             throw new BuildException(ioe);
+         }
+     }
+
+    /**
+     * Allow people to set the path to the velocity.properties file
+     * This file is found relative to the path where the JVM was run.
+     * For example, if build.sh was executed in the ./build directory,
+     * then the path would be relative to this directory.
+     * This is optional based on the setting of setTemplatePath().
+     * @param velocityPropertiesFile
+     */
+    public void setVelocityPropertiesFile(File velocityPropertiesFile)
+    {
+        this.velocityPropertiesFile = velocityPropertiesFile;
+    }
+
+    /**
+     * Turn on/off last modified checking. by default, it is on.
+     * @param lastmod
+     */
+    public void setLastModifiedCheck(String lastmod)
+    {
+        if (lastmod.equalsIgnoreCase("false") || lastmod.equalsIgnoreCase("no")
+                || lastmod.equalsIgnoreCase("off"))
+        {
+            this.lastModifiedCheck = false;
+        }
+    }
+
+    /**
+     * Main body of the application
+     * @throws BuildException
+     */
+    public void execute () throws BuildException
+    {
+        DirectoryScanner scanner;
+        String[]         list;
+
+        if (baseDir == null)
+        {
+            baseDir = project.resolveFile(".");
+        }
+        if (destDir == null )
+        {
+            String msg = "destdir attribute must be set!";
+            throw new BuildException(msg);
+        }
+        if (style == null)
+        {
+            throw new BuildException("style attribute must be set!");
+        }
+
+        if (velocityPropertiesFile == null)
+        {
+            velocityPropertiesFile = new File("velocity.properties");
+        }
+
+        /*
+         * If the props file doesn't exist AND a templatePath hasn't
+         * been defined, then throw the exception.
+         */
+        if ( !velocityPropertiesFile.exists() && templatePath == null )
+        {
+            throw new BuildException ("No template path and could not " +
+                "locate velocity.properties file: " +
+                velocityPropertiesFile.getAbsolutePath());
+        }
+
+        log("Transforming into: " + destDir.getAbsolutePath(), Project.MSG_INFO);
+
+        // projectFile relative to baseDir
+        if (projectAttribute != null && projectAttribute.length() > 0)
+        {
+            projectFile = new File(baseDir, projectAttribute);
+            if (projectFile.exists())
+            {
+                projectFileLastModified = projectFile.lastModified();
+            }
+            else
+            {
+                log ("Project file is defined, but could not be located: " +
+                    projectFile.getAbsolutePath(), Project.MSG_INFO );
+                projectFile = null;
+            }
+        }
+
+        Document projectDocument = null;
+        try
+        {
+            if ( velocityPropertiesFile.exists() )
+            {
+                String file = velocityPropertiesFile.getAbsolutePath();
+                ExtendedProperties config = new ExtendedProperties(file);
+                ve.setExtendedProperties(config);
+            }
+
+            // override the templatePath if it exists
+            if (templatePath != null && templatePath.length() > 0)
+            {
+                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)
+        {
+            log("Error: " + e.toString(), Project.MSG_INFO);
+            throw new BuildException(e);
+        }
+
+        // find the files/directories
+        scanner = getDirectoryScanner(baseDir);
+
+        // get a list of files to work on
+        list = scanner.getIncludedFiles();
+        for (int i = 0;i < list.length; ++i)
+        {
+            process(list[i], projectDocument );
+        }
+
+    }
+
+    /**
+     * Process an XML file using Velocity
+     */
+    private void process(String xmlFile, Document projectDocument)
+        throws BuildException
+    {
+        File   outFile=null;
+        File   inFile=null;
+        Writer writer = null;
+        try
+        {
+            // the current input file relative to the baseDir
+            inFile = new File(baseDir,xmlFile);
+            // the output file relative to basedir
+            outFile = new File(destDir,
+                            xmlFile.substring(0,
+                            xmlFile.lastIndexOf('.')) + extension);
+
+            // only process files that have changed
+            if (lastModifiedCheck == false ||
+                    (inFile.lastModified() > outFile.lastModified() ||
+                    styleSheetLastModified > outFile.lastModified() ||
+                    projectFileLastModified > outFile.lastModified() ||
+                    userContextsModifed(outFile.lastModified())))
+            {
+                ensureDirectoryFor( outFile );
+
+                //-- command line status
+                log("Input:  " + xmlFile, Project.MSG_INFO );
+
+                // Build the JDOM Document
+                Document root = builder.build(inFile);
+
+                // Shove things into the Context
+                VelocityContext context = new VelocityContext();
+
+                /*
+                 *  get the property TEMPLATE_ENCODING
+                 *  we know it's a string...
+                 */
+                String encoding = (String) ve.getProperty( RuntimeConstants.OUTPUT_ENCODING );
+                if (encoding == null || encoding.length() == 0
+                    || encoding.equals("8859-1") || encoding.equals("8859_1"))
+                {
+                    encoding = "ISO-8859-1";
+                }
+
+                Format f = Format.getRawFormat();
+                f.setEncoding(encoding);
+
+                OutputWrapper ow = new OutputWrapper(f);
+
+                context.put ("root", root.getRootElement());
+                context.put ("xmlout", ow );
+                context.put ("relativePath", getRelativePath(xmlFile));
+                context.put ("treeWalk", new TreeWalker());
+                context.put ("xpath", new XPathTool() );
+                context.put ("escape", new Escape() );
+                context.put ("date", new java.util.Date() );
+
+                /**
+                 * only put this into the context if it exists.
+                 */
+                if (projectDocument != null)
+                {
+                    context.put ("project", projectDocument.getRootElement());
+                }
+
+                /**
+                 *  Add the user subcontexts to the to context
+                 */
+                for (Iterator iter = contexts.iterator(); iter.hasNext();)
+                {
+                    Context subContext = (Context) iter.next();
+                    if (subContext == null)
+                    {
+                        throw new BuildException("Found an undefined SubContext!");
+                    }
+
+                    if (subContext.getContextDocument() == null)
+                    {
+                        throw new BuildException("Could not build a subContext for " + subContext.getName());
+                    }
+
+                    context.put(subContext.getName(), subContext
+                            .getContextDocument().getRootElement());
+                }
+
+                /**
+                 * Process the VSL template with the context and write out
+                 * the result as the outFile.
+                 */
+                writer = new BufferedWriter(new OutputStreamWriter(
+                                            new FileOutputStream(outFile),
+                                                encoding));
+
+                /**
+                 * get the template to process
+                 */
+                Template template = ve.getTemplate(style);
+                template.merge(context, writer);
+
+                log("Output: " + outFile, Project.MSG_INFO );
+            }
+        }
+        catch (JDOMException e)
+        {
+            outFile.delete();
+
+            if (e.getCause() != null)
+            {
+                Throwable rootCause = e.getCause();
+                if (rootCause instanceof SAXParseException)
+                {
+                    System.out.println("");
+                    System.out.println("Error: " + rootCause.getMessage());
+                    System.out.println(
+                        "       Line: " +
+                            ((SAXParseException)rootCause).getLineNumber() +
+                        " Column: " +
+                            ((SAXParseException)rootCause).getColumnNumber());
+                    System.out.println("");
+                }
+                else
+                {
+                    rootCause.printStackTrace();
+                }
+            }
+            else
+            {
+                e.printStackTrace();
+            }
+        }
+        catch (Throwable e)
+        {
+            if (outFile != null)
+            {
+                outFile.delete();
+            }
+            e.printStackTrace();
+        }
+        finally
+        {
+            if (writer != null)
+            {
+                try
+                {
+                    writer.flush();
+                }
+                catch (IOException e)
+                {
+                    // Do nothing
+                }
+
+                try
+                {
+                    writer.close();
+                }
+                catch (IOException e)
+                {
+                    // Do nothing
+                }
+            }
+        }
+    }
+
+    /**
+     * Hacky method to figure out the relative path
+     * that we are currently in. This is good for getting
+     * the relative path for images and anchor's.
+     */
+    private String getRelativePath(String file)
+    {
+        if (file == null || file.length()==0)
+            return "";
+        StringTokenizer st = new StringTokenizer(file, "/\\");
+        // needs to be -1 cause ST returns 1 even if there are no matches. huh?
+        int slashCount = st.countTokens() - 1;
+        StringBuffer sb = new StringBuffer();
+        for (int i=0;i<slashCount ;i++ )
+        {
+            sb.append ("../");
+        }
+
+        if (sb.toString().length() > 0)
+        {
+            return StringUtils.chop(sb.toString(), 1);
+        }
+
+        return ".";
+    }
+
+    /**
+     * create directories as needed
+     */
+    private void ensureDirectoryFor( File targetFile ) throws BuildException
+    {
+        File directory = new File( targetFile.getParent() );
+        if (!directory.exists())
+        {
+            if (!directory.mkdirs())
+            {
+                throw new BuildException("Unable to create directory: "
+                                         + directory.getAbsolutePath() );
+            }
+        }
+    }
+
+
+    /**
+     * Check to see if user context is modified.
+     */
+    private boolean userContextsModifed(long lastModified)
+    {
+        for (Iterator iter = contexts.iterator(); iter.hasNext();)
+        {
+            AnakiaTask.Context ctx = (AnakiaTask.Context) iter.next();
+            if(ctx.getLastModified() > lastModified)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Create a new context.
+     * @return A new context.
+     */
+    public Context createContext()
+    {
+        Context context = new Context();
+        contexts.add(context);
+        return context;
+    }
+
+
+    /**
+     * A context implementation that loads all values from an XML file.
+     */
+    public class Context
+    {
+
+        private String name;
+        private Document contextDoc = null;
+        private String file;
+
+        /**
+         * Public constructor.
+         */
+        public Context()
+        {
+        }
+
+        /**
+         * Get the name of the context.
+         * @return The name of the context.
+         */
+        public String getName()
+        {
+            return name;
+        }
+
+        /**
+         * Set the name of the context.
+         * @param name
+         *
+         * @throws IllegalArgumentException if a reserved word is used as a
+         * name, specifically any of "relativePath", "treeWalk", "xpath",
+         * "escape", "date", or "project"
+         */
+        public void setName(String name)
+        {
+            if (name.equals("relativePath") ||
+                    name.equals("treeWalk") ||
+                    name.equals("xpath") ||
+                    name.equals("escape") ||
+                    name.equals("date") ||
+                    name.equals("project"))
+            {
+
+                    throw new IllegalArgumentException("Context name '" + name
+                            + "' is reserved by Anakia");
+            }
+
+            this.name = name;
+        }
+
+        /**
+         * Build the context based on a file path.
+         * @param file
+         */
+        public void setFile(String file)
+        {
+            this.file = file;
+        }
+
+        /**
+         * Retrieve the time the source file was last modified.
+         * @return The time the source file was last modified.
+         */
+        public long getLastModified()
+        {
+            return new File(baseDir, file).lastModified();
+        }
+
+        /**
+         * Retrieve the context document object.
+         * @return The context document object.
+         */
+        public Document getContextDocument()
+        {
+            if (contextDoc == null)
+            {
+                File contextFile = new File(baseDir, file);
+
+                try
+                {
+                    contextDoc = builder.build(contextFile);
+                }
+                catch (Exception e)
+                {
+                    throw new BuildException(e);
+                }
+            }
+            return contextDoc;
+        }
+    }
+
+}

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/AnakiaTask.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/AnakiaTask.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/java/org/apache/anakia/Escape.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/Escape.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/Escape.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/Escape.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,96 @@
+package org.apache.anakia;
+
+/*
+ * 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.    
+ */
+
+/**
+ * This class is for escaping CDATA sections. The code was
+ * "borrowed" from the JDOM code. Also included is escaping
+ * the " -> &amp;quot; character and the conversion of newlines
+ * to the platform line separator.
+ *
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Id$
+ */
+public class Escape
+{
+    /**
+     *
+     */
+    public static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+    /**
+     * Empty constructor
+     */
+    public Escape()
+    {
+        // left blank on purpose
+    }
+
+    /**
+     * Do the escaping.
+     * @param st
+     * @return The escaped text.
+     */
+    public static final String getText(String st)
+    {
+        StringBuffer buff = new StringBuffer();
+        char[] block = st.toCharArray();
+        String stEntity = null;
+        int i, last;
+
+        for (i=0, last=0; i < block.length; i++)
+        {
+            switch(block[i])
+            {
+                case '<' :
+                    stEntity = "&lt;";
+                    break;
+                case '>' :
+                    stEntity = "&gt;";
+                    break;
+                case '&' :
+                    stEntity = "&amp;";
+                    break;
+                case '"' :
+                    stEntity = "&quot;";
+                    break;
+                case '\n' :
+                    stEntity = LINE_SEPARATOR;
+                    break;
+                default :
+                    /* no-op */
+                    break;
+            }
+            if (stEntity != null)
+            {
+                buff.append(block, last, i - last);
+                buff.append(stEntity);
+                stEntity = null;
+                last = i + 1;
+            }
+        }
+        if(last < block.length)
+        {
+            buff.append(block, last, i - last);
+        }
+        return buff.toString();
+    }
+}

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/Escape.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/Escape.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/java/org/apache/anakia/NodeList.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/NodeList.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/NodeList.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/NodeList.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,509 @@
+package org.apache.anakia;
+
+/*
+ * 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.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.jdom.Attribute;
+import org.jdom.CDATA;
+import org.jdom.Comment;
+import org.jdom.DocType;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.EntityRef;
+import org.jdom.ProcessingInstruction;
+import org.jdom.Text;
+import org.jdom.output.XMLOutputter;
+
+/**
+ * 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:szegedia@freemail.hu">Attila Szegedi</a>
+ * @version $Id$
+ */
+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.
+     * @param document
+     */
+    public NodeList(Document document)
+    {
+        this((Object)document);
+    }
+
+    /**
+     * Creates a node list that holds a single {@link Element} node.
+     * @param element
+     */
+    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.
+     * @return The internal node List.
+     */
+    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).
+     * @return The Nodelist as printable object.
+     */
+    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 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.
+     * @return A clone of this 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.
+     * @return The hashcode of the 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 xpathString 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
+
+    /**
+     * @see java.util.List#add(java.lang.Object)
+     */
+    public boolean add(Object o)
+    {
+        return nodes.add(o);
+    }
+
+    /**
+     * @see java.util.List#add(int, java.lang.Object)
+     */
+    public void add(int index, Object o)
+    {
+        nodes.add(index, o);
+    }
+
+    /**
+     * @see java.util.List#addAll(java.util.Collection)
+     */
+    public boolean addAll(Collection c)
+    {
+        return nodes.addAll(c);
+    }
+
+    /**
+     * @see java.util.List#addAll(int, java.util.Collection)
+     */
+    public boolean addAll(int index, Collection c)
+    {
+        return nodes.addAll(index, c);
+    }
+
+    /**
+     * @see java.util.List#clear()
+     */
+    public void clear()
+    {
+        nodes.clear();
+    }
+
+    /**
+     * @see java.util.List#contains(java.lang.Object)
+     */
+    public boolean contains(Object o)
+    {
+        return nodes.contains(o);
+    }
+
+    /**
+     * @see java.util.List#containsAll(java.util.Collection)
+     */
+    public boolean containsAll(Collection c)
+    {
+        return nodes.containsAll(c);
+    }
+
+    /**
+     * @see java.util.List#get(int)
+     */
+    public Object get(int index)
+    {
+        return nodes.get(index);
+    }
+
+    /**
+     * @see java.util.List#indexOf(java.lang.Object)
+     */
+    public int indexOf(Object o)
+    {
+        return nodes.indexOf(o);
+    }
+
+    /**
+     * @see java.util.List#isEmpty()
+     */
+    public boolean isEmpty()
+    {
+        return nodes.isEmpty();
+    }
+
+    /**
+     * @see java.util.List#iterator()
+     */
+    public Iterator iterator()
+    {
+        return nodes.iterator();
+    }
+
+    /**
+     * @see java.util.List#lastIndexOf(java.lang.Object)
+     */
+    public int lastIndexOf(Object o)
+    {
+        return nodes.lastIndexOf(o);
+    }
+
+    /**
+     * @see java.util.List#listIterator()
+     */
+    public ListIterator listIterator()
+    {
+        return nodes.listIterator();
+    }
+
+    /**
+     * @see java.util.List#listIterator(int)
+     */
+    public ListIterator listIterator(int index)
+    {
+        return nodes.listIterator(index);
+    }
+
+    /**
+     * @see java.util.List#remove(int)
+     */
+    public Object remove(int index)
+    {
+        return nodes.remove(index);
+    }
+
+    /**
+     * @see java.util.List#remove(java.lang.Object)
+     */
+    public boolean remove(Object o)
+    {
+        return nodes.remove(o);
+    }
+
+    /**
+     * @see java.util.List#removeAll(java.util.Collection)
+     */
+    public boolean removeAll(Collection c)
+    {
+        return nodes.removeAll(c);
+    }
+
+    /**
+     * @see java.util.List#retainAll(java.util.Collection)
+     */
+    public boolean retainAll(Collection c)
+    {
+        return nodes.retainAll(c);
+    }
+
+    /**
+     * @see java.util.List#set(int, java.lang.Object)
+     */
+    public Object set(int index, Object o)
+    {
+        return nodes.set(index, o);
+    }
+
+    /**
+     * @see java.util.List#size()
+     */
+    public int size()
+    {
+        return nodes.size();
+    }
+
+    /**
+     * @see java.util.List#subList(int, int)
+     */
+    public List subList(int fromIndex, int toIndex)
+    {
+        return new NodeList(nodes.subList(fromIndex, toIndex));
+    }
+
+    /**
+     * @see java.util.List#toArray()
+     */
+    public Object[] toArray()
+    {
+        return nodes.toArray();
+    }
+
+    /**
+     * @see java.util.List#toArray(java.lang.Object[])
+     */
+    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
+    {
+        /**
+         * @param attribute
+         * @param out
+         * @throws IOException
+         */
+        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("\"");
+        }
+    }
+}

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/NodeList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/NodeList.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/java/org/apache/anakia/OutputWrapper.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/OutputWrapper.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/OutputWrapper.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/OutputWrapper.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,82 @@
+package org.apache.anakia;
+
+/*
+ * 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.IOException;
+import java.io.StringWriter;
+
+import org.jdom.Element;
+import org.jdom.output.XMLOutputter;
+import org.jdom.output.Format;
+
+/**
+ * This class extends XMLOutputter in order to provide
+ * a way to walk an Element tree into a String.
+ *
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
+ * @version $Id$
+ */
+public class OutputWrapper extends XMLOutputter
+{
+    /**
+     * Empty constructor
+     */
+    public OutputWrapper()
+    {
+    }
+
+    /**
+     * @param f
+     */
+    public OutputWrapper(Format f)
+    {
+        super(f);
+    }
+
+    /**
+     * This method walks an Element tree into a String. The cool
+     * thing about it is that it will strip off the first Element.
+     * For example, if you have:
+     * <p>
+     * &lt;td&gt; foo &lt;strong&gt;bar&lt;/strong&gt; ack &lt;/td&gt;
+     * </p>
+     * It will output
+     * <p>
+     *  foo &lt;strong&gt;bar&lt;/strong&gt; ack &lt;/td&gt;
+     * </p>
+     * @param element
+     * @param strip
+     * @return The output string.
+     */
+    public String outputString(Element element, boolean strip)
+    {
+        StringWriter buff = new StringWriter();
+
+        try
+        {
+            outputElementContent(element, buff);
+        }
+        catch (IOException e)
+        {
+        }
+        return buff.toString();
+    }
+}

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/OutputWrapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/OutputWrapper.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/java/org/apache/anakia/TreeWalker.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/TreeWalker.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/TreeWalker.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/TreeWalker.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,77 @@
+package org.apache.anakia;
+
+/*
+ * 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.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.jdom.Element;
+
+/**
+ * This class allows you to walk a tree of JDOM Element objects.
+ * It first walks the tree itself starting at the Element passed
+ * 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. However, you can
+ * achieve the same effect by calling <code>element.selectNodes("//*")</code>.
+ *
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @version $Id$
+ */
+public class TreeWalker
+{
+    /**
+     * Empty constructor
+     */
+    public TreeWalker()
+    {
+        // Left blank
+    }
+
+    /**
+     * Creates a new Vector and walks the Element tree.
+     *
+     * @param e the starting Element node
+     * @return Vector a vector of Element nodes
+     */
+    public NodeList allElements(Element e)
+    {
+        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, Collection theElements )
+    {
+        for (Iterator i=e.getChildren().iterator(); i.hasNext(); )
+        {
+            Element child = (Element)i.next();
+            theElements.add(child);
+            treeWalk(child, theElements);
+        }
+    }
+}

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/TreeWalker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/TreeWalker.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/java/org/apache/anakia/XPathCache.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/XPathCache.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/XPathCache.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/XPathCache.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,64 @@
+package org.apache.anakia;
+
+/*
+ * 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 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:szegedia@freemail.hu">Attila Szegedi</a>
+ * @version $Id$
+ */
+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;
+    }
+}

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/XPathCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/XPathCache.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/java/org/apache/anakia/XPathTool.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/java/org/apache/anakia/XPathTool.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/java/org/apache/anakia/XPathTool.java (added)
+++ velocity/anakia/trunk/src/java/org/apache/anakia/XPathTool.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,131 @@
+package org.apache.anakia;
+
+/*
+ * 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.util.List;
+
+import org.jdom.Document;
+import org.jdom.Element;
+
+/**
+ * This class adds an entrypoint into XPath functionality,
+ * for Anakia.
+ * <p>
+ * All methods take a string XPath specification, along with
+ * a context, and produces a resulting java.util.List.
+ * <p>
+ * The W3C XPath Specification (http://www.w3.org/TR/xpath) refers
+ * to NodeSets repeatedly, but this implementation simply uses
+ * java.util.List to hold all Nodes.  A 'Node' is any object in
+ * a JDOM object tree, such as an org.jdom.Element, org.jdom.Document,
+ * or org.jdom.Attribute.
+ * <p>
+ * To use it in Velocity, do this:
+ * <p>
+ * <pre>
+ * #set $authors = $xpath.applyTo("document/author", $root)
+ * #foreach ($author in $authors)
+ *   $author.getValue()
+ * #end
+ * #set $chapterTitles = $xpath.applyTo("document/chapter/@title", $root)
+ * #foreach ($title in $chapterTitles)
+ *   $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:bob@werken.com">bob mcwhirter</a>
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @version $Id$
+ */
+public class XPathTool
+{
+    /**
+     * Constructor does nothing, as this is mostly
+     * just objectified static methods
+     */
+    public XPathTool()
+    {
+        //        RuntimeSingleton.info("XPathTool::XPathTool()");
+        // intentionally left blank
+    }
+
+    /**
+     * Apply an XPath to a JDOM Document
+     *
+     * @param xpathSpec The XPath to apply
+     * @param doc The Document context
+     *
+     * @return A list of selected nodes
+     */
+    public NodeList applyTo(String xpathSpec,
+                        Document doc)
+    {
+        //RuntimeSingleton.info("XPathTool::applyTo(String, Document)");
+        return new NodeList(XPathCache.getXPath(xpathSpec).applyTo( doc ), false);
+    }
+
+    /**
+     * Apply an XPath to a JDOM Element
+     *
+     * @param xpathSpec The XPath to apply
+     * @param elem The Element context
+     *
+     * @return A list of selected nodes
+     */
+    public NodeList applyTo(String xpathSpec,
+                        Element elem)
+    {
+        //RuntimeSingleton.info("XPathTool::applyTo(String, Element)");
+        return new NodeList(XPathCache.getXPath(xpathSpec).applyTo( elem ), false);
+    }
+
+    /**
+     * Apply an XPath to a nodeset
+     *
+     * @param xpathSpec The XPath to apply
+     * @param nodeSet The nodeset context
+     *
+     * @return A list of selected nodes
+     */
+    public NodeList applyTo(String xpathSpec,
+                        List nodeSet)
+    {
+        //RuntimeSingleton.info("XPathTool::applyTo(String, List)");
+        return new NodeList(XPathCache.getXPath(xpathSpec).applyTo( nodeSet ), false);
+    }
+}
+
+
+

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/XPathTool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/java/org/apache/anakia/XPathTool.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/test/org/apache/anakia/AnakiaTestCase.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/test/org/apache/anakia/AnakiaTestCase.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/test/org/apache/anakia/AnakiaTestCase.java (added)
+++ velocity/anakia/trunk/src/test/org/apache/anakia/AnakiaTestCase.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,118 @@
+package org.apache.anakia;
+
+
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.anakia.test.Log;
+import org.apache.anakia.test.TestUtil;
+
+
+/**
+ * This is a test case for Anakia. Right now, it simply will compare
+ * two index.html files together. These are produced as a result of
+ * first running Anakia and then running this test.
+ *
+ * @version $Id$
+ */
+public class AnakiaTestCase extends TestCase
+{
+    public static final String TEST_DIR_KEY = "test.compare.dir";
+    public static final String RESULTS_DIR_KEY = "test.results.dir";
+    
+    private String resultsDir;
+    private String compareDir;
+    
+    /**
+     * Creates a new instance.
+     *
+     */
+    public AnakiaTestCase(String name)
+    {
+        super(name);
+    }
+
+    public static Test suite()
+    {
+        return new TestSuite(AnakiaTestCase.class);
+    }
+    
+    /**
+     * Find the directories.
+     */
+    public void setUp()
+    {
+        resultsDir = System.getProperty(RESULTS_DIR_KEY, "bin/test") + "/anakia";
+        compareDir = System.getProperty(TEST_DIR_KEY, "test") + "/anakia/compare";
+    }
+
+    /**
+     * Runs the test. This is empty on purpose because the
+     * code to do the Anakia output is in the .xml file that runs
+     * this test.
+     */
+    public void testAnakiaResults ()
+            throws Exception
+    {
+        TestUtil.assureResultsDirectoryExists(resultsDir);
+
+        // compare with custom context
+        if (!TestUtil.compareFiles(
+                new File(compareDir, "index.html").toString(),
+                new File(resultsDir, "index.html").toString()))
+        {
+        fail("Anakia results are incorrect");
+        }
+        else
+        {
+            Log.log ("Passed (standard)!");
+        }
+    }
+
+    /**
+     * Runs the test. This is empty on purpose because the
+     * code to do the Anakia output is in the .xml file that runs
+     * this test.
+     */
+    public void testAnakiaCustomContextResults ()
+            throws Exception
+    {
+        TestUtil.assureResultsDirectoryExists(resultsDir);
+
+        // compare with custom context
+        if (!TestUtil.compareFiles(
+                new File(compareDir, "index.context.html").toString(),
+                new File(resultsDir, "index.context.html").toString()))
+        {
+        fail("Anakia results are incorrect");
+        }
+        else
+        {
+            Log.log ("Passed (custom context)!");
+        }
+    }
+
+
+}

Propchange: velocity/anakia/trunk/src/test/org/apache/anakia/AnakiaTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/test/org/apache/anakia/AnakiaTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/test/org/apache/anakia/test/Log.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/test/org/apache/anakia/test/Log.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/test/org/apache/anakia/test/Log.java (added)
+++ velocity/anakia/trunk/src/test/org/apache/anakia/test/Log.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,46 @@
+package org.apache.anakia.test;
+
+/*
+ * 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.
+ */
+
+/**
+* Simple class to log messages when running tests
+*
+* @version $Id$
+*/
+
+public class Log
+{
+
+    /**
+     * Do not instantiate.
+     */
+    private Log ()
+    {   
+    }
+
+    /**
+     * Output a message;
+     * @param message
+     */
+    public static void log(String message)
+    {
+         System.out.println(message);
+    }
+}

Propchange: velocity/anakia/trunk/src/test/org/apache/anakia/test/Log.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/test/org/apache/anakia/test/Log.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/src/test/org/apache/anakia/test/TestUtil.java
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/src/test/org/apache/anakia/test/TestUtil.java?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/src/test/org/apache/anakia/test/TestUtil.java (added)
+++ velocity/anakia/trunk/src/test/org/apache/anakia/test/TestUtil.java Sat Mar 31 13:51:49 2007
@@ -0,0 +1,119 @@
+package org.apache.anakia.test;
+
+/*
+ * 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;
+
+
+/**
+ * Miscellaneous routines to help with testing
+ *
+ * @version $Id$
+ */
+public class TestUtil
+{
+    
+    /**
+     * Do not instantiate.
+     */
+    private TestUtil()
+    {        
+    }
+
+    /**
+     * Assures that the results directory exists.  If the results directory
+     * cannot be created, fails the test.
+     */
+    public static void assureResultsDirectoryExists (String resultsDirectory)
+    {
+        File dir = new File(resultsDirectory);
+        if (!dir.exists())
+        {
+            Log.log("Template results directory does not exist");
+            if (dir.mkdirs())
+            {
+                Log.log("Created template results directory");
+            }
+            else
+            {
+                String errMsg = "Unable to create template results directory";
+                Log.log(errMsg);
+                throw new RuntimeException(errMsg);
+            }
+        }
+    }
+
+    /**
+     * Normalizes lines to account for platform differences.  Macs use
+     * a single \r, DOS derived operating systems use \r\n, and Unix
+     * uses \n.  Replace each with a single \n.
+     *
+     * @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
+     * @return source with all line terminations changed to Unix style
+     */
+    public static String normalizeNewlines (String source)
+    {
+        return source.replaceAll("\r[\r]?[\n]","\n");
+    }
+
+    /**
+     * Returns whether the processed template matches the
+     * content of the provided comparison file.
+     *
+     * @return Whether the output matches the contents
+     *         of the comparison file.
+     *
+     * @exception Exception Test failure condition.
+     */
+    public static boolean compareFiles(String compareFileName, String resultsFileName)
+        throws Exception
+    {
+        Log.log("Comparing result file '" + resultsFileName + "' with compare file '" + compareFileName + "'"); 
+        String resultText = org.apache.velocity.util.StringUtils.fileContentsToString(resultsFileName);
+
+        return compareTextWithFile(resultText, compareFileName);
+    }
+
+    
+    /**
+     * Returns whether the processed template matches the
+     * content of the provided comparison file.
+     *
+     * @return Whether the output matches the contents
+     *         of the comparison file.
+     *
+     * @exception Exception Test failure condition.
+     */
+    public static boolean compareTextWithFile(String resultText, String compareFileName)
+        throws Exception
+    {
+        String compareText = org.apache.velocity.util.StringUtils.fileContentsToString(compareFileName);
+
+        /*
+         *  normalize each wrt newline
+         */
+
+        String normalizedResultText = normalizeNewlines(resultText);
+        String normalizedCompareText = normalizeNewlines(compareText);
+        return normalizedResultText.equals( normalizedCompareText );
+    }
+
+    
+}

Propchange: velocity/anakia/trunk/src/test/org/apache/anakia/test/TestUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/src/test/org/apache/anakia/test/TestUtil.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/compare/index.context.html
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/compare/index.context.html?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/compare/index.context.html (added)
+++ velocity/anakia/trunk/test/anakia/compare/index.context.html Sat Mar 31 13:51:49 2007
@@ -0,0 +1,65 @@
+<!-- Content Stylesheet for Site -->
+
+        
+<!-- start the processing -->
+    <!-- ====================================================================== -->
+    <!-- Main Page Section -->
+    <!-- ====================================================================== -->
+    <html>
+        <head>
+            <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
+                        <meta name="author" value="$au.getText()">
+            <meta name="email" value="$em.getValue()">
+            <title>The Jakarta Project</title>
+        </head>
+
+        <body bgcolor="#ffffff" text="#000000" link="#525D76">        
+            <table border="1">
+                <tr>
+                <td>
+                            <strong>Home</strong>
+        <ul>
+                    <li>    <a href="./index.html">Front Page</a>
+</li>
+                </ul>
+            <strong>About</strong>
+        <ul>
+                    <li>    <a href="./about/index.html">About</a>
+</li>
+                </ul>
+        
+            	<strong>Other Context</strong>
+    	<ul>
+    	    		<li>    <a href="./index.html">Front Page OC</a>
+</li>
+    	    	</ul>
+        	<strong>Other About</strong>
+    	<ul>
+    	    		<li>    <a href="./about/index.html">About OC</a>
+</li>
+    	    	</ul>
+                    </td>
+                <td>
+                                                                                                        <p> This is an example template that gets processed. </p>
+                                                                                                <img src="./images/velocity.gif" width="329" height="105" align="">
+                                                                                                <table border="1">
+                <tr>
+                    <td> It even has a table in it! </td>
+                </tr>
+            </table>
+                                                                                                <h3>And an h3 tag</h3>
+                                                                                                                                    <p> here is another section </p>
+                                                                                                                                    <p>
+                <a href="./about/index.html">A link to a sub page</a>
+            </p>
+                                                                            </td>
+                </tr>
+            </table>
+        </body>
+    </html>
+<!-- end the processing -->
+
+
+
+
+

Propchange: velocity/anakia/trunk/test/anakia/compare/index.context.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/test/anakia/compare/index.context.html
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/compare/index.html
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/compare/index.html?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/compare/index.html (added)
+++ velocity/anakia/trunk/test/anakia/compare/index.html Sat Mar 31 13:51:49 2007
@@ -0,0 +1,55 @@
+<!-- Content Stylesheet for Site -->
+
+        
+<!-- start the processing -->
+    <!-- ====================================================================== -->
+    <!-- Main Page Section -->
+    <!-- ====================================================================== -->
+    <html>
+        <head>
+            <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
+                        <meta name="author" value="$au.getText()">
+            <meta name="email" value="$em.getValue()">
+            <title>The Jakarta Project</title>
+        </head>
+
+        <body bgcolor="#ffffff" text="#000000" link="#525D76">        
+            <table border="1">
+                <tr>
+                <td>
+                            <strong>Home</strong>
+        <ul>
+                    <li>    <a href="./index.html">Front Page</a>
+</li>
+                </ul>
+            <strong>About</strong>
+        <ul>
+                    <li>    <a href="./about/index.html">About</a>
+</li>
+                </ul>
+        
+                        </td>
+                <td>
+                                                                                                        <p> This is an example template that gets processed. </p>
+                                                                                                <img src="./images/velocity.gif" width="329" height="105" align="">
+                                                                                                <table border="1">
+                <tr>
+                    <td> It even has a table in it! </td>
+                </tr>
+            </table>
+                                                                                                <h3>And an h3 tag</h3>
+                                                                                                                                    <p> here is another section </p>
+                                                                                                                                    <p>
+                <a href="./about/index.html">A link to a sub page</a>
+            </p>
+                                                                            </td>
+                </tr>
+            </table>
+        </body>
+    </html>
+<!-- end the processing -->
+
+
+
+
+

Propchange: velocity/anakia/trunk/test/anakia/compare/index.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/test/anakia/compare/index.html
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/compare/index.html.bak
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/compare/index.html.bak?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/compare/index.html.bak (added)
+++ velocity/anakia/trunk/test/anakia/compare/index.html.bak Sat Mar 31 13:51:49 2007
@@ -0,0 +1,55 @@
+<!-- Content Stylesheet for Site -->
+
+        
+<!-- start the processing -->
+    <!-- ====================================================================== -->
+    <!-- Main Page Section -->
+    <!-- ====================================================================== -->
+    <html>
+        <head>
+            <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
+                        <meta name="author" value="$au.getText()">
+            <meta name="email" value="$em.getValue()">
+            <title>The Jakarta Project</title>
+        </head>
+
+        <body bgcolor="#ffffff" text="#000000" link="#525D76">        
+            <table border="1">
+                <tr>
+                <td>
+            <strong>Home</strong>
+        <ul>
+                    <li>    <a href="./index.html">Front Page</a>
+</li>
+                </ul>
+            <strong>About</strong>
+        <ul>
+                    <li>    <a href="./about/index.html">About</a>
+</li>
+                </ul>
+        
+                    </td>
+                <td>
+                                                                                        <p> This is an example template that gets processed. </p>
+                                                                                                <img src="./images/velocity.gif" width="329" height="105" align="">
+                                                                                                <table border="1">
+                <tr>
+                    <td> It even has a table in it! </td>
+                </tr>
+            </table>
+                                                                                                <h3>And an h3 tag</h3>
+                                                                                                                                    <p> here is another section </p>
+                                                                                                                                    <p>
+                <a href="./about/index.html">A link to a sub page</a>
+            </p>
+                                                                            </td>
+                </tr>
+            </table>
+        </body>
+    </html>
+<!-- end the processing -->
+
+
+
+
+

Added: velocity/anakia/trunk/test/anakia/velocity.properties
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/velocity.properties?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/velocity.properties (added)
+++ velocity/anakia/trunk/test/anakia/velocity.properties Sat Mar 31 13:51:49 2007
@@ -0,0 +1,16 @@
+# 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.    

Propchange: velocity/anakia/trunk/test/anakia/velocity.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/test/anakia/velocity.properties
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/xdocs/index.xml
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/xdocs/index.xml?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/xdocs/index.xml (added)
+++ velocity/anakia/trunk/test/anakia/xdocs/index.xml Sat Mar 31 13:51:49 2007
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.    
+-->
+<document>
+    <properties>
+        <author email="jon@latchkey.com">Jon S. Stevens</author>
+        <title>The Jakarta Project</title>
+    </properties>
+    <body>
+        <section name="Section 1">
+            <p> This is an example template that gets processed. </p>
+            <img src="/images/velocity.gif" width="329" height="105"/>
+            <table border="1">
+                <tr>
+                    <td> It even has a table in it! </td>
+                </tr>
+            </table>
+            <h3>And an h3 tag</h3>
+        </section>
+        <section name="Section 2">
+            <p> here is another section </p>
+        </section>
+        <section name="section 3">
+            <p>
+                <a href="./about/index.html">A link to a sub page</a>
+            </p>
+        </section>
+    </body>
+</document>

Propchange: velocity/anakia/trunk/test/anakia/xdocs/index.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/test/anakia/xdocs/index.xml
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/customContext.xml
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/xdocs/stylesheets/customContext.xml?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/xdocs/stylesheets/customContext.xml (added)
+++ velocity/anakia/trunk/test/anakia/xdocs/stylesheets/customContext.xml Sat Mar 31 13:51:49 2007
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.    
+-->
+<project name="Jakarta Site" href="http://jakarta.apache.org/">
+    <body>
+        <menu name="Other Context">
+            <item name="Front Page OC" href="/index.html"/>
+        </menu>
+        <menu name="Other About">
+            <item name="About OC" href="/about/index.html"/>
+        </menu>
+    </body>
+</project>

Propchange: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/customContext.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/customContext.xml
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/project.xml
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/xdocs/stylesheets/project.xml?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/xdocs/stylesheets/project.xml (added)
+++ velocity/anakia/trunk/test/anakia/xdocs/stylesheets/project.xml Sat Mar 31 13:51:49 2007
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.    
+-->
+<project name="Jakarta Site"
+        href="http://jakarta.apache.org/">
+
+    <title>Jakarta Site</title>
+
+    <body>
+        <menu name="Home">
+            <item name="Front Page"            href="/index.html"/>
+        </menu>
+    
+        <menu name="About">
+            <item name="About"                 href="/about/index.html"/>
+        </menu>
+    </body>
+</project>

Propchange: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/project.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/project.xml
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/anakia/trunk/test/anakia/xdocs/stylesheets/site.vsl
URL: http://svn.apache.org/viewvc/velocity/anakia/trunk/test/anakia/xdocs/stylesheets/site.vsl?view=auto&rev=524478
==============================================================================
--- velocity/anakia/trunk/test/anakia/xdocs/stylesheets/site.vsl (added)
+++ velocity/anakia/trunk/test/anakia/xdocs/stylesheets/site.vsl Sat Mar 31 13:51:49 2007
@@ -0,0 +1,108 @@
+## 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.    
+<!-- Content Stylesheet for Site -->
+
+    ## Defined variables
+    #set ($bodybg = "#ffffff")
+    #set ($bodyfg = "#000000")
+    #set ($bodylink = "#525D76")
+    #set ($bannerbg = "#525D76")
+    #set ($bannerfg = "#ffffff")
+    #set ($tablethbg = "#039acc")
+    #set ($tabletdbg = "#a0ddf0")
+    
+<!-- start the processing -->
+#document()
+<!-- end the processing -->
+
+## This is where the macro's live
+
+#macro ( makeProject )
+    #set ($menus = $xpath.applyTo("body/menu", $project))
+
+    #foreach ( $menu in $menus )
+        <strong>$menu.getAttributeValue("name")</strong>
+        <ul>
+        #foreach ( $item in $menu.getChildren() )
+            #set ($name = $item.getAttributeValue("name"))
+            <li>#projectanchor($name $item.getAttributeValue("href"))</li>
+        #end
+        </ul>
+    #end
+#end
+
+#macro ( image $value )
+#if ($value.getAttributeValue("width"))
+#set ($width=$value.getAttributeValue("width"))
+#end
+#if ($value.getAttributeValue("height"))
+#set ($height=$value.getAttributeValue("height"))
+#end
+#if ($value.getAttributeValue("align"))
+#set ($align=$value.getAttributeValue("align"))
+#end
+<img src="$relativePath$value.getAttributeValue("src")" width="$!width" height="$!height" align="$!align">
+#end
+
+#macro ( projectanchor $name $value )
+    <a href="$relativePath$value">$name</a>
+#end
+#macro ( metaauthor $author $email )
+            <meta name="author" value="$author">
+            <meta name="email" value="$email">
+#end
+
+#macro (document)
+    <!-- ====================================================================== -->
+    <!-- Main Page Section -->
+    <!-- ====================================================================== -->
+    <html>
+        <head>
+            <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
+
+            ## set ($au = $root.getChild("properties").getChild("author").getText())
+            ## set ($em = $root.getChild("properties").getChild("author").getAttributeValue("email"))
+            #metaauthor ( $au.getText() $em.getValue() )
+
+            <title>$root.getChild("properties").getChild("title").getText()</title>
+        </head>
+
+        <body bgcolor="$bodybg" text="$bodyfg" link="$bodylink">        
+            <table border="1">
+                <tr>
+                <td>
+                #makeProject()
+                </td>
+                <td>
+                ## set ($allSections = $root.getChild("body").getChildren("section"))
+                #set ($allSections = $xpath.applyTo("body/section", $root))
+
+                #foreach ( $section in $allSections )
+                    #foreach ( $item in $section.getChildren() )                
+                        #if ($item.getName().equals("img"))
+                            #image ($item)
+                        #else
+                            $xmlout.outputString($item)
+                        #end
+                    #end
+                #end
+                </td>
+                </tr>
+            </table>
+        </body>
+    </html>
+#end