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();
+	}
+
+}