This example shows the usage of Xpath and Velocity. It permits the usage of XPath in foreach directives.

This sample has a dependency to Saxon. Libraries used:

  • saxon8.jar
  • saxon8-dom.jar
  • velocity-1.5.jar
  • commons-collections-3.1.jar
  • commons-lang-2.1.jar

Run the test.Sample class.

File: test.v

package ${packageName};

public class My${xpath.run("xs:string(//package/class/@name)",$mydoc)}
{
    public My${xpath.run("xs:string(//package/class/@name)",$mydoc)}()
    {
#foreach($field in $xpath.iterator("//package/class/field",$mydoc))
        this.${xpath.run("xs:string(//field/@name)",$field)} = "1";
#end
    }
}

File: test.Sample

package test;

import java.io.StringWriter;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

public class Sample
{
    public String generate(String templateFile,String rawDoc) throws Exception
    {
        VelocityEngine engine = new VelocityEngine();
        Template t = engine.getTemplate(templateFile);
        VelocityContext context = new VelocityContext();
        context.put("mydoc", rawDoc);
        context.put("xpath", new XPathHandler());
        context.put("packageName", "org.test");
        StringWriter wr = new StringWriter();
        t.merge(context, wr);
        return wr.getBuffer().toString();
    }
    
    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception
    {
        String templateFile = "test.v";
        String rawDoc = "<package><class name=\"Director\"><field name=\"test1\"/></class></package>";
        System.out.println(new Sample().generate(templateFile, rawDoc));
    }

}

File: test.XPathHandler

package test;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import net.sf.saxon.Configuration;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.query.DynamicQueryContext;
import net.sf.saxon.query.StaticQueryContext;
import net.sf.saxon.query.XQueryExpression;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class XPathHandler
{
    
    /**
     * Run a Xpath query using Saxon
     * @param xpath 
     * @param doc either an instance of {@link Element} or a raw xml {@link String}.
     * @return
     */
    public String run(String xpath, Object doc)
    {
        try
        {
            Configuration config = new Configuration();
            StaticQueryContext staticContext = new StaticQueryContext(config);
            StringReader reader = new StringReader(xpath);
            XQueryExpression exp = staticContext.compileQuery(reader);
            DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
            
            if( doc instanceof String )
            {
                DocumentInfo d = staticContext.buildDocument(new StreamSource(new StringReader((String)doc))).getDocumentRoot();
                dynamicContext.setContextNode(d);
            }
            else if( doc instanceof Element)
            {
                DOMSource domSource = new DOMSource((Element)doc);
                StringWriter writer = new StringWriter();
                StreamResult result = new StreamResult(writer);
                TransformerFactory tf = TransformerFactory.newInstance();
                Transformer transformer = tf.newTransformer();
                Properties props = new Properties();
                props.setProperty(OutputKeys.METHOD, "xml");
                props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

                transformer.setOutputProperties(props);
                transformer.transform(domSource, result);
                
                DocumentInfo d = staticContext.buildDocument(new StreamSource(new StringReader(writer.toString()))).getDocumentRoot();
                dynamicContext.setContextNode(d);
            }
            else
            {
                return null;
            }
            Properties props = new Properties();
            props.setProperty(OutputKeys.METHOD, "text");
            props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            props.setProperty(OutputKeys.STANDALONE, "yes");
            StringWriter wr = new StringWriter();
            exp.run(dynamicContext,new StreamResult(wr), props);
            return wr.getBuffer().toString();
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    /**
     * Run a xpath and return a collection of elements
     * @param xpath
     * @param rawdoc
     * @return Collection of {@link Element} elements
     */
    public Collection iterator(String xpath, String rawdoc)
    {
        try
        {
            Configuration config = new Configuration();
            StaticQueryContext staticContext = new StaticQueryContext(config);
            StringReader reader = new StringReader(xpath);
            XQueryExpression exp = staticContext.compileQuery(reader);
            DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
            
            DocumentInfo doc = staticContext.buildDocument(new StreamSource(new StringReader(rawdoc))).getDocumentRoot();
            Properties props = new Properties();
            props.setProperty(OutputKeys.METHOD, "xml");
            props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            dynamicContext.setContextNode(doc);
            StringWriter wr = new StringWriter();
            
            exp.run(dynamicContext,new StreamResult(wr), props);
            
            String xml ="<root>"+wr.getBuffer().toString()+"</root>"; //wraps in a root element, for getting a list of elements later
            DocumentBuilderFactory dom = javax.xml.parsers.DocumentBuilderFactory.newInstance();
            Document document = dom.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
            
            return new NodeListWrapper(document.getDocumentElement().getChildNodes());
            
        }
        catch (Throwable e)
        {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * Wraps a NodeList within an ArrayList. only accepts {@link Element} as elements
     */
    public static class NodeListWrapper extends ArrayList
    {
        public NodeListWrapper(NodeList si)
        {
            try
            {
                for(int i=0; i<si.getLength(); i++)
                {
                    if( si.item(i) instanceof Element)
                    {
                        add(si.item(i));
                    }
                }
            }
            catch (Exception e)
            {
                //ignore
            }
        }
    }
}
  • No labels