Author: mbenson
Date: Fri Jul 6 14:21:41 2007
New Revision: 554059
URL: http://svn.apache.org/viewvc?view=rev&rev=554059
Log:
[JXPATH-91] add full key() function support; initial submission by Sergey
Vladimirov
Added:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ExtendedKeyManager.java
(with props)
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/util/KeyManagerUtils.java
(with props)
Modified:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/JXPathContext.java
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/compiler/CoreFunction.java
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java
jakarta/commons/proper/jxpath/trunk/src/test/org/apache/commons/jxpath/ri/compiler/CoreFunctionTest.java
Added:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ExtendedKeyManager.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ExtendedKeyManager.java?view=auto&rev=554059
==============================================================================
---
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ExtendedKeyManager.java
(added)
+++
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ExtendedKeyManager.java
Fri Jul 6 14:21:41 2007
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * More complete implementation for the XPath <code>"key()"</code> function.
+ * Returns NodeSet results and allows Object values for better compatibility
+ * with non-XML graphs.
+ *
+ * @author Sergey Vladimirov
+ * @author Matt Benson
+ * @version $Revision:$ $Date:$
+ */
+public interface ExtendedKeyManager extends KeyManager {
+
+ /**
+ * Find a NodeSet by key/value.
+ * @param context
+ * @param key
+ * @param value
+ */
+ NodeSet getNodeSetByKey(JXPathContext context, String key, Object value);
+
+}
\ No newline at end of file
Propchange:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ExtendedKeyManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/JXPathContext.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/JXPathContext.java?view=diff&rev=554059&r1=554058&r2=554059
==============================================================================
---
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/JXPathContext.java
(original)
+++
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/JXPathContext.java
Fri Jul 6 14:21:41 2007
@@ -23,6 +23,8 @@
import java.util.List;
import java.util.Locale;
+import org.apache.commons.jxpath.util.KeyManagerUtils;
+
/**
* JXPathContext provides APIs for the traversal of graphs of JavaBeans using
* the XPath syntax. Using JXPathContext, you can read and write properties of
@@ -795,6 +797,21 @@
}
throw new JXPathException(
"Cannot find an element by key - "
+ + "no KeyManager has been specified");
+ }
+
+ /**
+ * Locates a NodeSet by key/value.
+ * @param key
+ * @param value
+ */
+ public NodeSet getNodeSetByKey(String key, Object value) {
+ KeyManager manager = getKeyManager();
+ if (manager != null) {
+ return KeyManagerUtils.getExtendedKeyManager(manager)
+ .getNodeSetByKey(this, key, value);
+ }
+ throw new JXPathException("Cannot find an element by key - "
+ "no KeyManager has been specified");
}
Modified:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/compiler/CoreFunction.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/compiler/CoreFunction.java?view=diff&rev=554059&r1=554058&r2=554059
==============================================================================
---
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/compiler/CoreFunction.java
(original)
+++
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/compiler/CoreFunction.java
Fri Jul 6 14:21:41 2007
@@ -22,12 +22,15 @@
import java.util.Collection;
import java.util.Locale;
+import org.apache.commons.jxpath.BasicNodeSet;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
+import org.apache.commons.jxpath.NodeSet;
import org.apache.commons.jxpath.ri.Compiler;
import org.apache.commons.jxpath.ri.EvalContext;
import org.apache.commons.jxpath.ri.InfoSetUtil;
+import org.apache.commons.jxpath.ri.axes.NodeSetContext;
import org.apache.commons.jxpath.ri.model.NodePointer;
/**
@@ -184,7 +187,7 @@
return false;
}
-
+
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(getFunctionName());
@@ -344,12 +347,40 @@
}
protected Object functionKey(EvalContext context) {
- assertArgCount(2);
+ assertArgRange(2, 3);
String key = InfoSetUtil.stringValue(getArg1().computeValue(context));
- String value =
InfoSetUtil.stringValue(getArg2().computeValue(context));
+ Object value = getArg2().compute(context);
+ EvalContext ec = null;
+ if (value instanceof EvalContext) {
+ ec = (EvalContext) value;
+ if (ec.hasNext()) {
+ value = ((NodePointer) ec.next()).getValue();
+ } else { // empty context -> empty results
+ return new BasicNodeSet();
+ }
+ }
JXPathContext jxpathContext = context.getJXPathContext();
- NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
- return pointer.getPointerByKey(jxpathContext, key, value);
+ if (getArgumentCount() == 3) {
+ Object arg3 = getArg3().computeValue(context);
+ if (arg3 instanceof EvalContext) {
+ arg3 = ((EvalContext) arg3).getCurrentNodePointer();
+ }
+ if (!(arg3 instanceof NodePointer)) {
+ throw new JXPathException("invalid third key() argument: " +
arg3);
+ }
+ jxpathContext = jxpathContext.getRelativeContext((NodePointer)
arg3);
+ }
+ NodeSet nodeSet = jxpathContext.getNodeSetByKey(key, value);
+ if (ec != null && ec.hasNext()) {
+ BasicNodeSet accum = new BasicNodeSet();
+ accum.add(nodeSet);
+ while (ec.hasNext()) {
+ value = ((NodePointer) ec.next()).getValue();
+ accum.add(jxpathContext.getNodeSetByKey(key, value));
+ }
+ nodeSet = accum;
+ }
+ return new NodeSetContext(context, nodeSet);
}
protected Object functionNamespaceURI(EvalContext context) {
@@ -684,9 +715,14 @@
}
private void assertArgCount(int count) {
- if (getArgumentCount() != count) {
+ assertArgRange(count, count);
+ }
+
+ private void assertArgRange(int min, int max) {
+ int ct = getArgumentCount();
+ if (ct < min || ct > max) {
throw new JXPathInvalidSyntaxException(
- "Incorrect number of argument: " + this);
+ "Incorrect number of arguments: " + this);
}
}
}
Modified:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java?view=diff&rev=554059&r1=554058&r2=554059
==============================================================================
---
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java
(original)
+++
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java
Fri Jul 6 14:21:41 2007
@@ -16,10 +16,12 @@
*/
package org.apache.commons.jxpath.ri.model;
+import java.util.HashSet;
import java.util.Locale;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.NodeSet;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.ri.Compiler;
import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
@@ -568,6 +570,17 @@
}
/**
+ * Find a NodeSet by key/value.
+ * @param context
+ * @param key
+ * @param value
+ * @return NodeSet
+ */
+ public NodeSet getNodeSetByKey(JXPathContext context, String key, Object
value) {
+ return context.getNodeSetByKey(key, value);
+ }
+
+ /**
* Returns an XPath that maps to this Pointer.
*/
public String asPath() {
@@ -617,6 +630,9 @@
}
public int compareTo(Object object) {
+ if (object == this) {
+ return 0;
+ }
// Let it throw a ClassCastException
NodePointer pointer = (NodePointer) object;
if (parent == pointer.parent) {
@@ -626,17 +642,26 @@
// Task 1: find the common parent
int depth1 = 0;
NodePointer p1 = this;
+ HashSet parents1 = new HashSet();
while (p1 != null) {
depth1++;
p1 = p1.parent;
+ if (p1 != null) {
+ parents1.add(p1);
+ }
}
+ boolean commonParentFound = false;
int depth2 = 0;
NodePointer p2 = pointer;
while (p2 != null) {
depth2++;
p2 = p2.parent;
+ if (parents1.contains(p2)) {
+ commonParentFound = true;
+ }
}
- return compareNodePointers(this, depth1, pointer, depth2);
+ //nodes from different graphs are equal, else continue comparison:
+ return commonParentFound ? compareNodePointers(this, depth1, pointer,
depth2) : 0;
}
private int compareNodePointers(
@@ -654,8 +679,13 @@
return r == 0 ? 1 : r;
}
//henceforth depth1 == depth2:
- if (depth1 == 1 || p1 == p2 || p1 != null && p1.equals(p2)) {
+ if (p1 == p2 || p1 != null && p1.equals(p2)) {
return 0;
+ }
+ if (depth1 == 1) {
+ throw new JXPathException(
+ "Cannot compare pointers that do not belong to the same
tree: '"
+ + p1 + "' and '" + p2 + "'");
}
int r = compareNodePointers(p1.parent, depth1 - 1, p2.parent, depth2 -
1);
return r == 0 ? p1.parent.compareChildNodePointers(p1, p2) : r;
Added:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/util/KeyManagerUtils.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/util/KeyManagerUtils.java?view=auto&rev=554059
==============================================================================
---
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/util/KeyManagerUtils.java
(added)
+++
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/util/KeyManagerUtils.java
Fri Jul 6 14:21:41 2007
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jxpath.util;
+
+import org.apache.commons.jxpath.BasicNodeSet;
+import org.apache.commons.jxpath.ExtendedKeyManager;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.KeyManager;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Utility class.
+ * @author Matt Benson
+ * @version $Revision:$ $Date:$
+ */
+public class KeyManagerUtils {
+ private static class SingleNodeExtendedKeyManager implements
+ ExtendedKeyManager {
+ private KeyManager delegate;
+
+ public SingleNodeExtendedKeyManager(KeyManager delegate) {
+ this.delegate = delegate;
+ }
+
+ public NodeSet getNodeSetByKey(JXPathContext context, String key,
+ Object value) {
+ Pointer pointer = delegate.getPointerByKey(context, key,
InfoSetUtil.stringValue(value));
+ BasicNodeSet result = new BasicNodeSet();
+ result.add(pointer);
+ return result;
+ }
+
+ public Pointer getPointerByKey(JXPathContext context, String keyName,
+ String keyValue) {
+ return delegate.getPointerByKey(context, keyName, keyValue);
+ }
+ }
+
+ /**
+ * Get an ExtendedKeyManager from the specified KeyManager.
+ * @param keyManager
+ * @return <code>keyManager</code> if it implements ExtendedKeyManager
+ * or a basic single-result ExtendedKeyManager that delegates to
+ * <code>keyManager</code>.
+ */
+ public static ExtendedKeyManager getExtendedKeyManager(KeyManager
keyManager) {
+ return keyManager instanceof ExtendedKeyManager ? (ExtendedKeyManager)
keyManager
+ : new SingleNodeExtendedKeyManager(keyManager);
+ }
+}
Propchange:
jakarta/commons/proper/jxpath/trunk/src/java/org/apache/commons/jxpath/util/KeyManagerUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jakarta/commons/proper/jxpath/trunk/src/test/org/apache/commons/jxpath/ri/compiler/CoreFunctionTest.java
URL:
http://svn.apache.org/viewvc/jakarta/commons/proper/jxpath/trunk/src/test/org/apache/commons/jxpath/ri/compiler/CoreFunctionTest.java?view=diff&rev=554059&r1=554058&r2=554059
==============================================================================
---
jakarta/commons/proper/jxpath/trunk/src/test/org/apache/commons/jxpath/ri/compiler/CoreFunctionTest.java
(original)
+++
jakarta/commons/proper/jxpath/trunk/src/test/org/apache/commons/jxpath/ri/compiler/CoreFunctionTest.java
Fri Jul 6 14:21:41 2007
@@ -17,11 +17,15 @@
package org.apache.commons.jxpath.ri.compiler;
import java.text.DecimalFormatSymbols;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.jxpath.ExtendedKeyManager;
import org.apache.commons.jxpath.IdentityManager;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathTestCase;
import org.apache.commons.jxpath.KeyManager;
+import org.apache.commons.jxpath.NodeSet;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.TestMixedModelBean;
import org.apache.commons.jxpath.Variables;
@@ -144,9 +148,50 @@
}
});
- assertEquals("Test key", "42", context.getValue("key('a', 'b')"));
+ assertXPathValue(context, "key('a', 'b')", "42");
}
-
+
+ public void testExtendedKeyFunction() {
+ context.setKeyManager(new ExtendedKeyManager() {
+ public Pointer getPointerByKey(JXPathContext context, String key,
+ String value) {
+ return NodePointer.newNodePointer(null, "incorrect", null);
+ }
+
+ public NodeSet getNodeSetByKey(JXPathContext context,
+ String keyName, Object keyValue) {
+ return new NodeSet() {
+
+ public List getNodes() {
+ return Arrays.asList(new Object[] { "53", "64" });
+ }
+
+ public List getPointers() {
+ return Arrays.asList(new NodePointer[] {
+ NodePointer.newNodePointer(null, "53", null),
+ NodePointer.newNodePointer(null, "64", null)
});
+ }
+
+ public List getValues() {
+ return Arrays.asList(new Object[] { "53", "64" });
+ }
+
+ };
+ }
+ });
+ assertXPathValue(context, "key('a', 'b')", "53");
+ assertXPathValue(context, "key('a', 'b')[1]", "53");
+ assertXPathValue(context, "key('a', 'b')[2]", "64");
+ assertXPathValueIterator(context, "key('a', 'b')", list("53", "64"));
+ assertXPathValueIterator(context, "'x' | 'y'", list("x", "y"));
+ assertXPathValueIterator(context, "key('a', 'x' | 'y')", list("53",
"64", "53", "64"));
+ assertXPathValueIterator(context, "key('a', /list[position() < 4])",
list("53", "64", "53", "64", "53", "64"));
+ context.getVariables().declareVariable("ints", new int[] { 0, 0 });
+ assertXPathValueIterator(context, "key('a', $ints)", list("53", "64",
"53", "64"));
+ assertXPathValueIterator(context, "key('a', 'b', /list)", list("53",
"64"));
+ assertXPathValueIterator(context, "key('a', $ints, /list)", list("53",
"64", "53", "64"));
+ }
+
public void testFormatNumberFunction() {
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]