Bug 364885: Reduce XPath2 performance and memory footprint https://bugs.eclipse.org/bugs/show_bug.cgi?id=364885
diff --git a/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/CompleteNewApiTest.java b/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/CompleteNewApiTest.java index 3bd7f3c..2592f80 100644 --- a/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/CompleteNewApiTest.java +++ b/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/CompleteNewApiTest.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011 Jesper Steen Moller + * Copyright (c) 2011 Jesper Steen Moller and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at
diff --git a/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/ContextBuilderTest.java b/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/ContextBuilderTest.java index f989bc6..925dc96 100644 --- a/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/ContextBuilderTest.java +++ b/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/ContextBuilderTest.java
@@ -1,3 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2011 Jesper Steen Moller and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Jesper Steen Moller - initial API and implementation + *******************************************************************************/ + package org.eclipse.wst.xml.xpath2.processor.test.newapi; import junit.framework.TestCase;
diff --git a/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/FilteringPerformanceTest.java b/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/FilteringPerformanceTest.java new file mode 100644 index 0000000..43d7357 --- /dev/null +++ b/tests/org.eclipse.wst.xml.xpath2.processor.tests/src/org/eclipse/wst/xml/xpath2/processor/test/newapi/FilteringPerformanceTest.java
@@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2011 Jesper Steen Moller and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Jesper Steen Moller - initial API and implementation + *******************************************************************************/ + +package org.eclipse.wst.xml.xpath2.processor.test.newapi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.StringWriter; +import java.io.Writer; +import java.math.BigDecimal; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +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.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.eclipse.wst.xml.xpath2.api.DynamicContext; +import org.eclipse.wst.xml.xpath2.api.StaticContext; +import org.eclipse.wst.xml.xpath2.api.XPath2Expression; +import org.eclipse.wst.xml.xpath2.processor.Engine; +import org.eclipse.wst.xml.xpath2.processor.util.DynamicContextBuilder; +import org.eclipse.wst.xml.xpath2.processor.util.StaticContextBuilder; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +public class FilteringPerformanceTest { + + private Document document; + + @Before + public void setUp() throws Exception { + document = buildBigDocument(6, -1, 5); + } + + private Document buildBigDocument(int width, int deltaWidth, int depth) + throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + Document newDoc = dbf.newDocumentBuilder().newDocument(); + Element root = newDoc.createElementNS("urn:x-my-ns", "root"); + newDoc.appendChild(root); + int created = fillElement(root, width, deltaWidth, 0, depth, newDoc); + root.setAttribute("nodeCount", "" + created); + return newDoc; + } + + private int fillElement(Element parent, int width, int deltaWidth, + int currentDepth, int depthMax, Document owner) { + int nodesCreated = 3; + + parent.appendChild(owner.createComment("Width : " + width + + ", deltaWidth: " + deltaWidth + " currentDepth: " + + currentDepth)); + parent.appendChild(owner.createTextNode("\r\n")); + Element child = owner.createElementNS("urn:x-my-ns", "element" + + currentDepth); + parent.appendChild(child); + if (currentDepth < depthMax) { + for (int i = 0; i < width; ++i) { + child.setAttribute("childNumber", "" + (i + 1)); + nodesCreated += 1 + fillElement(child, width + deltaWidth, + deltaWidth, currentDepth + 1, depthMax, owner); + } + } else { + child.appendChild(owner.createTextNode("leaf")); + nodesCreated++; + } + + return nodesCreated; + } + + @After + public void tearDown() throws Exception { + document = null; + } + + @Test + public void countAllOperation1() throws ParserConfigurationException, XPathExpressionException { + Document bigDoc = buildBigDocument(2, 1, 6); + System.out.println(bigDoc.getDocumentElement().getAttribute("nodeCount")); + String control = evalXPath1("count(//node())", bigDoc); + String evaluated = evalXPath2("count(//node())", bigDoc, BigDecimal.class).toString(); + assertEquals(control, evaluated); + } + + @Test + public void countAllOperationWithFilter() throws ParserConfigurationException, XPathExpressionException { + Document bigDoc = buildBigDocument(2, 1, 5); + System.out.println(bigDoc.getDocumentElement().getAttribute("nodeCount")); + String control = evalXPath1("count(//node()[count(ancestor-or-self::*)>4])", bigDoc); + String evaluated = evalXPath2("count(//node()[count(ancestor-or-self::*)>4])", bigDoc, BigDecimal.class).toString(); + assertEquals(control, evaluated); + } + + @Test + public void countAllOperationBig() throws ParserConfigurationException, XPathExpressionException { + Document bigDoc = buildBigDocument(2, 1, 7); + System.out.println(bigDoc.getDocumentElement().getAttribute("nodeCount")); + String control = evalXPath1("count(//node())", bigDoc); + String evaluated = evalXPath2("count(//node())", bigDoc, BigDecimal.class).toString(); + assertEquals(control, evaluated); + } + + protected <R> R evalXPath2(String xpath, Node doc, Class<R> resultClass) { + StaticContext sc = new StaticContextBuilder(); + XPath2Expression path = new Engine().parseExpression(xpath, sc); + DynamicContext dynamicContext = new DynamicContextBuilder(sc); + long before = System.nanoTime(); +// path.evaluate(dynamicContext, doc != null ? new Object[] { doc } : new Object[0]); +// path.evaluate(dynamicContext, doc != null ? new Object[] { doc } : new Object[0]); + org.eclipse.wst.xml.xpath2.api.ResultSequence rs = path.evaluate(dynamicContext, doc != null ? new Object[] { doc } : new Object[0]); + assertEquals("Expected single result from \'" + xpath + "\'", 1, rs.size()); + Object result = rs.value(0); + long after = System.nanoTime(); + System.out.println("XPath2 " + xpath + " evaluated to " + result + " in " + (after-before)/1000 + " μs"); + assertTrue("Exected XPath result instanceof class " + resultClass.getSimpleName() + " from \'" + xpath + "\', got " + result.getClass(), resultClass.isInstance(result)); + return resultClass.cast(result); + } + + protected String evalXPath1(String xpath, Node doc) throws XPathExpressionException { + XPathExpression expression = XPathFactory.newInstance().newXPath().compile(xpath); + long before = System.nanoTime(); +// expression.evaluate(doc); +// expression.evaluate(doc); + String result = expression.evaluate(doc); + long after = System.nanoTime(); + System.out.println("XPath1 " + xpath + " evaluated to " + result + " in " + (after-before)/1000 + " μs"); + return result; + } + + public static void elementToStream(Element element, Writer writer) { + try { + DOMSource source = new DOMSource(element); + StreamResult result = new StreamResult(writer); + TransformerFactory transFactory = TransformerFactory.newInstance(); + Transformer transformer = transFactory.newTransformer(); + transformer.transform(source, result); + } catch (Exception ex) { + } + } + + public static String documentToString(Document doc) { + StringWriter sw = new StringWriter(); + elementToStream(doc.getDocumentElement(), sw); + return sw.toString(); + } + +}