You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2008/04/10 15:41:15 UTC

svn commit: r646802 - in /jackrabbit/trunk: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/co...

Author: angela
Date: Thu Apr 10 06:41:08 2008
New Revision: 646802

URL: http://svn.apache.org/viewvc?rev=646802&view=rev
Log:
JCR-1526: Various improvements to Path and PathImpl

Added:
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/CachingHierarchyManagerTest.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/JcrPath.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathTest.java
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java
    jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authorization/acl/ACLProvider.java Thu Apr 10 06:41:08 2008
@@ -485,7 +485,7 @@
                 Path parentPath = absPath.getAncestor(1);
                 while (nid == null) {
                     nid = session.getHierarchyManager().resolveNodePath(parentPath);
-                    if (parentPath.getDepth() == 1) {
+                    if (parentPath.getDepth() == Path.ROOT_DEPTH) {
                         // root-node reached
                         break;
                     } else {

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/CachingHierarchyManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/CachingHierarchyManagerTest.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/CachingHierarchyManagerTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/CachingHierarchyManagerTest.java Thu Apr 10 06:41:08 2008
@@ -44,7 +44,7 @@
         ItemStateManager provider = new MyItemStateManager();
         cache = new CachingHierarchyManager(rootNodeId, provider, null);
         PathFactory factory = PathFactoryImpl.getInstance();
-        final Path path = factory.create("{}\t{}");
+        final Path path = factory.getRootPath();
         for(int i=0; i<3; i++) {
             new Thread(new Runnable() {
                 public void run() {

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java Thu Apr 10 06:41:08 2008
@@ -51,6 +51,8 @@
      * the root path
      */
     private static final Path ROOT = new PathImpl(new Path.Element[]{ROOT_ELEMENT}, true);
+    private static final Path CURRENT_PATH = new PathImpl(new Path.Element[]{CURRENT_ELEMENT}, true);
+    private static final Path PARENT_PATH = new PathImpl(new Path.Element[]{PARENT_ELEMENT}, true);
 
     private PathFactoryImpl() {}
 
@@ -332,7 +334,7 @@
         /**
          * @see Path#getNormalizedPath()
          */
-        public Path getNormalizedPath() throws RepositoryException {
+        public Path getNormalizedPath() {
             if (isNormalized()) {
                 return this;
             }
@@ -341,11 +343,6 @@
             for (int i = 0; i < elements.length; i++) {
                 Path.Element elem = elements[i];
                 if (elem.denotesParent() && !last.denotesParent()) {
-                    if (last.denotesRoot()) {
-                        // the first element is the root element;
-                        // ".." would refer to the parent of root
-                        throw new RepositoryException("Path can not be canonicalized: unresolvable '..' element");
-                    }
                     queue.removeLast();
                     if (queue.isEmpty()) {
                         last = PARENT_ELEMENT;
@@ -358,7 +355,7 @@
                 }
             }
             if (queue.isEmpty()) {
-                throw new RepositoryException("Path can not be normalized: would result in an empty path.");
+                return CURRENT_PATH;
             }
             boolean isNormalized = true;
             return new PathImpl((Path.Element[]) queue.toArray(new Element[queue.size()]), isNormalized);
@@ -396,8 +393,7 @@
 
             if (p0.equals(p1)) {
                 // both paths are equal, the relative path is therefore '.'
-                Builder pb = new Builder(new Path.Element[] {CURRENT_ELEMENT});
-                return pb.getPath();
+                return CURRENT_PATH;
             }
 
             // determine length of common path fragment
@@ -436,22 +432,34 @@
             if (degree < 0) {
                 throw new IllegalArgumentException("degree must be >= 0");
             } else if (degree == 0) {
-                return this;
+                return this.getNormalizedPath();
+            }
+
+            if (isAbsolute()) {
+                Path.Element[] normElems = getNormalizedPath().getElements();
+                int length = normElems.length - degree;
+                if (length < 1) {
+                    throw new PathNotFoundException("no such ancestor path of degree " + degree);
+                }
+                Path.Element[] ancestorElements = new Element[length];
+                System.arraycopy(normElems, 0, ancestorElements, 0, length);
+                return new PathImpl(ancestorElements, true);
+            } else {
+                Path.Element[] ancestorElements = new Element[elements.length + degree];
+                System.arraycopy(elements, 0, ancestorElements, 0, elements.length);
+
+                for (int i = elements.length; i < ancestorElements.length; i++) {
+                    ancestorElements[i] = PARENT_ELEMENT;
+                }
+                return new PathImpl(ancestorElements, false).getNormalizedPath();
             }
-            int length = elements.length - degree;
-            if (length < 1) {
-                throw new PathNotFoundException("no such ancestor path of degree " + degree);
-            }
-            Path.Element[] elements = new Element[length];
-            System.arraycopy(this.elements, 0, elements, 0, length);
-            return new PathImpl(elements, normalized);
         }
 
         /**
          * @see Path#getAncestorCount()
          */
         public int getAncestorCount() {
-            return getDepth() - 1;
+            return (isAbsolute()) ? getDepth() : -1;
         }
 
         /**
@@ -469,7 +477,8 @@
             for (int i = 0; i < elements.length; i++) {
                 if (elements[i].denotesParent()) {
                     depth--;
-                } else if (!elements[i].denotesCurrent()) {
+                } else if (elements[i].denotesName()) {
+                    // don't count root/current element.
                     depth++;
                 }
             }
@@ -477,38 +486,53 @@
         }
 
         /**
-         * @see Path#isAncestorOf(Path)
+         * @see Path#isEquivalentTo(Path)
          */
-        public boolean isAncestorOf(Path other) throws IllegalArgumentException, RepositoryException {
+        public boolean isEquivalentTo(Path other) throws RepositoryException {
             if (other == null) {
                 throw new IllegalArgumentException("null argument");
             }
-            // make sure both paths are either absolute or relative
             if (isAbsolute() != other.isAbsolute()) {
                 throw new IllegalArgumentException("Cannot compare a relative path with an absolute path");
             }
-            // make sure we're comparing normalized paths
-            Path p0 = getNormalizedPath();
-            Path p1 = other.getNormalizedPath();
 
-            if (p0.equals(p1)) {
+            if (getDepth() != other.getDepth()) {
                 return false;
             }
-            // calculate depth of paths (might be negative)
-            if (p0.getDepth() >= p1.getDepth()) {
+
+            Element[] elems0 = getNormalizedPath().getElements();
+            Element[] elems1 = other.getNormalizedPath().getElements();
+
+            if (elems0.length != elems1.length)
                 return false;
-            }
-            Path.Element[] elems0 = p0.getElements();
-            Path.Element[] elems1 = p1.getElements();
-            for (int i = 0; i < elems0.length; i++) {
-                if (!elems0[i].equals(elems1[i])) {
+
+            for (int k = 0; k < elems0.length; k++) {
+                if (!elems0[k].equals(elems1[k]))
                     return false;
-                }
             }
             return true;
         }
 
         /**
+         * @see Path#isAncestorOf(Path)
+         */
+        public boolean isAncestorOf(Path other) throws IllegalArgumentException, RepositoryException {
+            if (other == null) {
+                throw new IllegalArgumentException("null argument");
+            }
+            // make sure both paths are either absolute or relative
+            if (isAbsolute() != other.isAbsolute()) {
+                throw new IllegalArgumentException("Cannot compare a relative path with an absolute path");
+            }
+
+            int delta = other.getDepth() - getDepth();
+            if (delta <= 0)
+                return false;
+
+            return isEquivalentTo(other.getAncestor(delta));
+        }
+
+        /**
          * @see Path#isDescendantOf(Path)
          */
         public boolean isDescendantOf(Path other) throws IllegalArgumentException, RepositoryException {
@@ -522,7 +546,7 @@
          * @see Path#subPath(int, int)
          */
         public Path subPath(int from, int to) throws IllegalArgumentException, RepositoryException {
-            if (from < 0 || to >= elements.length || from >= to) {
+            if (from < 0 || to > elements.length || from >= to) {
                 throw new IllegalArgumentException();
             }
             if (!isNormalized()) {
@@ -843,12 +867,7 @@
         /**
          * flag indicating if the current path is normalized
          */
-        private boolean isNormalized = true;
-
-        /**
-         * flag indicating if the current path has leading parent '..' elements
-         */
-        private boolean leadingParent = true;
+        private boolean isNormalized;
 
         /**
          * Creates a new Builder and initialized it with the given path
@@ -874,11 +893,35 @@
             if (elements == null || elements.length == 0) {
                 throw new IllegalArgumentException("Cannot build path from null or 0 elements.");
             }
+
             this.elements = elements;
-            for (int i = 0; i < elements.length; i++) {
-                Path.Element elem = elements[i];
-                leadingParent &= elem.denotesParent();
-                isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
+            if (elements.length == 1) {
+                isNormalized = true;
+            } else {
+                boolean absolute = elements[0].denotesRoot();
+                isNormalized = true;
+                int parents = 0;
+                int named = 0;
+                for (int i = 0; i < elements.length; i++) {
+                    Path.Element elem = elements[i];
+                    if (elem.denotesName()) {
+                        named++;
+                    } else if (elem.denotesRoot()) {
+                        if (i > 0) {
+                            throw new IllegalArgumentException("Invalid path: The root element may only occur at the beginning.");
+                        }
+                    } else  if (elem.denotesParent()) {
+                        parents++;
+                        if (absolute || named > 0) {
+                            isNormalized = false;
+                        }
+                    } else /* current element */ {
+                        isNormalized = false;
+                    }
+                }
+                if (absolute && parents > named) {
+                    throw new IllegalArgumentException("Invalid path: Too many parent elements.");
+                }
             }
         }
 
@@ -888,8 +931,22 @@
          * @return a new {@link Path}
          */
         private Path getPath() {
+            // special path with a single element
+            if (elements.length == 1) {
+                if (elements[0].denotesRoot()) {
+                    return PathFactoryImpl.ROOT;
+                }
+                if (elements[0].denotesParent()) {
+                    return PathFactoryImpl.PARENT_PATH;
+                }
+                if (elements[0].denotesCurrent()) {
+                    return PathFactoryImpl.CURRENT_PATH;
+                }
+            }
+
+            // default: build a new path
             // no need to check the path format, assuming all names correct
             return new PathImpl(elements, isNormalized);
         }
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java Thu Apr 10 06:41:08 2008
@@ -95,10 +95,6 @@
         assertValidPath("../a/b/../../../../f");
         assertValidPath("a/../..");
         assertValidPath("../../a/.");
-
-        // TODO: Should these paths be detected as invalid by the parser?
-        assertValidPath("/..");
-        assertValidPath("/a/b/../../..");
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java Thu Apr 10 06:41:08 2008
@@ -28,8 +28,6 @@
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
 
-import javax.jcr.RepositoryException;
-
 /**
  * PathParserTest
  */
@@ -57,7 +55,7 @@
                     Path p = PathParser.parse(t.path, resolver, factory);
                     if (t.normalizedPath==null) {
                         if (!t.isValid()) {
-                            fail("Should throw MalformedPathException: " + t.path);
+                            fail("Should throw IllegalArgumentException: " + t.path);
                         }
                         assertEquals("\"" + t.path + "\".create(false)", t.path,  pathResolver.getJCRPath(p));
                         assertEquals("\"" + t.path + "\".isNormalized()", t.isNormalized(), p.isNormalized());
@@ -66,12 +64,12 @@
                         // check with normalization
                         p = p.getNormalizedPath();
                         if (!t.isValid()) {
-                            fail("Should throw MalformedPathException: " + t.path);
+                            fail("Should throw IllegalArgumentException: " + t.path);
                         }
                         assertEquals("\"" + t.path + "\".create(true)", t.normalizedPath, pathResolver.getJCRPath(p));
                         assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
                     }
-                } catch (RepositoryException e) {
+                } catch (Exception e) {
                     if (t.isValid()) {
                         System.out.println(t.path);
                         throw e;
@@ -108,7 +106,7 @@
         }
     }
 
-   public void testNormalizedPaths() throws Exception {
+    public void testNormalizedPaths() throws Exception {
         List paths = new ArrayList();
         // normalized paths
         paths.add(PathParser.parse("/", resolver, factory));
@@ -118,6 +116,7 @@
         paths.add(PathParser.parse("foo", resolver, factory));
         paths.add(PathParser.parse("../../foo/bar", resolver, factory));
         paths.add(PathParser.parse("..", resolver, factory));
+        paths.add(PathParser.parse(".", resolver, factory));
 
         for (Iterator it = paths.iterator(); it.hasNext(); ) {
             Path path = (Path) it.next();
@@ -132,7 +131,6 @@
         paths.add(PathParser.parse("/foo/../bar", resolver, factory));
         paths.add(PathParser.parse("/foo/./bar", resolver, factory));
         paths.add(PathParser.parse("./foo", resolver, factory));
-        paths.add(PathParser.parse(".", resolver, factory));
         paths.add(PathParser.parse("foo/..", resolver, factory));
         paths.add(PathParser.parse("../foo/..", resolver, factory));
         paths.add(PathParser.parse("../foo/.", resolver, factory));
@@ -202,7 +200,6 @@
         paths.add(PathParser.parse("./foo", resolver, factory));
         paths.add(PathParser.parse(".", resolver, factory));
         paths.add(PathParser.parse("/foo/..", resolver, factory));
-        paths.add(PathParser.parse("/../foo/..", resolver, factory));
         paths.add(PathParser.parse("/../foo/.", resolver, factory));
 
         for (Iterator it = paths.iterator(); it.hasNext(); ) {

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/JcrPath.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/JcrPath.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/JcrPath.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/JcrPath.java Thu Apr 10 06:41:08 2008
@@ -42,12 +42,12 @@
         list.add(new JcrPath("/prefix:name/prefix:name", NOR|VAL));
         list.add(new JcrPath("/name[2]/name[2]", NOR|VAL));
         list.add(new JcrPath("/prefix:name[2]/prefix:name[2]", NOR|VAL));
-        list.add(new JcrPath("a/b/c/", "a/b/c", NOR|VAL));
         list.add(new JcrPath("/a/b/c/", "/a/b/c", NOR|VAL));
         list.add(new JcrPath("/a/..../", "/a/....", NOR|VAL));
         list.add(new JcrPath("/a/b:.a./", "/a/b:.a.", NOR|VAL));
 
         // relative paths
+        list.add(new JcrPath("a/b/c/", "a/b/c", NOR|VAL));
         list.add(new JcrPath("a/b/c", NOR|VAL));
         list.add(new JcrPath("prefix:name/prefix:name", NOR|VAL));
         list.add(new JcrPath("name[2]/name[2]", NOR|VAL));
@@ -57,6 +57,8 @@
 
         // invalid paths
         list.add(new JcrPath(""));
+        list.add(new JcrPath("//"));
+        list.add(new JcrPath("/a//b"));
         list.add(new JcrPath(" /a/b/c/"));
         list.add(new JcrPath("/a/b/c/ "));
         list.add(new JcrPath("/:name/prefix:name"));
@@ -69,9 +71,19 @@
         list.add(new JcrPath(":name/prefix:name"));
         list.add(new JcrPath("name[0]/name[2]"));
         list.add(new JcrPath("prefix:name[2]foo/prefix:name[2]"));
+        list.add(new JcrPath("/..", "/..", 0));
+        list.add(new JcrPath("/a/b/../../..", "/a/b/../../..", 0));
+
+        // normalized, relative paths
+        list.add(new JcrPath(".", ".", NOR|VAL));
+        list.add(new JcrPath("..", "..", NOR|VAL));
+        list.add(new JcrPath("../..", "../..", NOR|VAL));
+        list.add(new JcrPath("../../a/b", "../../a/b", NOR|VAL));
+        list.add(new JcrPath("../a", "../a",NOR|VAL));        
 
         // not normalized paths
         list.add(new JcrPath("/a/../b", "/b", VAL));
+        list.add(new JcrPath("/a/../b/./c/d/..", "/b/c", VAL));
         list.add(new JcrPath("./../.", "..", VAL));
         list.add(new JcrPath("/a/./b", "/a/b", VAL));
         list.add(new JcrPath("/a/b/../..", "/", VAL));
@@ -80,9 +92,13 @@
         list.add(new JcrPath("a/../..", "..", VAL));
         list.add(new JcrPath("../../a/.", "../../a", VAL));
 
-        // invalid normalized paths
-        list.add(new JcrPath("/..", "/..", 0));
-        list.add(new JcrPath("/a/b/../../..", "/a/b/../../..", 0));
+        // other non-normalized, relative path
+        list.add(new JcrPath("./.", ".", VAL));
+        list.add(new JcrPath("./a", "a", VAL));
+        list.add(new JcrPath("a/..", ".", VAL));
+        list.add(new JcrPath("../a/..", "..", VAL));
+        list.add(new JcrPath("../a/.", "../a", VAL));
+        list.add(new JcrPath("a/./b", "a/b", VAL));
     }
 
     /**
@@ -104,7 +120,7 @@
     public JcrPath(String path, String normalizedPath, int flags) {
         this.path = path;
         this.normalizedPath = normalizedPath;
-        this.flags = flags | ((path.length()>0 && path.charAt(0)=='/') ? ABS : 0);
+        this.flags = flags | ((path.length() > 0 && path.charAt(0)=='/') ? ABS : 0);
     }
 
     public static JcrPath[] getTests() {
@@ -139,4 +155,4 @@
         }
         return b.toString();
     }
-}
\ No newline at end of file
+}

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java?rev=646802&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java Thu Apr 10 06:41:08 2008
@@ -0,0 +1,284 @@
+/*
+ * 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.spi.commons.name;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+
+import javax.jcr.NamespaceException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * <code>PathFactoryTest</code>...
+ */
+public class PathFactoryTest extends TestCase {
+
+    private PathFactory factory;
+    private PathResolver resolver;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        factory = PathFactoryImpl.getInstance();
+
+        NamespaceResolver nsresolver = new NamespaceResolver() {
+            public String getURI(String prefix) throws NamespaceException {
+                throw new UnsupportedOperationException();
+            }
+            public String getPrefix(String uri) throws NamespaceException {
+                if (uri.equals(Name.NS_JCR_URI)) {
+                    return Name.NS_JCR_PREFIX;
+                } else {
+                    return uri;
+                }
+            }
+        };
+        resolver = new DefaultNamePathResolver(nsresolver);
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private String getString(Path p) throws NamespaceException {
+        return resolver.getJCRPath(p);
+    }
+
+    public void testCreateNullName() {
+        try {
+            factory.create((Name) null);
+            fail("Creating with null name is invalid");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCreateNullNameIndex() {
+        try {
+            factory.create(null, 1);
+            fail("Creating with null name is invalid");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCreateElementNullName() {
+        try {
+            factory.createElement(null);
+            fail("Creating element with null name is invalid");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCreateElementNullNameIndex() {
+        try {
+            factory.createElement(null, 1);
+            fail("Creating element with null name is invalid");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCreateWithInvalidIndex() {
+        try {
+            factory.create(NameConstants.JCR_NAME, -1);
+            fail("-1 is an invalid index");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCreateElementWithInvalidIndex() {
+        try {
+            factory.createElement(NameConstants.JCR_NAME, -1);
+            fail("-1 is an invalid index");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testRoot() {
+        assertTrue(factory.getRootPath().isAbsolute());
+        assertTrue(factory.getRootPath().isNormalized());
+    }
+
+    public void testCreateRoot() {
+        Path root = factory.getRootPath();
+        Path.Element rootElement = factory.getRootElement();
+        Name rootName = rootElement.getName();
+
+        assertEquals(root, factory.create(rootName));
+        assertEquals(root, factory.create(new Path.Element[] {rootElement}));
+        assertEquals(root, factory.create(root.toString()));
+        assertEquals(root, factory.create(new Path.Element[] {factory.createElement(rootName)}));
+
+        try {
+            factory.create(rootName, 1);
+            fail("Cannot create path from root name with a specific index.");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            factory.createElement(rootName, 1);
+            fail("Cannot create element from root name with a specific index.");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCurrent() {
+        Path.Element currElem = factory.getCurrentElement();
+        Name currName = currElem.getName();
+
+        assertEquals(currElem, factory.createElement(currName));
+
+        Path current = factory.create(new Path.Element[] {currElem});
+        assertEquals(current, factory.create(currName));
+        assertFalse(current.isAbsolute());
+        assertTrue(current.isNormalized());
+
+        try {
+            factory.createElement(currName, 1);
+            fail("Cannot create current element with an index.");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            factory.create(currName, 1);
+            fail("Cannot create current path with an index.");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testParent() {
+        Path.Element parentElem = factory.getParentElement();
+        Name parentName = parentElem.getName();
+
+        assertEquals(parentElem, factory.createElement(parentName));
+
+        Path parent = factory.create(new Path.Element[] {parentElem});
+        assertEquals(parent, factory.create(parentName));
+        assertFalse(parent.isAbsolute());
+        assertTrue(parent.isNormalized());
+
+        try {
+            factory.createElement(parentName, 1);
+            fail("Cannot create parent element with an index.");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            factory.create(parentName, 1);
+            fail("Cannot create parent path with an index.");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testCreateInvalidPath() throws NamespaceException {
+
+        Path.Element rootEl = factory.getRootElement();
+        Path.Element pe = factory.getParentElement();
+        Path.Element ce = factory.getCurrentElement();
+        Path.Element element = factory.createElement(NameConstants.JCR_NAME, 3);
+        Path.Element element2 = factory.createElement(NameConstants.JCR_DATA, 3);
+
+        List elementArrays = new ArrayList();
+        elementArrays.add(new Path.Element[]{rootEl, rootEl});
+        elementArrays.add(new Path.Element[] {element, rootEl, pe});
+        elementArrays.add(new Path.Element[] {pe, rootEl, element});
+        elementArrays.add(new Path.Element[] {pe, rootEl, element});
+        elementArrays.add(new Path.Element[] {rootEl, pe});
+        elementArrays.add(new Path.Element[] {rootEl, element, element2, pe, pe, pe});
+
+        for (Iterator it = elementArrays.iterator(); it.hasNext(); ) {
+            try {
+                Path p = factory.create((Path.Element[]) it.next());
+                fail("Invalid path " + getString(p));
+            } catch (IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    public void testCreateInvalidPath2() {
+        Path root = factory.getRootPath();
+        Name rootName = factory.getRootElement().getName();
+        Name parentName = factory.getParentElement().getName();
+
+        List list = new ArrayList();
+        list.add(new ParentPathNameIndexDoNormalize(root, rootName, -1, true));
+        list.add(new ParentPathNameIndexDoNormalize(root, rootName, -1, false));
+        list.add(new ParentPathNameIndexDoNormalize(root, rootName, 3, false));
+        list.add(new ParentPathNameIndexDoNormalize(factory.create(parentName), rootName, 3, true));
+
+        for (Iterator it = list.iterator(); it.hasNext();) {
+            ParentPathNameIndexDoNormalize test = (ParentPathNameIndexDoNormalize) it.next();
+            try {
+                if (test.index == -1) {
+                    Path p = factory.create(test.parentPath, test.name, test.doNormalize);
+                } else {
+                    Path p = factory.create(test.parentPath, test.name, test.index, test.doNormalize);
+                }
+                fail("Invalid path " + test.parentPath + " + " + test.name);
+            } catch (Exception e) {
+                // ok
+            }
+        }
+    }
+
+    public void testCreateInvalidPath3() {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (!tests[i].isValid()) {
+                try {
+                    Path p = resolver.getQPath(tests[i].path);
+                    fail("Invalid path " + getString(p));
+                } catch (Exception e) {
+                    // ok
+                }
+            }
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    private static class ParentPathNameIndexDoNormalize {
+
+        private final Path parentPath;
+        private final Name name;
+        private final int index;
+        private final boolean doNormalize;
+
+        private ParentPathNameIndexDoNormalize(Path parentPath, Name name,
+                                               int index, boolean doNormalize) {
+            this.parentPath = parentPath;
+            this.name = name;
+            this.index = index;
+            this.doNormalize = doNormalize;
+        }
+    }
+}
+

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathTest.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathTest.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathTest.java Thu Apr 10 06:41:08 2008
@@ -16,16 +16,23 @@
  */
 package org.apache.jackrabbit.spi.commons.name;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+
 import junit.framework.TestCase;
+
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
 import org.apache.jackrabbit.spi.commons.conversion.ParsingNameResolver;
 import org.apache.jackrabbit.spi.commons.conversion.ParsingPathResolver;
 import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
 import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
-
-import javax.jcr.NamespaceException;
-import javax.jcr.RepositoryException;
+import org.apache.jackrabbit.util.Text;
 
 /**
  * <code>PathTest</code>...
@@ -41,7 +48,8 @@
             return uri;
         }
     };
-    private static final PathResolver resolver = new ParsingPathResolver(factory, new ParsingNameResolver(NameFactoryImpl.getInstance(), nsResolver));
+    private static final NameResolver nameResolver = new ParsingNameResolver(NameFactoryImpl.getInstance(), nsResolver);
+    private static final PathResolver resolver = new ParsingPathResolver(factory, nameResolver);
 
     public void testRootIsDescendantOfRoot() throws RepositoryException {
         Path root = factory.getRootPath();
@@ -52,6 +60,111 @@
         assertFalse(root.isAncestorOf(root));
     }
 
+    public void testGetAncestor() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                if (p.getNormalizedPath().denotesRoot()) {
+                    continue;
+                }
+
+                String jcrAncestor = Text.getRelativeParent(resolver.getJCRPath(p.getNormalizedPath()), 1);
+                Path ancestor = resolver.getQPath(jcrAncestor);
+                assertEquals(ancestor, p.getAncestor(1));
+            }
+        }
+    }
+
+    public void testGetAncestorOfRelativePath() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        int degree = 5;
+        for (int i = 0; i < tests.length; i++) {
+            JcrPath test = tests[i];
+            if (test.isValid() && !test.isAbsolute()) {
+                Path p = resolver.getQPath(test.path);
+
+                StringBuffer expJcrAnc = new StringBuffer(test.path);
+                expJcrAnc.append((test.path.endsWith("/") ? "" : "/"));
+                expJcrAnc.append("../../../../..");
+
+                Path ancestor = resolver.getQPath(expJcrAnc.toString()).getNormalizedPath();
+                assertEquals(ancestor, p.getAncestor(5).getNormalizedPath());
+            }
+        }
+    }
+
+    public void testGetAncestorAtDegreeDepth() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            JcrPath test = tests[i];
+            if (test.isValid() && test.isAbsolute()) {
+                Path p = resolver.getQPath(test.path);
+
+                int degree = p.getDepth();
+                if (degree > 0) {
+                    assertTrue(p.getAncestor(degree).denotesRoot());
+                }
+            }
+        }
+    }
+
+    public void testGetAncestorIsAncestor() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                while (!p.getNormalizedPath().denotesRoot()) {
+                    Path ancestor = p.getAncestor(1);
+                    assertTrue(ancestor.isAncestorOf(p));
+                    p = ancestor;
+                }
+            }
+        }
+    }
+
+    public void testGetAncestorOfRelativePath2() throws RepositoryException {
+        for (Iterator it = JcrPathAndAncestor.list.iterator(); it.hasNext();) {
+            JcrPathAndAncestor tp = (JcrPathAndAncestor) it.next();
+
+            Path ancestor = resolver.getQPath(tp.ancestor).getNormalizedPath();
+            Path p = resolver.getQPath(tp.path);
+            assertEquals("Expected ancestor " + tp.ancestor + " was " + tp.path + ".",
+                    ancestor, p.getAncestor(tp.degree).getNormalizedPath());
+        }
+    }
+
+    public void testGetAncestorReturnsNormalized() throws RepositoryException {
+        List tests = JcrPathAndAncestor.list;
+        for (Iterator it = tests.iterator(); it.hasNext();) {
+            JcrPathAndAncestor test = (JcrPathAndAncestor) it.next();
+
+            Path p = resolver.getQPath(test.path);
+            assertTrue(p.getAncestor(test.degree).isNormalized());
+        }
+    }
+
+    public void testIsAncestorOfRelativePath() throws RepositoryException {
+        for (Iterator it = JcrPathAndAncestor.list.iterator(); it.hasNext();) {
+            JcrPathAndAncestor tp = (JcrPathAndAncestor) it.next();
+
+            Path ancestor = resolver.getQPath(tp.ancestor);
+            Path p = resolver.getQPath(tp.path);
+
+            if (tp.degree == 0) {
+                assertFalse(tp.ancestor + " should not be ancestor of " + tp.path,
+                    ancestor.isAncestorOf(p));
+            } else {
+                assertTrue(tp.ancestor + " should be ancestor of " + tp.path,
+                    ancestor.isAncestorOf(p));
+            }
+        }
+    }
+
     public void testAbsolutePathIsDescendantOfRoot() throws RepositoryException {
         Path root = factory.getRootPath();
         JcrPath[] tests = JcrPath.getTests();
@@ -64,6 +177,7 @@
             }
         }
     }
+
     public void testRootIsAncestorOfAbsolutePath() throws RepositoryException {
         Path root = factory.getRootPath();
         JcrPath[] tests = JcrPath.getTests();
@@ -77,6 +191,59 @@
         }
     }
 
+    public void testIsEquivalentToSelf() throws RepositoryException {
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid()) {
+                Path p = resolver.getQPath(tests[i].path);
+                assertTrue(p.isEquivalentTo(p));
+            }
+        }
+    }
+
+    public void testIsEquivalentTo() throws IllegalArgumentException, RepositoryException {
+        for (Iterator it = Equivalent.list.iterator(); it.hasNext();) {
+            Equivalent tp = (Equivalent) it.next();
+
+            Path path = resolver.getQPath(tp.path);
+            Path other = resolver.getQPath(tp.other);
+
+            if (tp.isEquivalent) {
+                assertTrue(tp.path + " should be equivalent to " + tp.other,
+                    path.isEquivalentTo(other));
+            } else {
+                assertFalse(tp.path + " should not be equivalent to " + tp.other,
+                        path.isEquivalentTo(other));
+            }
+        }
+    }
+
+    public void testIsAncestorIsDescendant() throws RepositoryException {
+        Path absPath = factory.getRootPath();
+        Path relPath = factory.create(NameConstants.JCR_DATA);
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid()) {
+                Path p = resolver.getQPath(tests[i].path).getNormalizedPath();
+                if (tests[i].isAbsolute()) {
+                    if (absPath.isAncestorOf(p)) {
+                        assertTrue(p.isDescendantOf(absPath));
+                    } else {
+                        assertFalse(p.isDescendantOf(absPath));
+                    }
+                    absPath = p;
+                } else {
+                    if (relPath.isAncestorOf(p)) {
+                        assertTrue(p.isDescendantOf(relPath));
+                    } else {
+                        assertFalse(p.isDescendantOf(relPath));
+                    }
+                    relPath = p;
+                }
+            }
+        }
+    }
+
     /**
      * Test if IllegalArgumentException is thrown as expected.
      */
@@ -91,7 +258,7 @@
     }
 
     /**
-     * Test if IllegalArgumentException is thrown as expected.
+     * Testing Path.isDescendantOf with rel/abs path where the path is abs/rel.
      */
     public void testIsDescendantOfThrowsIllegalArgumentException() throws RepositoryException {
         Path abs = factory.create(factory.getRootPath(), NameConstants.JCR_DATA, true);
@@ -116,20 +283,6 @@
     }
 
     /**
-     * Test if RepositoryException is thrown as expected.
-     */
-    public void testIsDescendantOfThrowsRepositoryException() throws RepositoryException {
-        Path abs = factory.create(NameConstants.JCR_DATA);
-        Path rel = factory.create(new Path.Element[] {factory.getCurrentElement()});
-        try {
-            abs.isDescendantOf(rel);
-            fail("Path.isDescendantOf(Path) must throw RepositoryException if either path cannot be normalized.");
-        } catch (RepositoryException e) {
-            // ok.
-        }
-    }
-
-    /**
      * Test if IllegalArgumentException is thrown as expected.
      */
     public void testIsAncestorOfNull() throws RepositoryException {
@@ -167,17 +320,416 @@
         }
     }
 
-    /**
-     * Test if RepositoryException is thrown as expected.
-     */
-    public void testIsAncestorOfThrowsRepositoryException() throws RepositoryException {
-        Path abs = factory.create(NameConstants.JCR_DATA);
-        Path rel = factory.create(new Path.Element[] {factory.getCurrentElement()});
-        try {
-            abs.isAncestorOf(rel);
-            fail("Path.isAncestorOf(Path) must throw RepositoryException if either path cannot be normalized.");
-        } catch (RepositoryException e) {
-            // ok.
+    public void testAbsolutePaths() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                assertTrue("Path must be absolute " + tests[i].path, p.isAbsolute());
+            }
+        }
+    }
+
+    public void testNotAbsolutePaths() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && !tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                assertFalse("Path must not be absolute " + tests[i].path, p.isAbsolute());
+            }
+        }
+    }
+
+    public void testCanonicalPaths() throws Exception {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);;
+                if (!tests[i].isNormalized()) {
+                    p = p.getNormalizedPath();
+                }
+                assertTrue("Path must be canonical " + tests[i].path, p.isCanonical());
+            }
+        }
+    }
+
+    public void testNotCanonicalPaths() throws Exception {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && (!tests[i].isNormalized() || !tests[i].isAbsolute())) {
+                Path p = resolver.getQPath(tests[i].path);
+                assertFalse("Path must not be canonical " + tests[i].path, p.isCanonical());
+            }
+        }
+    }
+
+    public void testIsNotAncestor() throws RepositoryException {
+        for (Iterator it = NotAncestor.list.iterator(); it.hasNext();) {
+            NotAncestor test = (NotAncestor) it.next();
+            Path p = resolver.getQPath(test.path);
+            Path ancestor = resolver.getQPath(test.notAncestor);
+            assertFalse(test.notAncestor + " isn't an ancestor of " + test.path,
+                    ancestor.isAncestorOf(p));
+        }
+    }
+
+    public void testDepth() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                String normJcrPath = (tests[i].normalizedPath == null) ? tests[i].path : tests[i].normalizedPath;
+                int depth = Text.explode(normJcrPath, '/').length;
+                assertTrue("Depth of " + tests[i].path + " must be " + depth, depth == p.getDepth());
+            }
+        }
+    }
+
+    public void testDepthOfRelativePath() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && !tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                int depth = Path.ROOT_DEPTH;
+                Path.Element[] elements = p.getNormalizedPath().getElements();
+                for (int j = 0; j < elements.length; j++) {
+                    if (elements[j].denotesParent()) {
+                        depth--;
+                    } else if (elements[j].denotesName()) {
+                        depth++;
+                    }
+                }
+                //System.out.println("Depth of " + tests[i].path + " = " + depth);
+                assertTrue("Depth of " + tests[i].path + " must be " + depth, depth == p.getDepth());
+            }
+        }
+    }
+
+    public void testDepthOfRoot() throws RepositoryException {
+        assertTrue("Depth of root must be " + Path.ROOT_DEPTH,
+                factory.getRootPath().getDepth() == Path.ROOT_DEPTH);
+    }
+
+    public void testDepthOfCurrent() throws RepositoryException {
+        Path current = factory.create(factory.getCurrentElement().getName());
+        assertTrue("Depth of current must be same as for root (" + Path.ROOT_DEPTH + ")",
+                current.getDepth() == Path.ROOT_DEPTH);
+    }
+
+    public void testDepthOfParent() throws RepositoryException {
+        Path parent = factory.create(factory.getParentElement().getName());
+        int depth = Path.ROOT_DEPTH - 1;
+        assertTrue("Depth of parent must be same as for root -1 (" + depth + ")",
+                parent.getDepth() == depth);
+    }
+
+    public void testAncestorCount() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                assertTrue("Ancestor count must be same a depth", p.getDepth() == p.getAncestorCount());
+            }
+        }
+    }
+
+    public void testAncestorCountOfRelativePath() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && !tests[i].isAbsolute()) {
+                Path p = resolver.getQPath(tests[i].path);
+                assertTrue("Ancestor count or a relative path must be -1", -1 == p.getAncestorCount());
+            }
+        }
+    }
+
+    public void testAncestorCountOfRoot() throws RepositoryException {
+        assertTrue("AncestorCount of root must be " + 0,
+                factory.getRootPath().getAncestorCount() == 0);
+    }
+
+    public void testAncestorCountOfCurrent() throws RepositoryException {
+        Path current = factory.create(factory.getCurrentElement().getName());
+        assertTrue("AncestorCount of current must be -1",
+                current.getAncestorCount() == -1);
+    }
+
+    public void testAncestorCountOfParent() throws RepositoryException {
+        Path parent = factory.create(factory.getParentElement().getName());
+        assertTrue("AncestorCount of parent must be same as for -1",
+                parent.getAncestorCount() == - 1);
+    }
+
+    public void testLength() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid()) {
+                int length = Text.explode(tests[i].path, '/').length;
+                if (tests[i].isAbsolute()) {
+                    length++;
+                }
+                Path p = resolver.getQPath(tests[i].path);
+                //System.out.println("Length of " + tests[i].path + " = " + length);
+                assertEquals("Length of " + tests[i].path + " must reflect " +
+                        "number of elements.", new Integer(length), new Integer(p.getLength()));
+            }
+        }
+    }
+
+    public void testIsNormalized() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid()) {
+                Path p = resolver.getQPath(tests[i].path);
+                if (tests[i].isNormalized()) {
+                    assertTrue("Path " + tests[i].path + " must be normalized.", p.isNormalized());
+                } else {
+                    assertFalse("Path " + tests[i].path + " must not be normalized.", p.isNormalized());
+                }
+            }
+        }
+    }
+
+    public void testGetNameElement() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid()) {
+                Path p = resolver.getQPath(tests[i].path);
+                Path.Element nameEl = p.getNameElement();
+                Path.Element[] all = p.getElements();
+                assertEquals(all[all.length-1], nameEl);
+            }
+        }
+    }
+
+    public void testSubPath() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && tests[i].isNormalized()) {
+                Path p = resolver.getQPath(tests[i].path);
+
+                // subpath between 0 and length -> equal path
+                assertEquals(p, p.subPath(0, p.getLength()));
+
+                // subpath a single element
+                if (p.getLength() > 2) {
+                    Path expected = factory.create(new Path.Element[] {p.getElements()[1]});
+                    assertEquals(expected, p.subPath(1,2));
+                }
+                // subpath name element
+                if (p.getLength() > 2) {
+                    Path expected = factory.create(new Path.Element[] {p.getNameElement()});
+                    assertEquals(expected, p.subPath(p.getLength()-1, p.getLength()));
+                }
+            }
+        }
+    }
+
+    public void testSubPathInvalid() throws RepositoryException {
+        Path p = resolver.getQPath("/a/b/c/d/e");
+
+        try {
+            p.subPath(2,2);
+            fail("Path.subPath with identical from/to must throw IllegalArumentException");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            p.subPath(3,2);
+            fail("Path.subPath with from > to must throw IllegalArumentException");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            p.subPath(-1, 2);
+            fail("Path.subPath with from == -1 to must throw IllegalArumentException");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            p.subPath(1, p.getLength()+1);
+            fail("Path.subPath with to > length to must throw IllegalArumentException");
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testSubPathNotNormalized() throws RepositoryException {
+        Path root = factory.getRootPath();
+        JcrPath[] tests = JcrPath.getTests();
+        for (int i = 0; i < tests.length; i++) {
+            if (tests[i].isValid() && !tests[i].isNormalized()) {
+                Path p = resolver.getQPath(tests[i].path);
+                try {
+                    p.subPath(0, p.getLength());
+                    fail("Path.subPath on a non-normalized path to must throw IllegalArumentException");
+                } catch (RepositoryException e) {
+                    // ok
+                }
+            }
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    private static class JcrPathAndAncestor {
+
+        private final String path;
+        private final String ancestor;
+        private final int degree;
+
+        private JcrPathAndAncestor(String path, String ancestor, int degree) {
+            this.path = path;
+            this.ancestor = ancestor;
+            this.degree = degree;
+        }
+
+        private static List list = new ArrayList();
+        static {
+            // normalized
+            list.add(new JcrPathAndAncestor("abc/def", "abc", 1));
+            list.add(new JcrPathAndAncestor("a/b/c/", "a", 2));
+            list.add(new JcrPathAndAncestor("prefix:name[2]/prefix:name[2]", "prefix:name[2]/prefix:name[2]", 0));
+            list.add(new JcrPathAndAncestor("../../a/b/c/d", "../../a/b/c", 1));
+            list.add(new JcrPathAndAncestor("..", "../..", 1));
+            list.add(new JcrPathAndAncestor("a/b", ".", 2));
+            list.add(new JcrPathAndAncestor("a/b", "..", 3));
+            list.add(new JcrPathAndAncestor("a/b", ".", 2));
+            list.add(new JcrPathAndAncestor("..", "../..", 1));
+            list.add(new JcrPathAndAncestor("../a", "../..", 2));
+            list.add(new JcrPathAndAncestor(".", "..", 1));
+            list.add(new JcrPathAndAncestor(".", "../..", 2));
+            list.add(new JcrPathAndAncestor("../a/b", "../a", 1));
+            list.add(new JcrPathAndAncestor("../a/b", "../a", 1));
+            list.add(new JcrPathAndAncestor("a", "..", 2));
+            list.add(new JcrPathAndAncestor("a", ".", 1));
+            list.add(new JcrPathAndAncestor("a", ".", 1));
+            list.add(new JcrPathAndAncestor("../a", "..", 1));
+
+            // not normalized paths
+            list.add(new JcrPathAndAncestor("a/./b", "a", 1));
+            list.add(new JcrPathAndAncestor(".a./.b.", ".a.", 1));
+            list.add(new JcrPathAndAncestor("./../.", "./../.", 0));
+            list.add(new JcrPathAndAncestor("./../.", "../../..", 2));
+            list.add(new JcrPathAndAncestor("a/b/c/../d/..././f", "a", 4));
+            list.add(new JcrPathAndAncestor("../a/b/../../../../f", "../a/b/../../../../f", 0));
+            list.add(new JcrPathAndAncestor("../a/b/../../../../f", "../../..", 1));
+
+            list.add(new JcrPathAndAncestor("a/b/c/", "a/b/c/../../..", 3));
+            list.add(new JcrPathAndAncestor("a/b/c/", "a/b/c/../../../..", 4));
+            list.add(new JcrPathAndAncestor("a/../b", ".", 1));
+            list.add(new JcrPathAndAncestor(".", "..", 1));
+            list.add(new JcrPathAndAncestor("a/b/../..", "a/b/../..", 0));
+            list.add(new JcrPathAndAncestor("a/b/../..", "..", 1));
+            list.add(new JcrPathAndAncestor("a", "a", 0));
+            list.add(new JcrPathAndAncestor(".../...", "..", 3));
+            list.add(new JcrPathAndAncestor("../a/b/../../../../f", "../a/b/../../../../f/../..", 2));
+        }
+    }
+
+    private static class NotAncestor {
+
+        private final String path;
+        private final String notAncestor;
+
+        private NotAncestor(String path, String notAncestor) {
+            this.path = path;
+            this.notAncestor = notAncestor;
+        }
+
+        private static List list = new ArrayList();
+        static {
+            // false if same path
+            list.add(new NotAncestor("/", "/"));
+            list.add(new NotAncestor("/a/.", "/a"));
+
+            // false if siblings or in sibling tree
+            list.add(new NotAncestor("a", "b"));
+            list.add(new NotAncestor("a/b", "b"));
+            list.add(new NotAncestor("../../a/b/c", "../../d/a/b"));
+            list.add(new NotAncestor("../../a/b/c", "../../d/e/f"));
+
+            // false if path to test is ancestor
+            list.add(new NotAncestor("/", "/a"));
+            list.add(new NotAncestor("/", "/a/."));
+            list.add(new NotAncestor("/", "/a/b/c"));
+            list.add(new NotAncestor("a/b", "a/b/c"));
+            list.add(new NotAncestor("../..", ".."));
+
+            // undefined if ancestor -> false
+            list.add(new NotAncestor("a", "../a"));
+            list.add(new NotAncestor("b", "../a"));
+            list.add(new NotAncestor("../../b", "../../../a"));
+            list.add(new NotAncestor("../../a", "../../../a"));
+            list.add(new NotAncestor(".", "../../a"));
+            list.add(new NotAncestor(".", "../a"));
+            list.add(new NotAncestor("../a", "../../../a/a"));
+            list.add(new NotAncestor("../../a/b/c", "../../../a/b"));
+            list.add(new NotAncestor("../../a/b/c", "../../../a"));
+            list.add(new NotAncestor("../../d/b/c", "../../../a"));
+
+            // misc relative paths
+            list.add(new NotAncestor(".", "a/b"));
+            list.add(new NotAncestor("../..", ".."));
+            list.add(new NotAncestor("../../a", ".."));
+            list.add(new NotAncestor("../..", "../a"));
+            list.add(new NotAncestor(".", "."));
+            list.add(new NotAncestor("..", "."));
+            list.add(new NotAncestor("../..", "."));
+            list.add(new NotAncestor("../../a", "b"));
+            list.add(new NotAncestor("b", "../../a"));
+            list.add(new NotAncestor("../../a", "."));
+            list.add(new NotAncestor(".", "../../a"));
+            list.add(new NotAncestor("../../a", "a/.."));
+            list.add(new NotAncestor("a/..", "../../a"));
+            list.add(new NotAncestor("../a", "."));
+            list.add(new NotAncestor(".", "../a"));
+            list.add(new NotAncestor("../a", "a/.."));
+            list.add(new NotAncestor("a/..", "../a"));
+            list.add(new NotAncestor("../a", "../a/b"));
+            list.add(new NotAncestor("..", "a"));
+            list.add(new NotAncestor(".", "a"));
+            list.add(new NotAncestor("..", ".."));
+            list.add(new NotAncestor(".", "."));
+            list.add(new NotAncestor("..", "../a"));
+            list.add(new NotAncestor("..", "../../a"));
+            list.add(new NotAncestor("../../a", ".."));
         }
     }
-}
\ No newline at end of file
+
+    private static class Equivalent {
+
+        private final String path;
+        private final String other;
+        private final boolean isEquivalent;
+
+        private Equivalent(String path, String other, boolean isEquivalent) {
+            this.path = path;
+            this.other = other;
+            this.isEquivalent = isEquivalent;
+        }
+
+        private static List list = new ArrayList();
+        static {
+            list.add(new Equivalent(".", "a/b", false));
+            list.add(new Equivalent(".", "a/..", true));
+            list.add(new Equivalent(".", ".", true));
+            list.add(new Equivalent("..", "..", true));
+            list.add(new Equivalent("../..", "../..", true));
+            list.add(new Equivalent("../a", "../a/b/..", true));
+            list.add(new Equivalent("../a/b/..", "..", false));
+        }
+    }
+
+}

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java Thu Apr 10 06:41:08 2008
@@ -37,6 +37,7 @@
 
         suite.addTestSuite(NameFactoryTest.class);
         suite.addTestSuite(PathBuilderTest.class);
+        suite.addTestSuite(PathFactoryTest.class);
         suite.addTestSuite(PathTest.class);
 
         return suite;

Modified: jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java?rev=646802&r1=646801&r2=646802&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java (original)
+++ jackrabbit/trunk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java Thu Apr 10 06:41:08 2008
@@ -16,32 +16,96 @@
  */
 package org.apache.jackrabbit.spi;
 
+import java.io.Serializable;
+
 import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
-import java.io.Serializable;
 
 /**
  * The <code>Path</code> interface defines the qualified representation of
- * a JCR path. It consists of {@link Path.Element} objects and is immutable.
- * It has the following properties:
+ * a JCR path. It consists of an ordered list of {@link Path.Element} objects
+ * and is immutable.<p/>
+ *
+ * A {@link Path.Element} is either {@link Path.Element#denotesName() named}
+ * or one of the following special elements:
+ * <ul>
+ * <li>the {@link Element#denotesCurrent() current} element (Notation: "."),</li>
+ * <li>the {@link Element#denotesParent() parent} element (Notation: ".."),</li>
+ * <li>the {@link Element#denotesRoot() root} element (Notation: {}), which can
+ * only occur as the first element in a path.</li>
+ * </ul>
+ *
+ * A <code>Path</code> is defined to have the following characteristics:
+ * <p/>
+ *
+ * <strong>Equality:</strong><br>
+ * Two paths are equal if they consist of the same elements.
+ * <p/>
+ *
+ * <strong>Length:</strong><br>
+ * The {@link Path#getLength() length} of a path is the number of its elements.
  * <p/>
- * <code>isAbsolute()</code>:<br>
- * A path is absolute if the first path element denotes the root element '/'.
+ *
+ * <strong>Depth:</strong><br>
+ * The {@link Path#getDepth() depth} of a path is
+ * <ul>
+ * <li>0 for the root path,</li>
+ * <li>0 for the path consisting of the current element only,</li>
+ * <li>-1 for the path consisting of the parent element only,</li>
+ * <li>1 for the path consisting of any other single element,</li>
+ * <li>depth(P) + depth(Q) for the path P/Q.</li>
+ * </ul>
+ * The depth of a valid absolute path equals the length of its
+ * normalization minus 1.
  * <p/>
- * <code>isNormalized()</code>:<br>
- * A path is normalized if all '.' and '..' path elements are resolved as much
- * as possible. If the path is absolute it is normalized if it contains
- * no such elements. For example the path '../../a' is normalized where as
- * '../../b/../a/.' is not. Normalized paths never have '.' elements.
- * Absolute normalized paths have no and relative normalized paths have no or
- * only leading '..' elements.
+ *
+ * <strong>Absolute vs. Relative</strong><br>
+ * A path can be absolute or relative:<br>
+ * A path {@link #isAbsolute() is absolute} if its first element is the root
+ * element. A path is relative if it is not absolute.
+ * <ul>
+ * <li>An absolute path is valid if its depth is greater or equals 0. A relative
+ * path is always valid.</li>
+ * <li>Two absolute paths are equivalent if "they refer to the same item in the
+ * hierarchy".</li>
+ * <li>Two relative paths P and Q are equivalent if for every absolute path R such
+ * that R/P and R/Q are valid, R/P and R/Q are equivalent.</li>
+ * </ul>
  * <p/>
- * <code>isCanonical()</code>:<br>
- * A path is canonical if its absolute and normalized.
+ *
+ * <strong>Normalization:</strong><br>
+ * A path P {@link Path#isNormalized() is normalized} if P has minimal length
+ * amongst the set of all paths Q which are equivalent to P.<br>
+ * This means that '.' and '..' elements are resolved as much as possible.
+ * An absolute path it is normalized if it contains no current nor parent
+ * element. The normalization of a path is unique.<br>
  * <p/>
- * Note, that the implementation must implement the equals method such that
- * two <code>Path</code> objects having the equal <code>Element</code>s
- * must be equal.
+ *
+ * <strong>Equalivalence:</strong><br>
+ * Path P is {@link Path#isEquivalentTo(Path) equivalent} to path Q (in the above sense)
+ * if the normalization of P is equal to the normalization of Q. This is
+ * an equivalence relation (i.e. reflexive, transitive,
+ * and symmetric).
+ * <p/>
+ *
+ * <strong>Canonical Paths:</strong><br>
+ * A path {@link Path#isCanonical() is canonical} if its absolute and normalized.
+ * <p/>
+ *
+ * <strong>Hierarchical Relationship:</strong><br>
+ * The ancestor relationship is a strict partial order (i.e. irreflexive, transitive,
+ * and asymmetric). Path P is a direct ancestor of path Q if P is equivalent to Q/..
+ * <br>
+ * Path P is an {@link Path#isAncestorOf(Path) ancestor} of path Q if
+ * <ul>
+ * <li>P is a direct ancestor of Q or</li>
+ * <li>P is a direct ancestor of some path S which is an ancestor of Q.</li>
+ * </ul>
+ *
+ * Path P is an {@link Path#isDescendantOf(Path) descendant} of path Q if
+ * <ul>
+ * <li>Path P is a descendant of path Q if Q is an ancestor of P.</li>
+ * </ul>
  */
 public interface Path extends Serializable {
 
@@ -104,14 +168,10 @@
     public boolean isNormalized();
 
     /**
-     * Returns the normalized path representation of this path. This typically
-     * involves removing/resolving redundant elements such as "." and ".." from
-     * the path, e.g. "/a/./b/.." will be normalized to "/a", "../../a/b/c/.."
-     * will be normalized to "../../a/b", and so on.
+     * Returns the normalized path representation of this path.
      * <p/>
-     * If the normalized path results in an empty path (eg: 'a/..') or if an
-     * absolute path is normalized that would result in a 'negative' path
-     * (eg: /a/../../) a MalformedPathException is thrown.
+     * If the path cannot be normalized (e.g. if an absolute path is normalized
+     * that would result in a 'negative' path) a RepositoryException is thrown.
      *
      * @return a normalized path representation of this path.
      * @throws RepositoryException if the path cannot be normalized.
@@ -120,9 +180,10 @@
     public Path getNormalizedPath() throws RepositoryException;
 
     /**
-     * Returns the canonical path representation of this path. This typically
-     * involves removing/resolving redundant elements such as "." and ".." from
-     * the path.
+     * Returns the canonical path representation of this path.
+     * <p/>
+     * If the path is relative or cannot be normalized a RepositoryException
+     * is thrown.
      *
      * @return a canonical path representation of this path.
      * @throws RepositoryException if this path can not be canonicalized
@@ -143,7 +204,8 @@
     public Path computeRelativePath(Path other) throws RepositoryException;
 
     /**
-     * Returns the ancestor path of the specified relative degree.
+     * Normalizes this path and returns the ancestor path of the specified
+     * relative degree.
      * <p/>
      * An ancestor of relative degree <i>x</i> is the path that is <i>x</i>
      * levels up along the path.
@@ -155,26 +217,29 @@
      * of this path, which returns the root path.
      * </ul>
      * <p/>
-     * Note that there migth be an unexpected result if <i>this</i> path is not
-     * normalized, e.g. the ancestor of degree = 1 of the path "../.." would
-     * be ".." although this is not the parent of "../..".
+     * If this path is relative the implementation may not be able to determine
+     * if the ancestor at <code>degree</code> exists. Such an implementation
+     * should properly build the ancestor (i.e. parent of .. is ../..) and
+     * leave if it the caller to throw <code>PathNotFoundException</code>.
      *
      * @param degree the relative degree of the requested ancestor.
-     * @return the ancestor path of the specified degree.
-     * @throws PathNotFoundException if there is no ancestor of the specified degree.
+     * @return the normalized ancestor path of the specified degree.
      * @throws IllegalArgumentException if <code>degree</code> is negative.
+     * @throws PathNotFoundException if the implementation is able to determine
+     * that there is no ancestor of the specified degree. In case of this
+     * being an absolute path, this would be the case if <code>degree</code> is
+     * greater that the {@link #getDepth() depth} of this path.
      */
     public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException;
 
     /**
      * Returns the number of ancestors of this path. This is the equivalent
-     * of <code>{@link #getDepth()} - 1</code>.
-     * <p/>
-     * Note that the returned value might be negative if this path is not
-     * canonical, e.g. the depth of "../../a" is -1, its ancestor count is
-     * therefore -2.
+     * of <code>{@link #getDepth()}</code> in case of a absolute path.
+     * For relative path the number of ancestors cannot be determined and
+     * -1 should be returned.
      *
-     * @return the number of ancestors of this path
+     * @return the number of ancestors of this path or -1 if the number of
+     * ancestors cannot be determined.
      * @see #getDepth()
      * @see #getLength()
      * @see #isCanonical()
@@ -201,7 +266,8 @@
      * Returns the depth of this path. The depth reflects the absolute or
      * relative hierarchy level this path is representing, depending on whether
      * this path is an absolute or a relative path. The depth also takes '.'
-     * and '..' elements into account.
+     * and '..' elements into account. The depth of the root path and the
+     * current path must be 0.
      * <p/>
      * Note that the returned value might be negative if this path is not
      * canonical, e.g. the depth of "../../a" is -1.
@@ -213,9 +279,23 @@
     public int getDepth();
 
     /**
+     * Determines if the the <code>other</code> path would be equal to <code>this</code>
+     * path if both of them are normalized.
+     *
+     * @param other Another path.
+     * @return true if the given other path is equivalent to this path.
+     * @throws IllegalArgumentException if the given path is <code>null</code>
+     * or if not both paths are either absolute or relative.
+     * @throws RepositoryException if any of the path cannot be normalized.
+     */
+    public boolean isEquivalentTo(Path other) throws IllegalArgumentException, RepositoryException;
+
+    /**
      * Determines if <i>this</i> path is an ancestor of the specified path,
      * based on their (absolute or relative) hierarchy level as returned by
-     * <code>{@link #getDepth()}</code>.
+     * <code>{@link #getDepth()}</code>. In case of undefined ancestor/descendant
+     * relationship that might occur with relative paths, the return value
+     * should be <code>false</code>.
      *
      * @return <code>true</code> if <code>other</code> is a descendant;
      * otherwise <code>false</code>.
@@ -229,14 +309,16 @@
     /**
      * Determines if <i>this</i> path is a descendant of the specified path,
      * based on their (absolute or relative) hierarchy level as returned by
-     * <code>{@link #getDepth()}</code>.
+     * <code>{@link #getDepth()}</code>. In case of undefined ancestor/descendant
+     * relationship that might occur with relative paths, the return value
+     * should be <code>false</code>.
      *
      * @return <code>true</code> if <code>other</code> is an ancestor;
-     * otherwise <code>false</code>
+     * otherwise <code>false</code>.
      * @throws IllegalArgumentException if the given path is <code>null</code>
      * or if not both paths are either absolute or relative.
      * @throws RepositoryException if any of the path cannot be normalized.
-     * @see #getDepth()
+     * @see #isAncestorOf(Path)
      */
     public boolean isDescendantOf(Path other) throws IllegalArgumentException, RepositoryException;
 
@@ -248,10 +330,16 @@
      * out of the possible range. A <code>RepositoryException</code> is thrown
      * if this <code>Path</code> is not normalized.
      *
-     * @param from
-     * @param to
-     * @return
-     * @throws IllegalArgumentException
+     * @param from index of the element to start with and low endpoint
+     * (inclusive) within the list of elements to use for the sub-path.
+     * @param to index of the element outside of the range i.e. high endpoint
+     * (exclusive) within the list of elements to use for the sub-path.
+     * @return a new <code>Path</code> consisting of those Path.Element objects
+     * between the given <tt><code>from</code>, inclusive, and the given
+     * <code>to</code>, exclusive.
+     * @throws IllegalArgumentException if <code>from</code>
+     * is greater or equal than <code>to</code> or if any of both params is
+     * out of the possible range.
      * @throws RepositoryException If this Path is not normalized.
      */
     public Path subPath(int from, int to) throws IllegalArgumentException, RepositoryException;
@@ -277,7 +365,7 @@
      * its elements separated by {@link Path#DELIMITER}.
      *
      * @see Path.Element#getString()
-     * @return
+     * @return Returns the String representation of this Path.
      */
     public String getString();
 
@@ -359,12 +447,12 @@
         public boolean denotesName();
 
         /**
-         * Return the String presentation of a{@link Path.Element}. It must be
+         * Return the String presentation of a {@link Path.Element}. It must be
          * in the format "<code>{namespaceURI}localPart</code>" or
          * "<code>{namespaceURI}localPart[index]</code>" in case of an index
          * greater than {@link Path#INDEX_DEFAULT}.
          *
-         * @return
+         * @return String representation of a {@link Path.Element}.
          */
         public String getString();
     }