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]

Reply via email to