/*
* 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 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 org.apache.jackrabbit.util.Text;
/**
* <code>PathTest</code>...
*/
public class PathTest extends TestCase {
private static final PathFactory factory = PathFactoryImpl.getInstance();
private static final NamespaceResolver nsResolver = new NamespaceResolver() {
public String getURI(String prefix) throws NamespaceException {
return prefix;
}
public String getPrefix(String uri) throws NamespaceException {
return uri;
}
};
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();
assertFalse(root.isDescendantOf(root));
}
public void testRootIsAncestorOfRoot() throws RepositoryException {
Path root = factory.getRootPath();
assertFalse(root.isAncestorOf(root));
}
public void testGetAncestor() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.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 {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
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 {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
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 {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
while (!p.getNormalizedPath().denotesRoot()) {
Path ancestor = p.getAncestor(1);
assertTrue(ancestor.isAncestorOf(p));
p = ancestor;
}
}
}
}
public void testGetAncestorOfRelativePath2() throws RepositoryException {
for (Object aList : JcrPathAndAncestor.list) {
JcrPathAndAncestor tp = (JcrPathAndAncestor) aList;
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<JcrPathAndAncestor> tests = JcrPathAndAncestor.list;
for (JcrPathAndAncestor test : tests) {
Path p = resolver.getQPath(test.path);
assertTrue(p.getAncestor(test.degree).isNormalized());
}
}
public void testIsAncestorOfRelativePath() throws RepositoryException {
for (JcrPathAndAncestor tp : JcrPathAndAncestor.list) {
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();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path).getNormalizedPath();
if (!p.equals(root)) {
assertTrue(test.path + " must be decendant of the root path.", p.isDescendantOf(root));
}
}
}
}
public void testRootIsAncestorOfAbsolutePath() throws RepositoryException {
Path root = factory.getRootPath();
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path).getNormalizedPath();
if (!p.equals(root)) {
assertFalse(p.isAncestorOf(root));
}
}
}
}
public void testIsEquivalentToSelf() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid()) {
Path p = resolver.getQPath(test.path);
assertTrue(p.isEquivalentTo(p));
}
}
}
public void testIsEquivalentTo() throws IllegalArgumentException, RepositoryException {
for (Equivalent tp : Equivalent.list) {
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 (JcrPath test : tests) {
if (test.isValid()) {
Path p = resolver.getQPath(test.path).getNormalizedPath();
if (test.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.
*/
public void testIsDescendantOfNull() throws RepositoryException {
try {
Path p = factory.getRootPath();
p.isDescendantOf(null);
fail("Path.isDescendantOf(null) must throw IllegalArgumentException.");
} catch (IllegalArgumentException e) {
// ok.
}
}
/**
* 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);
Path rel = factory.create(NameConstants.JCR_DATA);
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path).getNormalizedPath();
try {
if (p.isAbsolute()) {
p.isDescendantOf(rel);
} else {
p.isDescendantOf(abs);
}
fail("Path.isDescendantOf(Path) must throw IllegalArgumentException if Path.isAbsolute is not the same for both.");
} catch (IllegalArgumentException e) {
// ok.
}
}
}
}
/**
* Test if IllegalArgumentException is thrown as expected.
*/
public void testIsAncestorOfNull() throws RepositoryException {
try {
Path p = factory.getRootPath();
p.isAncestorOf(null);
fail("Path.isAncestorOf(null) must throw IllegalArgumentException.");
} catch (IllegalArgumentException e) {
// ok.
}
}
/**
* Test if IllegalArgumentException is thrown as expected.
*/
public void testIsAncestorOfThrowsIllegalArgumentException() throws RepositoryException {
Path abs = factory.create(factory.getRootPath(), NameConstants.JCR_DATA, true);
Path rel = factory.create(NameConstants.JCR_DATA);
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path).getNormalizedPath();
try {
if (p.isAbsolute()) {
p.isAncestorOf(rel);
} else {
p.isAncestorOf(abs);
}
fail("Path.isAncestorOf(Path) must throw IllegalArgumentException if Path.isAbsolute is not the same for both.");
} catch (IllegalArgumentException e) {
// ok.
}
}
}
}
public void testAbsolutePaths() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
assertTrue("Path must be absolute " + test.path, p.isAbsolute());
}
}
}
public void testNotAbsolutePaths() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && !test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
assertFalse("Path must not be absolute " + test.path, p.isAbsolute());
}
}
}
public void testCanonicalPaths() throws Exception {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
if (!test.isNormalized()) {
p = p.getNormalizedPath();
}
assertTrue("Path must be canonical " + test.path, p.isCanonical());
}
}
}
public void testNotCanonicalPaths() throws Exception {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && (!test.isNormalized() || !test.isAbsolute())) {
Path p = resolver.getQPath(test.path);
assertFalse("Path must not be canonical " + test.path, p.isCanonical());
}
}
}
public void testIsNotAncestor() throws RepositoryException {
for (NotAncestor test : NotAncestor.list) {
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 {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
String normJcrPath = (test.normalizedPath == null) ? test.path : test.normalizedPath;
int depth = Text.explode(normJcrPath, '/').length;
assertTrue("Depth of " + test.path + " must be " + depth, depth == p.getDepth());
}
}
}
public void testDepthOfRelativePath() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && !test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
int depth = Path.ROOT_DEPTH;
Path.Element[] elements = p.getNormalizedPath().getElements();
for (Path.Element element : elements) {
if (element.denotesParent()) {
depth--;
} else if (element.denotesName()) {
depth++;
}
}
//System.out.println("Depth of " + tests[i].path + " = " + depth);
assertTrue("Depth of " + test.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 {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isAbsolute()) {
Path p = resolver.getQPath(test.path);
assertTrue("Ancestor count must be same a depth", p.getDepth() == p.getAncestorCount());
}
}
}
public void testAncestorCountOfRelativePath() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && !test.isAbsolute()) {
Path p = resolver.getQPath(test.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 {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid()) {
int length = Text.explode(test.path, '/').length;
if (test.isAbsolute()) {
length++;
}
Path p = resolver.getQPath(test.path);
//System.out.println("Length of " + tests[i].path + " = " + length);
assertEquals("Length of " + test.path + " must reflect " +
"number of elements.", new Integer(length), new Integer(p.getLength()));
}
}
}
public void testIsNormalized() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid()) {
Path p = resolver.getQPath(test.path);
if (test.isNormalized()) {
assertTrue("Path " + test.path + " must be normalized.", p.isNormalized());
} else {
assertFalse("Path " + test.path + " must not be normalized.", p.isNormalized());
}
}
}
}
public void testGetNameElement() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid()) {
Path p = resolver.getQPath(test.path);
Path.Element nameEl = p.getNameElement();
Path.Element[] all = p.getElements();
assertEquals(all[all.length - 1], nameEl);
}
}
}
public void testSubPath() throws RepositoryException {
JcrPath[] tests = JcrPath.getTests();
for (JcrPath test : tests) {
if (test.isValid() && test.isNormalized()) {
Path p = resolver.getQPath(test.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 = p.getLastElement();
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
}
}
//--------------------------------------------------------------------------
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<JcrPathAndAncestor> list = new ArrayList<JcrPathAndAncestor>();
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<NotAncestor> list = new ArrayList<NotAncestor>();
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", ".."));
}
}
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<Equivalent> list = new ArrayList<Equivalent>();
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));
}
}
}