/* * 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.conversion; import junit.framework.TestCase; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.name.JcrPath; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.PathFactory; import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver; import javax.jcr.NamespaceException; import javax.jcr.RepositoryException; /** * PathParserTest */ public class PathParserTest extends TestCase { private final PathFactory factory = PathFactoryImpl.getInstance(); private final NameResolver resolver; private final PathResolver pathResolver; private JcrPath[] tests = JcrPath.getTests(); private static int NUM_TESTS = 1; public PathParserTest() { resolver = new ParsingNameResolver(NameFactoryImpl.getInstance(), new DummyNamespaceResolver()); pathResolver = new ParsingPathResolver(factory, resolver); } public void testParse() throws Exception { for (JcrPath t : tests) { long t1 = System.currentTimeMillis(); for (int j = 0; j < NUM_TESTS; j++) { try { Path p = PathParser.parse(t.path, resolver, factory); if (t.normalizedPath == null) { if (!t.isValid()) { fail("Should throw IllegalArgumentException: " + t.path); } assertEquals("\"" + t.path + "\".create(false)", t.path, pathResolver.getJCRPath(p)); assertEquals("\"" + t.path + "\".isNormalized()", t.isNormalized(), p.isNormalized()); assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute()); } else { // check with normalization p = p.getNormalizedPath(); if (!t.isValid()) { 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 (Exception e) { if (t.isValid()) { System.out.println(t.path); throw e; } } } long t2 = System.currentTimeMillis(); if (NUM_TESTS > 1) { System.out.println("testCreate():\t" + t + "\t" + (t2 - t1) + "\tms"); } } } public void testCheckFormat() throws Exception { for (JcrPath t : tests) { long t1 = System.currentTimeMillis(); for (int j = 0; j < NUM_TESTS; j++) { if (t.normalizedPath == null) { // check just creation boolean isValid = true; try { PathParser.checkFormat(t.path); } catch (MalformedPathException e) { isValid = false; } assertEquals("\"" + t.path + "\".checkFormat()", t.isValid(), isValid); } } long t2 = System.currentTimeMillis(); if (NUM_TESTS > 1) { System.out.println("testCheckFormat():\t" + t + "\t" + (t2 - t1) + "\tms"); } } } public void testNormalizedPaths() throws Exception { List<Path> paths = new ArrayList<Path>(); // normalized paths paths.add(PathParser.parse("/", resolver, factory)); paths.add(PathParser.parse("/foo", resolver, factory)); 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("../../foo/bar", resolver, factory)); paths.add(PathParser.parse("..", resolver, factory)); paths.add(PathParser.parse(".", resolver, factory)); for (Path path : paths) { assertTrue("path is not normalized: " + path, path.isNormalized()); } paths.clear(); // not normalized paths paths.add(PathParser.parse("/foo/..", resolver, factory)); paths.add(PathParser.parse("/foo/.", resolver, factory)); 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("foo/..", resolver, factory)); paths.add(PathParser.parse("../foo/..", resolver, factory)); paths.add(PathParser.parse("../foo/.", resolver, factory)); for (Path path : paths) { assertFalse("path is normalized: " + path, path.isNormalized()); } } public void testAbsolutePaths() throws Exception { List<Path> paths = new ArrayList<Path>(); // absolute paths paths.add(PathParser.parse("/", resolver, factory)); paths.add(PathParser.parse("/foo", resolver, factory)); 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("/foo/./bar", resolver, factory)); paths.add(PathParser.parse("/foo/.././bar/./foo", resolver, factory)); for (Path path : paths) { assertTrue("path is not absolute: " + path, path.isAbsolute()); } paths.clear(); // not absoulute paths paths.add(PathParser.parse("foo/..", resolver, factory)); paths.add(PathParser.parse("foo/.", resolver, factory)); 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)); for (Path path : paths) { assertFalse("path is absolute: " + path, path.isAbsolute()); } } public void testExpandedPaths() throws Exception { Map<String,Path> paths = new HashMap<String,Path>(); NameResolver ns = new ParsingNameResolver(NameFactoryImpl.getInstance(), new TestNamespaceResolver()); PathResolver ps = new ParsingPathResolver(factory, ns); // expanded paths paths.put("/",PathParser.parse("/", ns, factory)); paths.put("/foo",PathParser.parse("/{}foo", ns, factory)); paths.put("/a:foo/a:bar",PathParser.parse("/{http://jackrabbit.apache.org}foo/{http://jackrabbit.apache.org}bar", ns, factory)); paths.put("/a:foo/../a:bar",PathParser.parse("/{http://jackrabbit.apache.org}foo/../{http://jackrabbit.apache.org}bar", ns, factory)); paths.put("/foo/..",PathParser.parse("/{}foo/..", ns, factory)); paths.put("/a:foo/./a:bar",PathParser.parse("/{http://jackrabbit.apache.org}foo/./{http://jackrabbit.apache.org}bar", ns, factory)); paths.put("/a:foo/.././a:bar/./a:foo",PathParser.parse("/{http://jackrabbit.apache.org}foo/.././{http://jackrabbit.apache.org}bar/./{http://jackrabbit.apache.org}foo", resolver, factory)); paths.put("/foo/.{a}/a:b",PathParser.parse("/{}foo/{}.{a}/{http://jackrabbit.apache.org}b", ns, factory)); paths.put("/foo/.{.}/a:c",PathParser.parse("/{}foo/{}.{.}/{http://jackrabbit.apache.org}c", ns, factory)); paths.put("foo/.{.}/a:c",PathParser.parse("{}foo/{}.{.}/{http://jackrabbit.apache.org}c", ns, factory)); for (String key : paths.keySet()) { Path path = paths.get(key); assertEquals(key, ps.getJCRPath(path)); } } public void testJCRPaths() throws Exception { Map<String,Path> paths = new HashMap<String,Path>(); paths.put("/",PathParser.parse("/", resolver, factory)); paths.put("/foo",PathParser.parse("/foo", resolver, factory)); paths.put("/a:foo/a:bar",PathParser.parse("/a:foo/a:bar", resolver, factory)); paths.put("/a:foo/../a:bar",PathParser.parse("/a:foo/../a:bar", resolver, factory)); paths.put("/a:foo/..",PathParser.parse("/a:foo/..", resolver, factory)); paths.put("/a:foo/./a:bar",PathParser.parse("/a:foo/./a:bar", resolver, factory)); paths.put("/a:foo/.././a:bar/./a:foo",PathParser.parse("/a:foo/.././a:bar/./a:foo", resolver, factory)); paths.put("foo/..",PathParser.parse("foo/..", resolver, factory)); paths.put("foo/.",PathParser.parse("foo/.", resolver, factory)); paths.put("foo/../bar",PathParser.parse("foo/../bar", resolver, factory)); paths.put("foo/./bar",PathParser.parse("foo/./bar", resolver, factory)); paths.put("./foo",PathParser.parse("./foo", resolver, factory)); paths.put(".",PathParser.parse(".", resolver, factory)); paths.put("foo/..",PathParser.parse("foo/..", resolver, factory)); paths.put("../foo/..",PathParser.parse("../foo/..", resolver, factory)); paths.put("../foo/.",PathParser.parse("../foo/.", resolver, factory)); paths.put("/foo/.{a}/a:b",PathParser.parse("/foo/.{a}/a:b", resolver, factory)); paths.put("/a:foo/.{.}/a:c",PathParser.parse("/a:foo/.{.}/a:c", resolver, factory)); paths.put("/a:foo/{.}/a:c",PathParser.parse("/a:foo/{.}/a:c", resolver, factory)); paths.put("/a:foo/{..}/a:c",PathParser.parse("/a:foo/{..}/a:c", resolver, factory)); paths.put("/a:foo/{...}/a:c",PathParser.parse("/a:foo/{...}/a:c", resolver, factory)); paths.put("/a:foo/.{.}/a:c",PathParser.parse("/a:foo/.{.}/a:c", resolver, factory)); paths.put("/a:foo/.{.}/a:c",PathParser.parse("/a:foo/.{.}/a:c", resolver, factory)); paths.put(".{a}",PathParser.parse(".{a}", resolver, factory)); paths.put(".{.}",PathParser.parse(".{.}", resolver, factory)); paths.put("..{.}",PathParser.parse("..{.}", resolver, factory)); paths.put("..{..}",PathParser.parse("..{..}", resolver, factory)); paths.put(".{...}",PathParser.parse(".{...}", resolver, factory)); paths.put("{...}",PathParser.parse("{...}", resolver, factory)); paths.put("...",PathParser.parse("...", resolver, factory)); paths.put("a:.{.}",PathParser.parse("a:.{.}", resolver, factory)); paths.put("..{a}",PathParser.parse("..{a}", resolver, factory)); paths.put(".{..}",PathParser.parse(".{..}", resolver, factory)); paths.put("a:..{.}",PathParser.parse("a:..{.}", resolver, factory)); paths.put("a:.{..}",PathParser.parse("a:.{..}", resolver, factory)); paths.put("a:..{..}",PathParser.parse("a:..{..}", resolver, factory)); paths.put(".a",PathParser.parse(".a", resolver, factory)); paths.put("..a",PathParser.parse("..a", resolver, factory)); for (String key : paths.keySet()) { Path path = paths.get(key); assertEquals(key, pathResolver.getJCRPath(path)); } } public void testInvalidJCRPaths() throws Exception { List<String> paths = new ArrayList<String>(); paths.add("/a:.."); paths.add("/a:."); paths.add("/a::"); paths.add("/a:{:a}"); paths.add("/.{:a}"); paths.add("/.{a:a}"); paths.add("/:"); paths.add("/*"); paths.add("//"); paths.add("foo\u3000bar"); // non-ASCII whitespace for (String jcrPath : paths) { try { PathParser.parse(jcrPath, resolver, factory); fail(jcrPath + " isn't a valid jcr path"); } catch (MalformedPathException e) { // ok. } } } public void testCanonicalPaths() throws Exception { List<Path> paths = new ArrayList<Path>(); // canonical paths paths.add(PathParser.parse("/", resolver, factory)); paths.add(PathParser.parse("/foo", resolver, factory)); paths.add(PathParser.parse("/foo/bar", resolver, factory)); for (Path path : paths) { assertTrue("path is not canonical: " + path, path.isCanonical()); } paths.clear(); // non-canonical paths paths.add(PathParser.parse("/foo/..", resolver, factory)); paths.add(PathParser.parse("/foo/.", resolver, factory)); 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)); for (Path path : paths) { assertFalse("path is canonical: " + path, path.isCanonical()); } } public void testIdentifierParse() throws RepositoryException { DummyIdentifierResolver idResolver = new DummyIdentifierResolver(); List<String> valid = idResolver.getValidIdentifiers(); for (String id : valid) { String jcrPath = "[" + id + "]"; try { PathParser.parse(jcrPath, resolver, factory); fail("Parsing an identifier-based jcr path needs a IdentifierResolver"); } catch (MalformedPathException e) { // success: cannot parse identifier path if idResolver is missing. } try { PathParser.parse(factory.getRootPath(), jcrPath, resolver, factory); fail("Parsing an identifier-based jcr path needs a IdentifierResolver"); } catch (MalformedPathException e) { // success: cannot parse identifier path if idResolver is missing. } Path p = PathParser.parse(jcrPath, resolver, idResolver, factory, true); assertFalse(p.isIdentifierBased()); p = PathParser.parse(jcrPath, resolver, idResolver, factory, false); assertTrue(p.isIdentifierBased()); try { PathParser.parse(factory.getRootPath(), jcrPath, resolver, idResolver, factory); fail("Cannot parser an identifier-based path to a relative path."); } catch (MalformedPathException e) { // success: invalid argument parent-path if the jcr-path is an identifier-based path. } try { PathParser.parse(jcrPath, resolver, factory); fail("Parsing an identifier-based jcr path needs a IdentifierResolver"); } catch (MalformedPathException e) { // success: cannot parse identifier path if idResolver is missing. } } } public void testIdentifierParseWithTrailingString() throws RepositoryException { List<String> suffix = new ArrayList<String>(); suffix.add("/"); // additional path delimiter suffix.add("/property"); // additional path segment suffix.add("suffix"); // trailing string suffix.add("[1]"); // an index DummyIdentifierResolver idResolver = new DummyIdentifierResolver(); List<String> valid = idResolver.getValidIdentifiers(); for (String id : valid) { for (String s : suffix) { String jcrPath = "[" + id + "]" + s; try { PathParser.parse(jcrPath, resolver, idResolver, factory, true); } catch (MalformedPathException e) { // success } try { PathParser.parse(jcrPath, resolver, idResolver, factory, false); } catch (MalformedPathException e) { // success } } } } public void testInvalidIdentifierParse() throws RepositoryException { DummyIdentifierResolver idResolver = new DummyIdentifierResolver(); List<String> invalid = idResolver.getInvalidIdentifierPaths(); for (String jcrPath : invalid) { try { PathParser.parse(jcrPath, resolver, idResolver, factory, true); fail("Invalid identifier based path"); } catch (MalformedPathException e) { // ok } try { PathParser.parse(jcrPath, resolver, idResolver, factory, false); fail("Invalid identifier based path"); } catch (MalformedPathException e) { // ok } } } public void testIdentifierCheckFormat() throws RepositoryException { DummyIdentifierResolver idResolver = new DummyIdentifierResolver(); List<String> valid = idResolver.getValidIdentifiers(); for (String id : valid) { String jcrPath = "[" + id + "]"; PathParser.checkFormat(jcrPath); } List<String> invalid = idResolver.getInvalidIdentifierFormats(); for (String jcrPath : invalid) { try { // passing null-nameResolver -> executes check-format only PathParser.checkFormat(jcrPath); fail(jcrPath); } catch (MalformedPathException e) { // success } } } /** * Dummy NamespaceResolver that only knows the empty namespace and * namespaces containing either 'jackrabbit' or 'abc'. Used to test * the parsing of the expanded jcr names, which should treat a jcr name with * unknown namespace uri qualified jcr names. */ private class TestNamespaceResolver implements NamespaceResolver { public String getURI(String prefix) throws NamespaceException { if (Name.NS_EMPTY_PREFIX.equals(prefix)) { return Name.NS_DEFAULT_URI; } else if ("a".equals(prefix)) { return "http://jackrabbit.apache.org"; } else { throw new NamespaceException("Unknown namespace prefix " + prefix); } } public String getPrefix(String uri) throws NamespaceException { if (Name.NS_DEFAULT_URI.equals(uri)) { return Name.NS_EMPTY_PREFIX; } else if ("http://jackrabbit.apache.org".equals(uri)) { return "a"; } else { throw new NamespaceException("Unknown namespace prefix " + uri); } } } }