Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@www.apache.org Received: (qmail 62229 invoked from network); 6 Jul 2007 21:22:07 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 6 Jul 2007 21:22:07 -0000 Received: (qmail 2753 invoked by uid 500); 6 Jul 2007 21:22:07 -0000 Delivered-To: apmail-jakarta-commons-dev-archive@jakarta.apache.org Received: (qmail 2682 invoked by uid 500); 6 Jul 2007 21:22:06 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 2671 invoked by uid 500); 6 Jul 2007 21:22:06 -0000 Received: (qmail 2668 invoked by uid 99); 6 Jul 2007 21:22:06 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 06 Jul 2007 14:22:06 -0700 X-ASF-Spam-Status: No, hits=-99.5 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 06 Jul 2007 14:22:03 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id E16641A981A; Fri, 6 Jul 2007 14:21:42 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r554059 - in /jakarta/commons/proper/jxpath/trunk/src: java/org/apache/commons/jxpath/ java/org/apache/commons/jxpath/ri/compiler/ java/org/apache/commons/jxpath/ri/model/ java/org/apache/commons/jxpath/util/ test/org/apache/commons/jxpath/... Date: Fri, 06 Jul 2007 21:21:42 -0000 To: commons-cvs@jakarta.apache.org From: mbenson@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070706212142.E16641A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 "key()" 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 keyManager if it implements ExtendedKeyManager + * or a basic single-result ExtendedKeyManager that delegates to + * keyManager. + */ + 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: commons-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: commons-dev-help@jakarta.apache.org