You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2012/04/21 01:30:59 UTC

svn commit: r1328538 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/ oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/

Author: mduerig
Date: Fri Apr 20 23:30:59 2012
New Revision: 1328538

URL: http://svn.apache.org/viewvc?rev=1328538&view=rev
Log:
OAK-61: initial draft / prove of concept of path handling in oak-core
OAK-15: Clean up oak-jcr: use path utilities from oak-core


Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/NamespaceRegistry.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/Paths.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/NameSpaceRegistryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/PathsTest.java
Removed:
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Paths.java
Modified:
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/NamespaceRegistry.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/NamespaceRegistry.java?rev=1328538&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/NamespaceRegistry.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/NamespaceRegistry.java Fri Apr 20 23:30:59 2012
@@ -0,0 +1,143 @@
+/*
+* 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.jackrabbit.oak.namepath;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Prove of concept implementation for OAK-61.
+ *
+ * This implementation is entirely in memory. TODO: persist mappings
+ *
+ * For each registered mapping from a jcr prefix to a namespace a
+ * a mk prefix is generated. The mk prefixes are in one to one relation
+ * ship with the registered namespaces and should be used as shorthands
+ * in place of the actual namespaces in all further name and path handling.
+ *
+ * TODO: expose the relevant methods through the Oak API.
+ */
+public class NamespaceRegistry {
+    private final Map<String, String> jcr2NsMap = new HashMap<String, String>();
+    private final Map<String, String> ns2MkMap = new HashMap<String, String>();
+    private final Map<String, String> mk2JcrMap = new HashMap<String, String>();
+
+    /**
+     * Add a mapping jcr prefix to namespace mapping. If either
+     * {@code jcrPrefix} or {@code namespace} is already mapped, the
+     * existing mapping is removed first.
+     *
+     * @param jcrPrefix
+     * @param namespace
+     */
+    public void registerNamespace(String jcrPrefix, String namespace) {
+        if (jcrPrefix == null || namespace == null) {
+            throw new IllegalArgumentException();
+        }
+
+        unregisterJcrPrefix(jcrPrefix);
+        unregisterNamespace(namespace);
+
+        String mk = ns2MkMap.get(namespace);
+        if (mk == null) {
+            // Generate a mk prefix. Use jcrPrefix if possible, disambiguate otherwise
+            mk = jcrPrefix;
+            while (ns2MkMap.containsValue(mk)) {
+                mk += "+"; // todo: use more sophisticated approach for generating unique names
+            }
+            ns2MkMap.put(namespace, mk);
+        }
+
+        mk2JcrMap.put(mk, jcrPrefix);
+        jcr2NsMap.put(jcrPrefix, namespace);
+    }
+
+    /**
+     * Remove the mapping of {@code jcrPrefix} if such exists.
+     * @param jcrPrefix
+     * @return  the namespace to which {@code jcrPrefix} mapped or
+     * {@code null} if none.
+     */
+    public String unregisterJcrPrefix(String jcrPrefix) {
+        if (jcrPrefix == null) {
+            throw new IllegalArgumentException();
+        }
+
+        String ns = jcr2NsMap.remove(jcrPrefix);
+        mk2JcrMap.remove(ns2MkMap.get(ns));
+        return ns;
+    }
+
+    /**
+     * Remove the mapping for {@code namespace} if such exists.
+     * @param namespace
+     * @return  the jcr prefix which mapped to {@code namespace} or
+     * {@code null} if none.
+     */
+    public String unregisterNamespace(String namespace) {
+        if (namespace == null) {
+            throw new IllegalArgumentException();
+        }
+
+        String jcrPrefix = mk2JcrMap.remove(ns2MkMap.get(namespace));
+        jcr2NsMap.remove(jcrPrefix);
+        return jcrPrefix;
+    }
+
+    /**
+     * Retrieve the namespace which {@code jcrPrefix} maps to if any
+     * or {@code null} otherwise.
+     * @param jcrPrefix
+     * @return  namespace or {@code null}
+     */
+    public String getNamespace(String jcrPrefix) {
+        return jcr2NsMap.get(jcrPrefix);
+    }
+
+    /**
+     * Retrieve the jcr prefix which maps to {@code namespace} if any
+     * or {@code null} otherwise.
+     * @param namespace
+     * @return  jcr prefix or {@code null}
+     */
+    public String getJcrPrefix(String namespace) {
+        return mk2JcrMap.get(ns2MkMap.get(namespace));
+    }
+    
+    //------------------------------------------------------------< internal >--- 
+
+    /**
+     * Retrieve the jcr prefix which maps to {@code mkPrefix} if any
+     * or {@code null} otherwise.
+     * @param mkPrefix
+     * @return  jcr prefix or {@code null}
+     */
+    String getJcrPrefixFromMk(String mkPrefix) {
+        return mk2JcrMap.get(mkPrefix);
+    }
+
+    /**
+     * Retrieve the mk prefix which maps to {@code jcrPrefix} if any
+     * or {@code null} otherwise.
+     * @param jcrPrefix
+     * @return  mk prefix or {@code null}
+     */
+    String getMkPrefixFromJcr(String jcrPrefix) {
+        return ns2MkMap.get(jcr2NsMap.get(jcrPrefix));
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/Paths.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/Paths.java?rev=1328538&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/Paths.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/Paths.java Fri Apr 20 23:30:59 2012
@@ -0,0 +1,288 @@
+/*
+* 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.jackrabbit.oak.namepath;
+
+import org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.util.Function1;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * All path in the Oak API have the following form
+ * <p>
+ * <pre>
+ * PATH    := ("/" ELEMENT)* | ELEMENT ("/" ELEMENT)*
+ * ELEMENT := [PREFIX ":"] NAME
+ * PREFIX  := non empty string not containing ":" and "/"
+ * NAME    := non empty string not containing ":" and "/" TODO: check whether this is correct
+ * </pre>
+ */
+public final class Paths {
+
+    private Paths() {}
+
+    /**
+     * Get the parent of a path. The parent of the root path ("/") is the root
+     * path.
+     *
+     * @param path the path
+     * @return the parent path
+     */
+    public static String getParentPath(String path) {
+        return PathUtils.getParentPath(path);
+    }
+
+    /**
+     * Get the last element of the (absolute or relative) path. The name of the
+     * root node ("/") and the name of the empty path ("") is the empty path.
+     *
+     * @param path the complete path
+     * @return the last element
+     */
+    public static String getName(String path) {
+        return PathUtils.getName(path);
+    }
+
+    /**
+     * Get the nth ancestor of a path. The 1st ancestor is the parent path,
+     * 2nd ancestor the grandparent path, and so on...
+     * <p/>
+     * If nth <= 0, the path argument is returned as is.
+     *
+     * @param path the path
+     * @return the ancestor path
+     */
+    public static String getAncestorPath(String path, int nth) {
+        return PathUtils.getAncestorPath(path, nth);
+    }
+
+    /**
+     * Concatenate path elements.
+     *
+     * @param parentPath the parent path
+     * @param subPath the subPath path to add
+     * @return the concatenated path
+     */
+    public static String concat(String parentPath, String subPath) {
+        return PathUtils.concat(parentPath, subPath);
+    }
+
+    /**
+     * Relativize a path wrt. a parent path such that
+     * {@code relativize(parentPath, concat(parentPath, path)) == paths}
+     * holds.
+     *
+     * @param parentPath parent pth
+     * @param path path to relativize
+     * @return relativized path
+     */
+    public static String relativize(String parentPath, String path) {
+        return PathUtils.relativize(parentPath, path);
+    }
+
+    /**
+     * Calculate the number of elements in the path. The root path has zero
+     * elements.
+     *
+     * @param path the path
+     * @return the number of elements
+     */
+    public static int getDepth(String path) {
+        return PathUtils.getDepth(path);
+    }
+
+    /**
+     * Get the prefix of an element. Undefined if {@code element} is
+     * not an {@code ELEMENT}.
+     * @param element
+     * @return  the {@code PREFIX} of {@code element} or {@code null} if none
+     */
+    public static String getPrefixFromElement(String element) {
+        int pos = element.indexOf(':');
+        if (pos == -1) {
+            return null;
+        }
+        else {
+            return element.substring(0, pos);
+        }
+    }
+
+    /**
+     * Get the name of an element. Undefined if {@code element} is
+     * not an {@code ELEMENT}.
+     * @param element
+     * @return  the {@code NAME} of {@code element}
+     */
+    public static String getNameFromElement(String element) {
+        int pos = element.indexOf(':');
+        if (pos == -1) {
+            return element;
+        }
+        else {
+            return element.substring(pos + 1);
+        }
+    }
+
+    /**
+     * Determine whether {@code string} is a valid {@code ELEMENT}.
+     * @param string
+     * @return  {@code true} iff {@code string} is a valid {@code ELEMENT}.
+     */
+    public static boolean isValidElement(String string) {
+        if (string.isEmpty()) {
+            return false;
+        }
+
+        int colons = 0;
+        int pos = -1;
+        for (int k = 0; k < string.length(); k++) {
+            if (string.charAt(k) == ':') {
+                colons += 1;
+                pos = k;
+            }
+            else if (string.charAt(k) == '/') {
+                return false;
+            }
+        }
+
+        return colons <= 1 && (pos != 0 && pos != string.length() - 1);
+    }
+
+    /**
+     * Determine whether {@code string} is a valid {@code PATH}.
+     * @param string
+     * @return  {@code true} iff {@code string} is a valid {@code PATH}.
+     */
+    public static boolean isValidPath(String string) {
+        if (string.isEmpty()) {
+            return false;
+        }
+        if (string.length() > 1 && string.endsWith("/")) {
+            return false;
+        }
+
+        for (String part : split(string, '/')) {
+            if (!isValidElement(part)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Resolve {@code element} against {@code prefixResolver}: replace the
+     * prefix of this element with the prefix returned by the prefix resolver.
+     * Undefined if {@code element} is not a valid {@code ELEMENT}.
+     * @param element  {@code ELEMENT}
+     * @param prefixResolver  prefix resolver
+     * @return  resolved element
+     * @throws IllegalArgumentException  if {@code prefixResolver} returns {@code null}
+     * for the prefix of {@code element}.
+     */
+    public static String resolveElement(String element, Function1<String, String> prefixResolver) {
+        String prefix = getPrefixFromElement(element);
+        if (prefix == null) {
+            return element;
+        }
+
+        String newPrefix = prefixResolver.apply(prefix);
+        if (newPrefix == null) {
+            throw new IllegalArgumentException("Can't resolve prefix " + prefix);
+        }
+        return newPrefix + ':' + getNameFromElement(element);
+    }
+
+    /**
+     * Resolve {@code path} against {@code prefixResolver} by resolving each
+     * element of the path against that prefix resolver.
+     * @param path  {@code PATH}
+     * @param prefixResolver  prefix resolver
+     * @return  resolved path
+     * @throws IllegalArgumentException if {@code prefixResolver} returns {@code null}
+     * for a prefix in any of the elements of {@code path}.
+     */
+    public static String resolvePath(String path, Function1<String, String> prefixResolver) {
+        StringBuilder resolved = new StringBuilder();
+
+        String sep = path.charAt(0) == '/' ? "/" : "";
+
+        for (String element : split(path, '/')) {
+            resolved.append(sep).append(resolveElement(element, prefixResolver));
+            sep = "/";
+        }
+        
+        return resolved.toString();
+    }
+    
+    //------------------------------------------------------------< private >--- 
+
+    private static Iterable<String> split(final String string, final char separator) {
+        return new Iterable<String>() {
+            @Override
+            public Iterator<String> iterator() {
+                return new Iterator<String>() {
+                    int pos = !string.isEmpty() && string.charAt(0) == separator ? 1 : 0;
+                    String next;
+
+                    @Override
+                    public boolean hasNext() {
+                        if (next == null) {
+                            if (pos >= string.length()) {
+                                return false;
+                            }
+                            else {
+                                int i = string.indexOf(separator, pos);
+                                if (i < 0) {
+                                    next = string.substring(pos);
+                                    pos = string.length();
+                                }
+                                else {
+                                    next = string.substring(pos, i);
+                                    pos = i + 1;
+                                }
+                                return true;
+                            }
+                        }
+                        else {
+                            return true;
+                        }
+                    }
+
+                    @Override
+                    public String next() {
+                        if (hasNext()) {
+                            String next = this.next;
+                            this.next = null;
+                            return next;
+                        }
+                        else {
+                            throw new NoSuchElementException();
+                        }
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException("remove");
+                    }
+                };
+            }
+        };
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/NameSpaceRegistryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/NameSpaceRegistryTest.java?rev=1328538&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/NameSpaceRegistryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/NameSpaceRegistryTest.java Fri Apr 20 23:30:59 2012
@@ -0,0 +1,81 @@
+/*
+* 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.jackrabbit.oak.namepath;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class NameSpaceRegistryTest {
+
+    @Test
+    public void nameSpaceRegistry() {
+        NamespaceRegistry r = new NamespaceRegistry();
+
+        r.registerNamespace("p", "n");
+        assertEquals(r.getNamespace("p"), "n");
+        assertEquals(r.getJcrPrefix("n"), "p");
+        String mk = r.getMkPrefixFromJcr("p");
+        assertNotNull(mk);
+        assertEquals("p", r.getJcrPrefixFromMk(mk));
+
+        r.registerNamespace("p", "n2");
+        assertEquals(r.getNamespace("p"), "n2");
+        assertEquals(r.getJcrPrefix("n2"), "p");
+        assertNull(r.getJcrPrefix("n"));
+        mk = r.getMkPrefixFromJcr("p");
+        assertNotNull(mk);
+        assertEquals("p", r.getJcrPrefixFromMk(mk));
+
+        r.registerNamespace("p2", "n");
+        assertEquals(r.getNamespace("p2"), "n");
+        assertEquals(r.getJcrPrefix("n"), "p2");
+        assertEquals(r.getNamespace("p"), "n2");
+        assertEquals(r.getJcrPrefix("n2"), "p");
+        mk = r.getMkPrefixFromJcr("p");
+        assertNotNull(mk);
+        assertEquals("p", r.getJcrPrefixFromMk(mk));
+        mk = r.getMkPrefixFromJcr("p2");
+        assertNotNull(mk);
+        assertEquals("p2", r.getJcrPrefixFromMk(mk));
+
+        r.registerNamespace("p2", "n2");
+        assertEquals(r.getNamespace("p2"), "n2");
+        assertEquals(r.getJcrPrefix("n2"), "p2");
+        assertNull(r.getJcrPrefix("n"));
+        assertNull(r.getNamespace("p"));
+        mk = r.getMkPrefixFromJcr("p");
+        assertNull(mk);
+        mk = r.getMkPrefixFromJcr("p2");
+        assertNotNull(mk);
+        assertEquals("p2", r.getJcrPrefixFromMk(mk));
+
+        r.registerNamespace("p", "n");
+        assertEquals(r.getNamespace("p2"), "n2");
+        assertEquals(r.getJcrPrefix("n2"), "p2");
+        assertEquals(r.getNamespace("p"), "n");
+        assertEquals(r.getJcrPrefix("n"), "p");
+        mk = r.getMkPrefixFromJcr("p");
+        assertNotNull(mk);
+        assertEquals("p", r.getJcrPrefixFromMk(mk));
+        mk = r.getMkPrefixFromJcr("p2");
+        assertNotNull(mk);
+        assertEquals("p2", r.getJcrPrefixFromMk(mk));
+    }
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/PathsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/PathsTest.java?rev=1328538&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/PathsTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/PathsTest.java Fri Apr 20 23:30:59 2012
@@ -0,0 +1,141 @@
+/*
+* 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.jackrabbit.oak.namepath;
+
+import org.apache.jackrabbit.oak.util.Function1;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class PathsTest {
+    private static final Function1<String, String> RESOLVER = new Function1<String, String>() {
+        private final Map<String, String> map = new HashMap<String, String>();
+        {
+            map.put("a", "x");
+            map.put("b", "y");
+            map.put("c", "z");
+        }
+
+        @Override
+        public String apply(String argument) {
+            return map.get(argument);
+        }
+    };
+
+    @Test
+    public void getPrefix() {
+        assertEquals(null, Paths.getPrefixFromElement("foo"));
+        assertEquals("foo", Paths.getPrefixFromElement("foo:bar"));
+    }
+    
+    @Test
+    public void getName() {
+        assertEquals("foo", Paths.getNameFromElement("foo"));
+        assertEquals("bar", Paths.getNameFromElement("foo:bar"));
+    }
+
+    @Test
+    public void isValidElement() {
+        assertTrue(Paths.isValidElement("foo"));
+        assertTrue(Paths.isValidElement("foo:bar"));
+
+        assertFalse(Paths.isValidElement(""));
+        assertFalse(Paths.isValidElement(":"));
+        assertFalse(Paths.isValidElement("foo:"));
+        assertFalse(Paths.isValidElement("fo/o:"));
+        assertFalse(Paths.isValidElement(":bar"));
+    }
+
+    @Test
+    public void isValidAbsolutePath() {
+        assertTrue(Paths.isValidPath("/"));
+        assertTrue(Paths.isValidPath("/a/b/c"));
+        assertTrue(Paths.isValidPath("/p:a/q:b/r:c"));
+
+        assertFalse(Paths.isValidPath(""));
+        assertFalse(Paths.isValidPath("/a/b/c/"));
+        assertFalse(Paths.isValidPath("/p:a/:b/r:c"));
+        assertFalse(Paths.isValidPath("/p:a/q:/r:c"));
+        assertFalse(Paths.isValidPath("/p:a/:/r:c"));
+        assertFalse(Paths.isValidPath("/p:a//r:c"));
+        assertFalse(Paths.isValidPath("//"));
+    }
+    
+    @Test
+    public void isValidRelativePath() {
+        assertTrue(Paths.isValidPath("a/b/c"));
+        assertTrue(Paths.isValidPath("p:a/q:b/r:c"));
+
+        assertFalse(Paths.isValidPath("a/b/c/"));
+        assertFalse(Paths.isValidPath("p:a/:b/r:c"));
+        assertFalse(Paths.isValidPath("p:a/q:/r:c"));
+        assertFalse(Paths.isValidPath("p:a/:/r:c"));
+        assertFalse(Paths.isValidPath("p:a//r:c"));
+    }
+
+    @Test
+    public void resolveElement() {
+        assertEquals("foo", Paths.resolveElement("foo", RESOLVER));
+        assertEquals("x:foo", Paths.resolveElement("a:foo", RESOLVER));
+
+        try {
+            Paths.resolveElement("q:foo", RESOLVER);
+            fail();
+        }
+        catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void resolveAbsolutePath() {
+        assertEquals("/foo", Paths.resolvePath("/foo", RESOLVER));
+        assertEquals("/foo/bar", Paths.resolvePath("/foo/bar", RESOLVER));
+        assertEquals("/x:foo", Paths.resolvePath("/a:foo", RESOLVER));
+        assertEquals("/x:foo/y:bar", Paths.resolvePath("/a:foo/b:bar", RESOLVER));
+
+        try {
+            Paths.resolvePath("/a:foo/q:bar", RESOLVER);
+            fail();
+        }
+        catch (IllegalArgumentException expected) {
+        }
+
+    }
+
+    @Test
+    public void resolveRelativePath() {
+        assertEquals("foo", Paths.resolvePath("foo", RESOLVER));
+        assertEquals("foo/bar", Paths.resolvePath("foo/bar", RESOLVER));
+        assertEquals("x:foo", Paths.resolvePath("a:foo", RESOLVER));
+        assertEquals("x:foo/y:bar", Paths.resolvePath("a:foo/b:bar", RESOLVER));
+
+        try {
+            Paths.resolvePath("a:foo/q:bar", RESOLVER);
+            fail();
+        }
+        catch (IllegalArgumentException expected) {
+        }
+
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java?rev=1328538&r1=1328537&r2=1328538&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java Fri Apr 20 23:30:59 2012
@@ -26,6 +26,7 @@ import org.apache.jackrabbit.oak.jcr.uti
 import org.apache.jackrabbit.oak.jcr.util.LogUtil;
 import org.apache.jackrabbit.oak.jcr.util.ValueConverter;
 import org.apache.jackrabbit.oak.kernel.ScalarImpl;
+import org.apache.jackrabbit.oak.namepath.Paths;
 import org.apache.jackrabbit.oak.util.Function1;
 import org.apache.jackrabbit.oak.util.Iterators;
 import org.apache.jackrabbit.oak.util.Predicate;

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java?rev=1328538&r1=1328537&r2=1328538&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java Fri Apr 20 23:30:59 2012
@@ -21,6 +21,7 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.api.TransientNodeState;
 import org.apache.jackrabbit.oak.jcr.util.LogUtil;
 import org.apache.jackrabbit.oak.jcr.util.ValueConverter;
+import org.apache.jackrabbit.oak.namepath.Paths;
 import org.apache.jackrabbit.value.ValueHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java?rev=1328538&r1=1328537&r2=1328538&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java Fri Apr 20 23:30:59 2012
@@ -17,10 +17,11 @@
 package org.apache.jackrabbit.oak.jcr;
 
 import org.apache.jackrabbit.commons.AbstractSession;
+import org.apache.jackrabbit.oak.api.Branch;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Connection;
-import org.apache.jackrabbit.oak.api.Branch;
 import org.apache.jackrabbit.oak.api.TransientNodeState;
+import org.apache.jackrabbit.oak.namepath.Paths;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java?rev=1328538&r1=1328537&r2=1328538&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java Fri Apr 20 23:30:59 2012
@@ -16,10 +16,11 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import org.apache.jackrabbit.oak.api.Branch;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Connection;
-import org.apache.jackrabbit.oak.api.Branch;
 import org.apache.jackrabbit.oak.jcr.query.QueryManagerImpl;
+import org.apache.jackrabbit.oak.namepath.Paths;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;