Index: src/test/java/org/apache/jackrabbit/name/PathFactoryTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/name/PathFactoryTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/name/PathFactoryTest.java	(revision 0)
@@ -0,0 +1,455 @@
+/*
+ * 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.name;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.conversion.NameResolver;
+import org.apache.jackrabbit.conversion.PathResolver;
+import org.apache.jackrabbit.conversion.NamePathResolver;
+import org.apache.jackrabbit.conversion.ParsingNameResolver;
+import org.apache.jackrabbit.conversion.ParsingPathResolver;
+import org.apache.jackrabbit.conversion.NameException;
+import org.apache.jackrabbit.namespace.NamespaceResolver;
+import org.apache.jackrabbit.namespace.AbstractNamespaceResolver;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.Name;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.NamespaceException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This Class implements a test case for the 'Path' class.
+ *
+ * Actually, this should be below the {@link org.apache.jackrabbit.test} package,
+ * but it needs package protected methods of that class.
+ */
+public class PathFactoryTest extends TestCase {
+
+    private final PathFactory factory;
+    private final NamePathResolver resolver;
+
+    private Test[] tests;
+
+    private static final int ABS = 1;
+    private static final int NOR = 2;
+    private static final int VAL = 4;
+
+    private static int NUM_TESTS = 1;
+
+    public PathFactoryTest() {
+
+        factory = PathFactoryImpl.getInstance();
+
+        // create dummy namespace resolver
+        NamespaceResolver nsResolver = new AbstractNamespaceResolver(){
+            public String getURI(String prefix) {
+                return prefix;
+            }
+
+            public String getPrefix(String uri) {
+                return uri;
+            }
+        };
+
+        final NameResolver nR = new ParsingNameResolver(NameFactoryImpl.getInstance(), nsResolver);
+        final PathResolver pR = new ParsingPathResolver(factory, nR);
+        resolver = new NamePathResolver() {
+
+            public Name getQName(String name) throws NameException, NamespaceException {
+                return nR.getQName(name);
+            }
+
+            public String getJCRName(Name name) throws NamespaceException {
+                return nR.getJCRName(name);
+            }
+
+            public Path getQPath(String path) throws NameException, NamespaceException {
+                return pR.getQPath(path);
+            }
+
+            public String getJCRPath(Path path) throws NamespaceException {
+                return pR.getJCRPath(path);
+            }
+        };
+
+        // create tests
+        ArrayList list = new ArrayList();
+        // absolute paths
+        list.add(new Test("/", NOR|VAL));
+        list.add(new Test("/", NOR|VAL));
+        list.add(new Test("/", NOR|VAL));
+        list.add(new Test("/a/b/c", NOR|VAL));
+        list.add(new Test("/prefix:name/prefix:name", NOR|VAL));
+        list.add(new Test("/name[2]/name[2]", NOR|VAL));
+        list.add(new Test("/prefix:name[2]/prefix:name[2]", NOR|VAL));
+        list.add(new Test("a/b/c/", "a/b/c", NOR|VAL));
+        list.add(new Test("/a/b/c/", "/a/b/c", NOR|VAL));
+
+        // relative paths
+        list.add(new Test("a/b/c", NOR|VAL));
+        list.add(new Test("prefix:name/prefix:name", NOR|VAL));
+        list.add(new Test("name[2]/name[2]", NOR|VAL));
+        list.add(new Test("prefix:name[2]/prefix:name[2]", NOR|VAL));
+
+        // invalid paths
+        list.add(new Test(""));
+        list.add(new Test(" /a/b/c/"));
+        list.add(new Test("/a/b/c/ "));
+        list.add(new Test("/:name/prefix:name"));
+        list.add(new Test("/prefix:name "));
+        list.add(new Test("/prefix: name"));
+        list.add(new Test("/ prefix:name"));
+        list.add(new Test("/prefix : name"));
+        list.add(new Test("/name[0]/name[2]"));
+        list.add(new Test("/prefix:name[2]foo/prefix:name[2]"));
+        list.add(new Test(":name/prefix:name"));
+        list.add(new Test("name[0]/name[2]"));
+        list.add(new Test("prefix:name[2]foo/prefix:name[2]"));
+
+        // not normalized paths
+        list.add(new Test("/a/../b", "/b", VAL));
+        list.add(new Test("./../.", "..", VAL));
+        list.add(new Test("/a/./b", "/a/b", VAL));
+        list.add(new Test("/a/b/../..", "/", VAL));
+        list.add(new Test("/a/b/c/../d/..././f", "/a/b/d/.../f", VAL));
+        list.add(new Test("../a/b/../../../../f", "../../../f", VAL));
+        list.add(new Test("a/../..", "..", VAL));
+        list.add(new Test("../../a/.", "../../a", VAL));
+
+        // invalid normalized paths
+        list.add(new Test("/..", "/..", 0));
+        list.add(new Test("/a/b/../../..", "/a/b/../../..", 0));
+
+        tests = (Test[]) list.toArray(new Test[list.size()]);
+    }
+
+    public void testCreate() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            Test t = tests[i];
+            long t1 = System.currentTimeMillis();
+            for (int j=0; j<NUM_TESTS; j++) {
+                try {
+                    if (t.normalizedPath==null) {
+                        // check just creation
+                        Path p = resolver.getQPath(t.path);
+                        if (!t.isValid()) {
+                            fail("Should throw MalformedPathException: " + t.path);
+                        }
+                        assertEquals("\"" + t.path + "\".create(false)", t.path,  resolver.getJCRPath(p));
+                        assertEquals("\"" + t.path + "\".isNormalized()", t.isNormalized(), p.isNormalized());
+                        assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
+                    } else {
+                        // check with normalization
+                        Path p = resolver.getQPath(t.path).getNormalizedPath();
+                        if (!t.isValid()) {
+                            fail("Should throw MalformedPathException: " + t.path);
+                        }
+                        assertEquals("\"" + t.path + "\".create(true)", t.normalizedPath, resolver.getJCRPath(p));
+                        assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
+                    }
+                } catch (RepositoryException e) {
+                    if (t.isValid()) {
+                        throw e;
+                    }
+                }
+            }
+            long t2 = System.currentTimeMillis();
+            if (NUM_TESTS>1) {
+                System.out.println("testCreate():\t" + t + "\t" + (t2-t1) + "\tms");
+            }
+        }
+    }
+
+    public void testBuilder() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            Test t = tests[i];
+            if (t.isValid()) {
+                if (t.normalizedPath==null) {
+                    // check just creation
+                    Path p = build(t.path, resolver, false);
+                    assertEquals("\"" + t.path + "\".create(false)", t.path,  resolver.getJCRPath(p));
+                    assertEquals("\"" + t.path + "\".isNormalized()", t.isNormalized(), p.isNormalized());
+                    assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
+                } else {
+                    // check with normalization
+                    Path p = build(t.path, resolver, true);
+                    assertEquals("\"" + t.path + "\".create(true)", t.normalizedPath, resolver.getJCRPath(p));
+                    assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
+                }
+            }
+        }
+    }
+
+    public void testBuilderReverse() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            Test t = tests[i];
+            if (t.isValid()) {
+                if (t.normalizedPath==null) {
+                    // check just creation
+                    Path p = buildReverse(t.path, resolver, false);
+                    assertEquals("\"" + t.path + "\".create(false)", t.path,  resolver.getJCRPath(p));
+                    assertEquals("\"" + t.path + "\".isNormalized()", t.isNormalized(), p.isNormalized());
+                    assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
+                } else {
+                    // check with normalization
+                    Path p = buildReverse(t.path, resolver, true);
+                    assertEquals("\"" + t.path + "\".create(true)", t.normalizedPath, resolver.getJCRPath(p));
+                    assertEquals("\"" + t.path + "\".isAbsolute()", t.isAbsolute(), p.isAbsolute());
+                }
+            }
+        }
+    }
+
+    private Path build(String path, NamePathResolver resolver, boolean normalize)
+            throws Exception {
+        PathBuilder builder = new PathBuilder();
+        String[] elems = Text.explode(path, '/', false);
+        if (path.startsWith("/")) {
+            builder.addRoot();
+        }
+        for (int i=0; i<elems.length; i++) {
+            int pos = elems[i].indexOf('[');
+            String elem;
+            Name name;
+            int index;
+            if (pos<0) {
+                elem = elems[i];
+                index = -1;
+            } else {
+                index = Integer.parseInt(elems[i].substring(pos+1, elems[i].length()-1));
+                elem = elems[i].substring(0, pos);
+            }
+            if (".".equals(elem)) {
+                name = NameFactoryImpl.getInstance().create("", ".");
+            } else if ("..".equals(elems[i])) {
+                name = NameFactoryImpl.getInstance().create("", "..");
+            } else {
+                name = resolver.getQName(elem);
+            }
+            if (index < 0) {
+                builder.addLast(name);
+            } else {
+                builder.addLast(name, index);
+            }
+        }
+        return normalize ? builder.getPath().getNormalizedPath() : builder.getPath();
+    }
+
+    private Path buildReverse(String path, NamePathResolver resolver, boolean normalize)
+            throws Exception {
+        PathBuilder builder = new PathBuilder();
+        String[] elems = Text.explode(path, '/', false);
+        for (int i=elems.length-1; i>=0; i--) {
+            int pos = elems[i].indexOf('[');
+            String elem;
+            Name name;
+            int index;
+            if (pos<0) {
+                elem = elems[i];
+                index = -1;
+            } else {
+                index = Integer.parseInt(elems[i].substring(pos+1, elems[i].length()-1));
+                elem = elems[i].substring(0, pos);
+            }
+            if (".".equals(elem)) {
+                name = NameFactoryImpl.getInstance().create("", ".");
+            } else if ("..".equals(elems[i])) {
+                name = NameFactoryImpl.getInstance().create("", "..");
+            } else {
+                name = resolver.getQName(elem);
+            }
+            if (index < 0) {
+                builder.addFirst(name);
+            } else {
+                builder.addFirst(name, index);
+            }
+        }
+        if (path.startsWith("/")) {
+            builder.addRoot();
+        }
+        return normalize ? builder.getPath().getNormalizedPath() : builder.getPath();
+    }
+
+   public void testNormalizedPaths() throws Exception {
+        List paths = new ArrayList();
+
+        // normalized paths
+        paths.add(resolver.getQPath("/"));
+        paths.add(resolver.getQPath("/foo"));
+        paths.add(resolver.getQPath("/foo/bar"));
+        paths.add(resolver.getQPath("foo/bar"));
+        paths.add(resolver.getQPath("foo"));
+        paths.add(resolver.getQPath("../../foo/bar"));
+        paths.add(resolver.getQPath(".."));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertTrue("path is not normalized: " + resolver.getJCRPath(path), path.isNormalized());
+        }
+
+        paths.clear();
+
+        // not normalized paths
+        paths.add(resolver.getQPath("/foo/.."));
+        paths.add(resolver.getQPath("/foo/."));
+        paths.add(resolver.getQPath("/foo/../bar"));
+        paths.add(resolver.getQPath("/foo/./bar"));
+        paths.add(resolver.getQPath("./foo"));
+        paths.add(resolver.getQPath("."));
+        paths.add(resolver.getQPath("foo/.."));
+        paths.add(resolver.getQPath("../foo/.."));
+        paths.add(resolver.getQPath("../foo/."));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertFalse("path is normalized: " + resolver.getJCRPath(path), path.isNormalized());
+        }
+    }
+
+    public void testAbsolutePaths() throws Exception {
+        List paths = new ArrayList();
+
+        // absolute paths
+        paths.add(resolver.getQPath("/"));
+        paths.add(resolver.getQPath("/foo"));
+        paths.add(resolver.getQPath("/foo/bar"));
+        paths.add(resolver.getQPath("/foo/../bar"));
+        paths.add(resolver.getQPath("/foo/.."));
+        paths.add(resolver.getQPath("/foo/./bar"));
+        paths.add(resolver.getQPath("/foo/.././bar/./foo"));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertTrue("path is not absolute: " + resolver.getJCRPath(path), path.isAbsolute());
+        }
+
+        paths.clear();
+
+        // not absoulute paths
+        paths.add(resolver.getQPath("foo/.."));
+        paths.add(resolver.getQPath("foo/."));
+        paths.add(resolver.getQPath("foo/../bar"));
+        paths.add(resolver.getQPath("foo/./bar"));
+        paths.add(resolver.getQPath("./foo"));
+        paths.add(resolver.getQPath("."));
+        paths.add(resolver.getQPath("foo/.."));
+        paths.add(resolver.getQPath("../foo/.."));
+        paths.add(resolver.getQPath("../foo/."));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertFalse("path is absolute: " + resolver.getJCRPath(path), path.isAbsolute());
+        }
+    }
+
+    public void testCanonicalPaths() throws Exception {
+        List paths = new ArrayList();
+
+        // canonical paths
+        paths.add(resolver.getQPath("/"));
+        paths.add(resolver.getQPath("/foo"));
+        paths.add(resolver.getQPath("/foo/bar"));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertTrue("path is not canonical: " + resolver.getJCRPath(path), path.isCanonical());
+        }
+
+        paths.clear();
+
+        // not canonical paths
+        paths.add(resolver.getQPath("/foo/.."));
+        paths.add(resolver.getQPath("/foo/."));
+        paths.add(resolver.getQPath("/foo/../bar"));
+        paths.add(resolver.getQPath("/foo/./bar"));
+        paths.add(resolver.getQPath("./foo"));
+        paths.add(resolver.getQPath("."));
+        paths.add(resolver.getQPath("/foo/.."));
+        paths.add(resolver.getQPath("/../foo/.."));
+        paths.add(resolver.getQPath("/../foo/."));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertFalse("path is canonical: " + resolver.getJCRPath(path), path.isCanonical());
+        }
+    }
+    
+    private static class Test {
+
+        private final String path;
+
+        private final String normalizedPath;
+
+        private final int flags;
+
+        /**
+         * creates an invalid path test
+         * @param path
+         */
+        public Test(String path) {
+            this(path, null, 0);
+        }
+
+        /**
+         * @param path
+         * @param flags
+         */
+        public Test(String path, int flags) {
+            this(path, null, flags);
+        }
+
+        public Test(String path, String normalizedPath, int flags) {
+            this.path = path;
+            this.normalizedPath = normalizedPath;
+            this.flags = flags | ((path.length()>0 && path.charAt(0)=='/') ? ABS : 0);
+        }
+
+        public boolean isAbsolute() {
+            return (flags&ABS) > 0;
+        }
+
+        public boolean isNormalized() {
+            return (flags&NOR) > 0;
+        }
+
+        public boolean isValid() {
+            return (flags&VAL) > 0;
+        }
+
+        public String toString() {
+            StringBuffer b = new StringBuffer(path);
+            if (normalizedPath!=null) {
+                b.append(" -> ").append(normalizedPath);
+            }
+            if (isAbsolute()) {
+                b.append(",ABS");
+            }
+            if (isNormalized()) {
+                b.append(",NOR");
+            }
+            if (isValid()) {
+                b.append(",VAL");
+            }
+            return b.toString();
+        }
+    }
+}

Property changes on: src\test\java\org\apache\jackrabbit\name\PathFactoryTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/name/NameFactoryTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/name/NameFactoryTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/name/NameFactoryTest.java	(revision 0)
@@ -0,0 +1,220 @@
+/*
+ * 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.name;
+
+import org.apache.jackrabbit.conversion.NameResolver;
+import org.apache.jackrabbit.conversion.ParsingNameResolver;
+import org.apache.jackrabbit.namespace.NamespaceResolver;
+import org.apache.jackrabbit.namespace.AbstractNamespaceResolver;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.Name;
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+/**
+ * This Class implements a test case for the 'Path' class.
+ *
+ * Actually, this should be below the {@link org.apache.jackrabbit.test} package,
+ * but it needs package protected methods of that class.
+ */
+public class NameFactoryTest extends TestCase {
+
+    private final NameFactory factory;
+    private final NameResolver resolver;
+
+    private Test[] tests;
+
+    private static int NUM_TESTS = 1;
+
+    public NameFactoryTest() {
+
+        // create dummy namespace resolver
+        factory = NameFactoryImpl.getInstance();
+        NamespaceResolver nsResolver = new AbstractNamespaceResolver(){
+            public String getURI(String prefix) {
+                return prefix;
+            }
+
+            public String getPrefix(String uri) {
+                return uri;
+            }
+        };
+        resolver = new ParsingNameResolver(factory, nsResolver);
+
+        // create tests
+        ArrayList list = new ArrayList();
+
+        // valid names
+        list.add(new Test("name", "", "name"));
+        list.add(new Test("prefix:name", "prefix", "name"));
+        list.add(new Test("prefix:na me", "prefix", "na me"));
+
+        // invalid names
+        list.add(new Test(":name"));
+        list.add(new Test("."));
+        list.add(new Test(".."));
+        list.add(new Test("pre:"));
+        list.add(new Test(""));
+        list.add(new Test(" name"));
+        list.add(new Test(" prefix: name"));
+        list.add(new Test("prefix: name"));
+        list.add(new Test("prefix:name "));
+        list.add(new Test("pre fix:name"));
+        list.add(new Test("prefix :name"));
+        list.add(new Test("name/name"));
+        list.add(new Test("name[name"));
+        list.add(new Test("name]name"));
+        list.add(new Test("name*name"));
+        list.add(new Test("prefix:name:name"));
+
+        tests = (Test[]) list.toArray(new Test[list.size()]);
+    }
+
+    public void testCreate() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            Test t = tests[i];
+            long t1 = System.currentTimeMillis();
+            for (int j=0; j<NUM_TESTS; j++) {
+                try {
+                    Name n = resolver.getQName(t.jcrName);
+                    if (!t.isValid()) {
+                        fail("Should throw IllegalNameException: " + t.jcrName);
+                    }
+                    assertEquals("\"" + t.jcrName + "\".uri", t.prefix, n.getNamespaceURI());
+                    assertEquals("\"" + t.jcrName + "\".localName", t.name, n.getLocalName());
+                } catch (org.apache.jackrabbit.conversion.IllegalNameException e) {
+                    if (t.isValid()) {
+                        throw e;
+                    }
+                }
+            }
+            long t2 = System.currentTimeMillis();
+            if (NUM_TESTS>1) {
+                System.out.println("testCreate():\t" + t + "\t" + (t2-t1) + "\tms");
+            }
+        }
+    }
+
+    public void testEquality() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            final Test t = tests[i];
+            if (t.isValid()) {
+                Name n1 = new Name() {
+                    public String getLocalName() {
+                        return t.jcrName;
+                    }
+
+                    public String getNamespaceURI() {
+                        return t.prefix;
+                    }
+
+                    public int compareTo(Object o) {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+
+                Name n2 = factory.create(t.prefix, t.jcrName);
+                assertTrue(n2.equals(n1));
+            }
+        }
+    }
+
+    public void testCreationFromString() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            final Test t = tests[i];
+            if (t.isValid()) {
+                Name n1 = factory.create(t.prefix, t.jcrName);
+                Name n2 = factory.create(n1.toString());
+
+                assertTrue(n1.getLocalName().equals(n2.getLocalName()));
+                assertTrue(n1.getNamespaceURI().equals(n2.getNamespaceURI()));
+                assertTrue(n2.equals(n1));
+            }
+        }
+    }
+
+    public void testCreationFromOtherString() throws Exception {
+        for (int i=0; i<tests.length; i++) {
+            final Test t = tests[i];
+            if (t.isValid()) {
+                Name n1 = new Name() {
+                    public String getLocalName() {
+                        return t.jcrName;
+                    }
+
+                    public String getNamespaceURI() {
+                        return t.prefix;
+                    }
+
+                    public int compareTo(Object o) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String toString() {
+                        return "{" + t.prefix + "}" + t.jcrName;
+                    }
+
+                    public boolean equals(Object obj) {
+                        if (obj instanceof Name) {
+                            Name n = (Name) obj;
+                            return n.getLocalName().equals(t.jcrName) && n.getNamespaceURI().equals(t.prefix);
+                        }
+                        return false;
+                    }
+                };
+
+                Name n2 = factory.create(n1.toString());
+
+                assertTrue(n1.getLocalName().equals(n2.getLocalName()));
+                assertTrue(n1.getNamespaceURI().equals(n2.getNamespaceURI()));
+                assertTrue(n2.equals(n1));
+            }
+        }
+    }
+
+    private static class Test {
+
+        private final String jcrName;
+
+        private final String prefix;
+
+        private final String name;
+
+        public Test(String jcrName) {
+            this(jcrName, null, null);
+        }
+
+        public Test(String jcrName, String prefix, String name) {
+            this.jcrName = jcrName;
+            this.prefix = prefix;
+            this.name = name;
+        }
+
+        public boolean isValid() {
+            return name!=null;
+        }
+
+        public String toString() {
+            StringBuffer b = new StringBuffer(jcrName);
+            if (isValid()) {
+                b.append(",VAL");
+            }
+            return b.toString();
+        }
+    }
+}

Property changes on: src\test\java\org\apache\jackrabbit\name\NameFactoryTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/name/TestAll.java
===================================================================
--- src/test/java/org/apache/jackrabbit/name/TestAll.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/name/TestAll.java	(revision 0)
@@ -0,0 +1,43 @@
+/*
+ * 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.name;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all testcases for the Core module.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("name tests");
+
+        suite.addTestSuite(PathFactoryTest.class);
+        suite.addTestSuite(NameFactoryTest.class);
+
+        return suite;
+    }
+}

Property changes on: src\test\java\org\apache\jackrabbit\name\TestAll.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/conversion/ParsingPathResolverTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/conversion/ParsingPathResolverTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/conversion/ParsingPathResolverTest.java	(revision 0)
@@ -0,0 +1,136 @@
+/*
+ * 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.conversion;
+
+import junit.framework.TestCase;
+
+import javax.jcr.NamespaceException;
+
+import org.apache.jackrabbit.name.PathFactoryImpl;
+import org.apache.jackrabbit.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.Path;
+
+/**
+ * Test cases for the {@link ParsingPathResolver} class.
+ */
+public class ParsingPathResolverTest extends TestCase {
+
+    /**
+     * Path resolver being tested.
+     */
+    private PathResolver resolver = new ParsingPathResolver(PathFactoryImpl.getInstance(),
+            new ParsingNameResolver(NameFactoryImpl.getInstance(), new DummyNamespaceResolver()));
+
+    /**
+     * Checks that the given path resolves properly.
+     *
+     * @param path JCR path
+     */
+    private void assertValidPath(String path) {
+        try {
+            Path qpath = resolver.getQPath(path);
+            assertEquals(path, path, resolver.getJCRPath(qpath));
+        } catch (NameException e) {
+            fail(path);
+        } catch (NamespaceException e) {
+            fail(path);
+        }
+    }
+
+    /**
+     * Checks that the given path fails to resolve.
+     *
+     * @param path JCR path
+     */
+    private void assertInvalidPath(String path) {
+        try {
+            resolver.getQPath(path);
+            fail(path);
+        } catch (NameException e) {
+        } catch (NamespaceException e) {
+        }
+    }
+
+    /**
+     * Tests that valid paths are properly resolved.
+     */
+    public void testValidPaths() {
+        assertValidPath("/");
+        assertValidPath(".");
+        assertValidPath("..");
+        assertValidPath("x");
+        assertValidPath("x:y");
+        assertValidPath("x[2]");
+        assertValidPath("x:y[123]");
+
+        assertValidPath("/a/b/c");
+        assertValidPath("/prefix:name/prefix:name");
+        assertValidPath("/name[2]/name[2]");
+        assertValidPath("/prefix:name[2]/prefix:name[2]");
+
+        assertValidPath("a/b/c");
+        assertValidPath("prefix:name/prefix:name");
+        assertValidPath("name[2]/name[2]");
+        assertValidPath("prefix:name[2]/prefix:name[2]");
+
+        assertValidPath("/a/../b");
+        assertValidPath("./../.");
+        assertValidPath("/a/./b");
+        assertValidPath("/a/b/../..");
+        assertValidPath("/a/b/c/../d/..././f");
+        assertValidPath("../a/b/../../../../f");
+        assertValidPath("a/../..");
+        assertValidPath("../../a/.");
+
+        // TODO: Should these paths be detected as invalid by the parser?
+        assertValidPath("/..");
+        assertValidPath("/a/b/../../..");
+    }
+
+    /**
+     * Tests that resolution of invalid paths fails.
+     */
+    public void testInvalidPaths() {
+        assertInvalidPath("");
+        assertInvalidPath("//");
+        //assertInvalidPath("x/");  // TODO: was valid with jcr-commons-path but not with pathresolver? check again
+        assertInvalidPath("x:");
+        assertInvalidPath("x:/");
+        assertInvalidPath("x[]");
+        //assertInvalidPath("x:y/"); // TODO: was valid with jcr-commons-path but not with pathresolver? check again
+        //assertInvalidPath("x:y[1]/"); // TODO: was valid with jcr-commons-path but not with pathresolver? check again
+        assertInvalidPath("x:y[");
+        assertInvalidPath("x:y[]");
+        assertInvalidPath("x:y[1");
+        assertInvalidPath("x:y[1]2");
+        assertInvalidPath("x:y[1]]");
+        assertInvalidPath("x:y[[1]");
+        assertInvalidPath(" /a/b/c/");
+        assertInvalidPath("/a/b/c/ ");
+        assertInvalidPath("/:name/prefix:name");
+        assertInvalidPath("/prefix:name ");
+        assertInvalidPath("/prefix: name");
+        assertInvalidPath("/ prefix:name");
+        assertInvalidPath("/prefix : name");
+        assertInvalidPath("/name[0]/name[2]");
+        assertInvalidPath("/prefix:name[2]foo/prefix:name[2]");
+        assertInvalidPath(":name/prefix:name");
+        assertInvalidPath("name[0]/name[2]");
+        assertInvalidPath("prefix:name[2]foo/prefix:name[2]");
+    }
+
+}

Property changes on: src\test\java\org\apache\jackrabbit\conversion\ParsingPathResolverTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/conversion/ParsingNameResolverTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/conversion/ParsingNameResolverTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/conversion/ParsingNameResolverTest.java	(revision 0)
@@ -0,0 +1,107 @@
+/*
+ * 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.conversion;
+
+import javax.jcr.NamespaceException;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.name.NameFactoryImpl;
+
+/**
+ * Test cases for the {@link org.apache.jackrabbit.conversion.ParsingNameResolver} class.
+ */
+public class ParsingNameResolverTest extends TestCase {
+
+    /**
+     * Name resolver being tested.
+     */
+    private NameResolver resolver =
+        new ParsingNameResolver(NameFactoryImpl.getInstance(), new DummyNamespaceResolver());
+
+    /**
+     * Checks that the given name resolves to the given namespace URI and
+     * local part.
+     *
+     * @param name JCR name
+     * @param uri namespace URI
+     * @param local local part
+     */
+    private void assertValidName(String name, String uri, String local) {
+        try {
+            Name qname = resolver.getQName(name);
+            assertEquals(name, uri, qname.getNamespaceURI());
+            assertEquals(name, local, qname.getLocalName());
+            assertEquals(name, name, resolver.getJCRName(qname));
+        } catch (NameException e) {
+            fail(name);
+        } catch (NamespaceException e) {
+            fail(name);
+        }
+    }
+
+    /**
+     * Tests that valid names are properly resolved.
+     */
+    public void testValidNames() {
+        assertValidName("x", "", "x");
+        assertValidName("name", "", "name");
+        assertValidName("space name", "", "space name");
+        assertValidName("x:y", "x", "y");
+        assertValidName("prefix:name", "prefix", "name");
+        assertValidName("prefix:space name", "prefix", "space name");
+    }
+
+    /**
+     * Checks that the given name fails to resolve.
+     *
+     * @param name JCR name
+     */
+    private void assertInvalidName(String name) {
+        try {
+            resolver.getQName(name);
+            fail(name);
+        } catch (NameException e) {
+        } catch (NamespaceException e) {
+        }
+    }
+
+    /**
+     * Tests that resolution of invalid names fails.
+     */
+    public void testInvalidNames() {
+        assertInvalidName("");
+        assertInvalidName(":name");
+        assertInvalidName(".");
+        assertInvalidName("..");
+        assertInvalidName("pre:");
+        assertInvalidName(" name");
+        assertInvalidName(" prefix: name");
+        assertInvalidName("prefix: name");
+        assertInvalidName("prefix:name ");
+        assertInvalidName("pre fix:name");
+        assertInvalidName("prefix :name");
+        assertInvalidName("name/name");
+        assertInvalidName("name[]");
+        assertInvalidName("name[1]");
+        assertInvalidName("name[name");
+        assertInvalidName("name]name");
+        assertInvalidName("name*name");
+        assertInvalidName("prefix:name:name");
+    }
+
+}

Property changes on: src\test\java\org\apache\jackrabbit\conversion\ParsingNameResolverTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/conversion/TestAll.java
===================================================================
--- src/test/java/org/apache/jackrabbit/conversion/TestAll.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/conversion/TestAll.java	(revision 0)
@@ -0,0 +1,43 @@
+/*
+ * 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.conversion;
+
+import junit.framework.TestCase;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all testcases for the Core module.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("conversion tests");
+
+        suite.addTestSuite(ParsingNameResolverTest.class);
+        suite.addTestSuite(ParsingPathResolverTest.class);
+
+        return suite;
+    }
+}

Property changes on: src\test\java\org\apache\jackrabbit\conversion\TestAll.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/conversion/DummyNamespaceResolver.java
===================================================================
--- src/test/java/org/apache/jackrabbit/conversion/DummyNamespaceResolver.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/conversion/DummyNamespaceResolver.java	(revision 0)
@@ -0,0 +1,56 @@
+/*
+ * 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.conversion;
+
+import javax.jcr.NamespaceException;
+
+import org.apache.jackrabbit.util.XMLChar;
+import org.apache.jackrabbit.namespace.NamespaceResolver;
+
+/**
+ * Dummy namespace resolver for use in unit testing. This namespace resolver
+ * maps each valid XML prefix string to the same string as the namespace URI
+ * and vice versa.
+ */
+class DummyNamespaceResolver implements NamespaceResolver {
+
+    /**
+     * Returns the given prefix as the corresponding namespace URI after
+     * validating the correct format of the prefix.
+     *
+     * @param prefix namespace prefix
+     * @return the given prefix
+     * @throws NamespaceException if the given prefix is not a valid XML prefix
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        if (XMLChar.isValidNCName(prefix)) {
+            return prefix;
+        } else {
+            throw new NamespaceException("Invalid prefix: " + prefix);
+        }
+    }
+
+    /**
+     * Returns the given namespace URI as the corresponding prefix.
+     *
+     * @param uri namespace URI
+     * @return the given URI
+     */
+    public String getPrefix(String uri) {
+        return uri;
+    }
+}

Property changes on: src\test\java\org\apache\jackrabbit\conversion\DummyNamespaceResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/value/QValueTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/value/QValueTest.java	(revision 582823)
+++ src/test/java/org/apache/jackrabbit/value/QValueTest.java	(working copy)
@@ -36,10 +36,12 @@
 
 import org.apache.jackrabbit.util.ISO8601;
 import org.apache.jackrabbit.uuid.UUID;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.name.NameConstants;
+import org.apache.jackrabbit.name.PathFactoryImpl;
 
 /**
  * <code>QValueTest</code>...
@@ -48,6 +50,7 @@
 
     private final Calendar CALENDAR = Calendar.getInstance();
     private static final String REFERENCE = UUID.randomUUID().toString();
+    private static final Path ROOT_PATH = PathFactoryImpl.getInstance().getRootPath();
 
     private static final QValueFactory factory = QValueFactoryImpl.getInstance();
 
@@ -60,7 +63,7 @@
             // ok
         }
         try {
-            factory.create((String) null, PropertyType.DATE);
+            factory.create(null, PropertyType.DATE);
             fail();
         } catch (IllegalArgumentException e) {
             // ok
@@ -92,7 +95,7 @@
     //----------------------------------------------------------< REFERENCE >---
     public void testNullReferenceValue() throws IOException {
         try {
-            factory.create((String) null, PropertyType.REFERENCE);
+            factory.create(null, PropertyType.REFERENCE);
             fail();
         } catch (IllegalArgumentException e) {
             // ok
@@ -120,7 +123,7 @@
     //--------------------------------------------------------------< QName >---
     public void testNullQNameValue() throws IOException {
         try {
-            factory.create((QName) null);
+            factory.create((Name) null);
             fail();
         } catch (IllegalArgumentException e) {
             // ok
@@ -128,21 +131,21 @@
     }
 
     public void testQNameValueType() throws IOException {
-        QValue v = factory.create(QName.JCR_DATA);
+        QValue v = factory.create(NameConstants.JCR_DATA);
         assertTrue(v.getType() == PropertyType.NAME);
-        v = factory.create(QName.JCR_DATA.toString(), PropertyType.NAME);
+        v = factory.create(NameConstants.JCR_DATA.toString(), PropertyType.NAME);
         assertTrue(v.getType() == PropertyType.NAME);
     }
 
     public void testQNameValueEquality() throws IOException {
-        QValue v = factory.create(QName.JCR_DATA);
-        QValue v2 = factory.create(QName.JCR_DATA.toString(), PropertyType.NAME);
+        QValue v = factory.create(NameConstants.JCR_DATA);
+        QValue v2 = factory.create(NameConstants.JCR_DATA.toString(), PropertyType.NAME);
         assertTrue(v.equals(v2));
     }
 
     public void testQNameValueGetString() throws IOException, RepositoryException {
-        QValue v = factory.create(QName.JCR_DATA);
-        assertTrue(v.getString().equals(QName.JCR_DATA.toString()));
+        QValue v = factory.create(NameConstants.JCR_DATA);
+        assertTrue(v.getString().equals(NameConstants.JCR_DATA.toString()));
     }
 
     //--------------------------------------------------------------< QPath >---
@@ -156,22 +159,22 @@
     }
 
     public void testPathValueType() throws IOException {
-        QValue v = factory.create(Path.ROOT);
+        QValue v = factory.create(ROOT_PATH);
         assertTrue(v.getType() == PropertyType.PATH);
-        v = factory.create(Path.ROOT.toString(), PropertyType.PATH);
+        v = factory.create(ROOT_PATH.toString(), PropertyType.PATH);
         assertTrue(v.getType() == PropertyType.PATH);
     }
 
 
     public void testPathValueEquality() throws IOException {
-        QValue v = factory.create(Path.ROOT);
-        QValue v2 = factory.create(Path.ROOT.toString(), PropertyType.PATH);
+        QValue v = factory.create(ROOT_PATH);
+        QValue v2 = factory.create(ROOT_PATH.toString(), PropertyType.PATH);
         assertTrue(v.equals(v2));
     }
 
     public void testPathValueGetString() throws IOException, RepositoryException {
-        QValue v = factory.create(Path.ROOT);
-        assertTrue(v.getString().equals(Path.ROOT.toString()));
+        QValue v = factory.create(ROOT_PATH);
+        assertTrue(v.getString().equals(ROOT_PATH.toString()));
     }
 
     //-------------------------------------------------------------< BINARY >---
Index: src/main/java/org/apache/jackrabbit/name/PathBuilder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/name/PathBuilder.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/name/PathBuilder.java	(revision 0)
@@ -0,0 +1,196 @@
+/*
+ * 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.name;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.conversion.MalformedPathException;
+
+import java.util.LinkedList;
+
+/**
+ * Helper class used to build a path from pre-parsed path elements.
+ * <p/>
+ * Note that this class does neither validate the format of the path elements nor
+ * does it validate the format of the entire path.
+ * This class should therefore only be used in situations, where the elements
+ * and the resulting path are known to be valid. The regular way of creating
+ * a <code>Path</code> object is by calling any of the
+ * <code>PathFactory.create()</code>methods.
+ */
+public final class PathBuilder {
+
+    /**
+     * The path factory
+     */
+    private final PathFactory factory;
+
+    /**
+     * the list of path elements of the constructed path
+     */
+    private final LinkedList queue;
+
+    /**
+     * flag indicating if the current path is normalized
+     */
+    boolean isNormalized = true;
+
+    /**
+     * flag indicating if the current path has leading parent '..' elements
+     */
+    boolean leadingParent = true;
+
+    /**
+     * Creates a new PathBuilder to create a Path using the
+     * {@link PathFactoryImpl default PathFactory}. See
+     * {@link PathBuilder#PathBuilder(PathFactory)} for a constructor explicitely
+     * specifying the factory to use.
+     */
+    public PathBuilder() {
+        this(PathFactoryImpl.getInstance());
+    }
+
+    /**
+     * Creates a new PathBuilder.
+     *
+     * @param factory The PathFactory used to create the elements and the final path.
+     */
+    public PathBuilder(PathFactory factory) {
+        this.factory = factory;
+        queue = new LinkedList();
+    }
+
+    /**
+     * Creates a new PathBuilder and initialized it with the given path
+     * elements.
+     *
+     * @param elements
+     */
+    public PathBuilder(Path.Element[] elements) {
+        this();
+        addAll(elements);
+    }
+
+    /**
+     * Creates a new PathBuilder and initialized it with elements of the
+     * given path.
+     *
+     * @param parent
+     */
+    public PathBuilder(Path parent) {
+        this();
+        addAll(parent.getElements());
+    }
+
+    /**
+     * Adds the {@link org.apache.jackrabbit.spi.PathFactory#getRootElement()}.
+     */
+    public void addRoot() {
+        addFirst(factory.getRootElement());
+    }
+
+    /**
+     * Adds the given elemenets
+     *
+     * @param elements
+     */
+    public void addAll(Path.Element[] elements) {
+        for (int i = 0; i < elements.length; i++) {
+            addLast(elements[i]);
+        }
+    }
+
+    /**
+     * Inserts the element at the beginning of the path to be built.
+     *
+     * @param elem
+     */
+    public void addFirst(Path.Element elem) {
+        if (queue.isEmpty()) {
+            isNormalized &= !elem.denotesCurrent();
+            leadingParent = elem.denotesParent();
+        } else {
+            isNormalized &= !elem.denotesCurrent() && (!leadingParent || elem.denotesParent());
+            leadingParent |= elem.denotesParent();
+        }
+        queue.addFirst(elem);
+    }
+
+    /**
+     * Inserts the element at the beginning of the path to be built.
+     *
+     * @param name
+     */
+    public void addFirst(Name name) {
+        addFirst(factory.createElement(name));
+    }
+
+    /**
+     * Inserts the element at the beginning of the path to be built.
+     *
+     * @param name
+     * @param index
+     */
+    public void addFirst(Name name, int index) {
+        addFirst(factory.createElement(name, index));
+    }
+
+    /**
+     * Inserts the element at the end of the path to be built.
+     *
+     * @param elem
+     */
+    public void addLast(Path.Element elem) {
+        queue.addLast(elem);
+        leadingParent &= elem.denotesParent();
+        isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
+    }
+
+    /**
+     * Inserts the element at the end of the path to be built.
+     *
+     * @param name
+     */
+    public void addLast(Name name) {
+        addLast(factory.createElement(name));
+    }
+
+    /**
+     * Inserts the element at the end of the path to be built.
+     *
+     * @param name
+     * @param index
+     */
+    public void addLast(Name name, int index) {
+        addLast(factory.createElement(name, index));
+    }
+
+    /**
+     * Assembles the built path and returns a new {@link Path}.
+     *
+     * @return a new {@link Path}
+     * @throws MalformedPathException if the internal path element queue is empty.
+     */
+    public Path getPath() throws MalformedPathException {
+        if (queue.size() == 0) {
+            throw new MalformedPathException("empty path");
+        }
+        Path.Element[] elements = (Path.Element[]) queue.toArray(new Path.Element[queue.size()]);
+        return factory.create(elements);
+    }
+}
\ No newline at end of file

Property changes on: src\main\java\org\apache\jackrabbit\name\PathBuilder.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java	(revision 0)
@@ -0,0 +1,878 @@
+/*
+ * 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.name;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.NameFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PathNotFoundException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * <code>PathFactoryImpl</code>...
+ */
+public class PathFactoryImpl implements PathFactory {
+
+    private static PathFactory FACTORY = new PathFactoryImpl();
+
+    private static final String CURRENT_LITERAL = ".";
+    private static final String PARENT_LITERAL = "..";
+
+    private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+    private final static Name CURRENT_NAME = NAME_FACTORY.create(Name.NS_DEFAULT_URI, CURRENT_LITERAL);
+    private final static Name PARENT_NAME = NAME_FACTORY.create(Name.NS_DEFAULT_URI, PARENT_LITERAL);
+    private final static Name ROOT_NAME = NAME_FACTORY.create(Name.NS_DEFAULT_URI, "");
+
+    private static final Path.Element CURRENT_ELEMENT = new SpecialElement(CURRENT_NAME);
+    private static final Path.Element PARENT_ELEMENT = new SpecialElement(PARENT_NAME);
+    private static final Path.Element ROOT_ELEMENT = new SpecialElement(ROOT_NAME);
+
+    /**
+     * the root path
+     */
+    private static final Path ROOT = new PathImpl(new Path.Element[]{ROOT_ELEMENT}, true);
+
+    private PathFactoryImpl() {}
+
+    public static PathFactory getInstance() {
+        return FACTORY;
+    }
+
+    //--------------------------------------------------------< PathFactory >---
+    /**
+     * @see PathFactory#create(Path, Path, boolean)
+     */
+    public Path create(Path parent, Path relPath, boolean normalize) throws IllegalArgumentException, RepositoryException {
+        if (relPath.isAbsolute()) {
+            throw new IllegalArgumentException("relPath is not a relative path");
+        }
+        List l = new ArrayList();
+        l.addAll(Arrays.asList(parent.getElements()));
+        l.addAll(Arrays.asList(relPath.getElements()));
+
+        Builder pb = new Builder(l);
+        Path path = pb.getPath();
+        if (normalize) {
+            return path.getNormalizedPath();
+        } else {
+            return path;
+        }
+    }
+
+    /**
+     * @see PathFactory#create(Path, Name, boolean)
+     */
+    public Path create(Path parent, Name name, boolean normalize) throws RepositoryException {
+        List elements = new ArrayList();
+        elements.addAll(Arrays.asList(parent.getElements()));
+        elements.add(createElement(name));
+
+        Builder pb = new Builder(elements);
+        Path path = pb.getPath();
+        if (normalize) {
+            return path.getNormalizedPath();
+        } else {
+            return path;
+        }
+    }
+
+    /**
+     * @see PathFactory#create(Path, Name, int, boolean)
+     */
+    public Path create(Path parent, Name name, int index, boolean normalize) throws IllegalArgumentException, RepositoryException {
+        List elements = new ArrayList();
+        elements.addAll(Arrays.asList(parent.getElements()));
+        elements.add(createElement(name, index));
+
+        Builder pb = new Builder(elements);
+        Path path = pb.getPath();
+        if (normalize) {
+            return path.getNormalizedPath();
+        } else {
+            return path;
+        }
+    }
+
+    /**
+     * @see PathFactory#create(Name)
+     */
+    public Path create(Name name) throws IllegalArgumentException {
+        Path.Element elem = createElement(name);
+        return new Builder(new Path.Element[]{elem}).getPath();
+    }
+
+    /**
+     * @see PathFactory#create(Name, int)
+     */
+    public Path create(Name name, int index) throws IllegalArgumentException {
+        if (index < Path.INDEX_UNDEFINED) {
+            throw new IllegalArgumentException("Index must not be negative: " + index);
+        }
+        Path.Element elem = createElement(name, index);
+        return new Builder(new Path.Element[]{elem}).getPath();
+    }
+
+    /**
+     * @see PathFactory#create(Path.Element[])
+     */
+    public Path create(Path.Element[] elements) throws IllegalArgumentException {
+        return new Builder(elements).getPath();
+    }
+
+    /**
+     * @see PathFactory#create(String)
+     */
+    public Path create(String pathString) throws IllegalArgumentException {
+        if (pathString == null || "".equals(pathString)) {
+            throw new IllegalArgumentException("Invalid Path literal");
+        }
+        // split into path elements
+        int lastPos = 0;
+        int pos = pathString.indexOf(Path.DELIMITER);
+        ArrayList list = new ArrayList();
+        while (lastPos >= 0) {
+            Path.Element elem;
+            if (pos >= 0) {
+                elem = createElement(pathString.substring(lastPos, pos));
+                lastPos = pos + 1;
+                pos = pathString.indexOf(Path.DELIMITER, lastPos);
+            } else {
+                elem = createElement(pathString.substring(lastPos));
+                lastPos = -1;
+            }
+            list.add(elem);
+        }
+        return new Builder(list).getPath();
+    }
+
+    private Path.Element createElement(String elementString) {
+        if (elementString == null) {
+            throw new IllegalArgumentException("null PathElement literal");
+        }
+        if (elementString.equals(ROOT_NAME.toString())) {
+            return ROOT_ELEMENT;
+        } else if (elementString.equals(CURRENT_LITERAL)) {
+            return CURRENT_ELEMENT;
+        } else if (elementString.equals(PARENT_LITERAL)) {
+            return PARENT_ELEMENT;
+        }
+
+        int pos = elementString.indexOf('[');
+        if (pos == -1) {
+            Name name = NAME_FACTORY.create(elementString);
+            return new Element(name, Path.INDEX_UNDEFINED);
+        }
+        Name name = NAME_FACTORY.create(elementString.substring(0, pos));
+        int pos1 = elementString.indexOf(']');
+        if (pos1 == -1) {
+            throw new IllegalArgumentException("invalid PathElement literal: " + elementString + " (missing ']')");
+        }
+        try {
+            int index = Integer.valueOf(elementString.substring(pos + 1, pos1)).intValue();
+            if (index < 1) {
+                throw new IllegalArgumentException("invalid PathElement literal: " + elementString + " (index is 1-based)");
+            }
+            return new Element(name, index);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("invalid PathElement literal: " + elementString + " (" + e.getMessage() + ")");
+        }
+    }
+
+    /**
+     * @see PathFactory#createElement(Name)
+     */
+    public Path.Element createElement(Name name) throws IllegalArgumentException {
+        if (name == null) {
+            throw new IllegalArgumentException("name must not be null");
+        } else if (name.equals(PARENT_NAME)) {
+            return PARENT_ELEMENT;
+        } else if (name.equals(CURRENT_NAME)) {
+            return CURRENT_ELEMENT;
+        } else if (name.equals(ROOT_NAME)) {
+            return ROOT_ELEMENT;
+        } else {
+            return new Element(name, Path.INDEX_UNDEFINED);
+        }
+    }
+
+    /**
+     * @see PathFactory#createElement(Name, int)
+     */
+    public Path.Element createElement(Name name, int index) throws IllegalArgumentException {
+        if (index < Path.INDEX_UNDEFINED) {
+            throw new IllegalArgumentException("The index may not be negative.");
+        } else if (name == null) {
+            throw new IllegalArgumentException("The name must not be null");
+        } else if (name.equals(PARENT_NAME)
+                || name.equals(CURRENT_NAME)
+                || name.equals(ROOT_NAME)) {
+            throw new IllegalArgumentException(
+                    "Special path elements (root, '.' and '..') can not have an explicit index.");
+        } else {
+            return new Element(name, index);
+        }
+    }
+
+    /**
+     * @see PathFactory#getCurrentElement()
+     */
+    public Path.Element getCurrentElement() {
+        return CURRENT_ELEMENT;
+    }
+
+    /**
+     * @see PathFactory#getParentElement()
+     */
+    public Path.Element getParentElement() {
+        return PARENT_ELEMENT;
+    }
+
+    /**
+     * @see PathFactory#getRootElement()
+     */
+    public Path.Element getRootElement() {
+        return ROOT_ELEMENT;
+    }
+
+    /**
+     * @see PathFactory#getRootPath()
+     */
+    public Path getRootPath() {
+        return ROOT;
+    }
+
+    //--------------------------------------------------------------------------
+    private static final class PathImpl implements Path {
+
+        /**
+         * the elements of this path
+         */
+        private final Path.Element[] elements;
+
+        /**
+         * flag indicating if this path is normalized
+         */
+        private final boolean normalized;
+
+        /**
+         * flag indicating if this path is absolute
+         */
+        private final boolean absolute;
+
+        /**
+         * the cached hashcode of this path
+         */
+        private transient int hash = 0;
+
+        /**
+         * the cached 'toString' of this path
+         */
+        private transient String string;
+
+        private PathImpl(Path.Element[] elements, boolean isNormalized) {
+            if (elements == null || elements.length == 0) {
+                throw new IllegalArgumentException("Empty paths are not allowed");
+            }
+            this.elements = elements;
+            this.absolute = elements[0].denotesRoot();
+            this.normalized = isNormalized;
+        }
+
+        /**
+         * @see Path#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return absolute && elements.length == 1;
+        }
+
+        /**
+         * @see Path#isAbsolute()
+         */
+        public boolean isAbsolute() {
+            return absolute;
+        }
+
+        /**
+         * @see Path#isCanonical()
+         */
+        public boolean isCanonical() {
+            return absolute && normalized;
+        }
+
+        /**
+         * @see Path#isNormalized()
+         */
+        public boolean isNormalized() {
+            return normalized;
+        }
+
+        /**
+         * @see Path#getNormalizedPath()
+         */
+        public Path getNormalizedPath() throws RepositoryException {
+            if (isNormalized()) {
+                return this;
+            }
+            LinkedList queue = new LinkedList();
+            Path.Element last = PARENT_ELEMENT;
+            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;
+                    } else {
+                        last = (Path.Element) queue.getLast();
+                    }
+                } else if (!elem.denotesCurrent()) {
+                    last = elem;
+                    queue.add(last);
+                }
+            }
+            if (queue.isEmpty()) {
+                throw new RepositoryException("Path can not be normalized: would result in an empty path.");
+            }
+            boolean isNormalized = true;
+            return new PathImpl((Path.Element[]) queue.toArray(new Element[queue.size()]), isNormalized);
+        }
+
+        /**
+         * @see Path#getCanonicalPath()
+         */
+        public Path getCanonicalPath() throws RepositoryException {
+            if (isCanonical()) {
+                return this;
+            }
+            if (!isAbsolute()) {
+                throw new RepositoryException("Only an absolute path can be canonicalized.");
+            }
+            return getNormalizedPath();
+        }
+
+        /**
+         * @see Path#computeRelativePath(Path)
+         */
+        public Path computeRelativePath(Path other) throws RepositoryException {
+            if (other == null) {
+                throw new IllegalArgumentException("null argument");
+            }
+
+            // make sure both paths are absolute
+            if (!isAbsolute() || !other.isAbsolute()) {
+                throw new RepositoryException("Cannot compute relative path from relative paths");
+            }
+
+            // make sure we're comparing canonical paths
+            Path p0 = getCanonicalPath();
+            Path p1 = other.getCanonicalPath();
+
+            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();
+            }
+
+            // determine length of common path fragment
+            int lengthCommon = 0;
+            Path.Element[] elems0 = p0.getElements();
+            Path.Element[] elems1 = p1.getElements();
+            for (int i = 0; i < elems0.length && i < elems1.length; i++) {
+                if (!elems0[i].equals(elems1[i])) {
+                    break;
+                }
+                lengthCommon++;
+            }
+            List l = new ArrayList();
+            if (lengthCommon < elems0.length) {
+                /**
+                 * the common path fragment is an ancestor of this path;
+                 * this has to be accounted for by prepending '..' elements
+                 * to the relative path
+                 */
+                int tmp = elems0.length - lengthCommon;
+                while (tmp-- > 0) {
+                    l.add(0, PARENT_ELEMENT);
+                }
+            }
+            // add remainder of other path
+            for (int i = lengthCommon; i < elems1.length; i++) {
+                l.add(elems1[i]);
+            }
+            return new Builder(l).getPath();
+        }
+
+        /**
+         * @see Path#getAncestor(int)
+         */
+        public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException {
+            if (degree < 0) {
+                throw new IllegalArgumentException("degree must be >= 0");
+            } else if (degree == 0) {
+                return this;
+            }
+            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;
+        }
+
+        /**
+         * @see Path#getLength()
+         */
+        public int getLength() {
+            return elements.length;
+        }
+
+        /**
+         * @see Path#getDepth()
+         */
+        public int getDepth() {
+            int depth = ROOT_DEPTH;
+            for (int i = 0; i < elements.length; i++) {
+                if (elements[i].denotesParent()) {
+                    depth--;
+                } else if (!elements[i].denotesCurrent()) {
+                    depth++;
+                }
+            }
+            return depth;
+        }
+
+        /**
+         * @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");
+            }
+            // make sure we're comparing normalized paths
+            Path p0 = getNormalizedPath();
+            Path p1 = other.getNormalizedPath();
+
+            if (p0.equals(p1)) {
+                return false;
+            }
+            // calculate depth of paths (might be negative)
+            if (p0.getDepth() >= p1.getDepth()) {
+                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])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * @see Path#isDescendantOf(Path)
+         */
+        public boolean isDescendantOf(Path other) throws IllegalArgumentException, RepositoryException {
+            if (other == null) {
+                throw new IllegalArgumentException("Null argument");
+            }
+            return other.isAncestorOf(this);
+        }
+
+        /**
+         * @see Path#subPath(int, int)
+         */
+        public Path subPath(int from, int to) throws IllegalArgumentException, RepositoryException {
+            if (from < 0 || to >= elements.length || from >= to) {
+                throw new IllegalArgumentException();
+            }
+            if (!isNormalized()) {
+                throw new RepositoryException("Cannot extract sub-Path from a non-normalized Path.");
+            }
+            Path.Element[] dest = new Path.Element[to-from];
+            System.arraycopy(elements, from, dest, 0, dest.length);
+            Builder pb = new Builder(dest);
+            return pb.getPath();
+        }
+
+        /**
+         * @see Path#getNameElement()
+         */
+        public Element getNameElement() {
+            return elements[elements.length - 1];
+        }
+
+        /**
+         * @see Path#getElements()
+         */
+        public Element[] getElements() {
+            return elements;
+        }
+
+        //---------------------------------------------------------< Object >---
+        /**
+         * Returns the internal string representation of this <code>Path</code>.
+         * <p/>
+         * Note that the returned string is not a valid JCR path, i.e. the
+         * namespace URI's of the individual path elements are not replaced with
+         * their mapped prefixes.
+         *
+         * @return the internal string representation of this <code>Path</code>.
+         */
+        public String toString() {
+            // Path is immutable, we can store the string representation
+            if (string == null) {
+                StringBuffer sb = new StringBuffer();
+                for (int i = 0; i < elements.length; i++) {
+                    if (i > 0) {
+                        sb.append(Path.DELIMITER);
+                    }
+                    Path.Element element = elements[i];
+                    String elem = element.toString();
+                    sb.append(elem);
+                }
+                string = sb.toString();
+            }
+            return string;
+        }
+
+        /**
+         * Returns a hash code value for this path.
+         *
+         * @return a hash code value for this path.
+         * @see Object#hashCode()
+         */
+        public int hashCode() {
+            // Path is immutable, we can store the computed hash code value
+            int h = hash;
+            if (h == 0) {
+                h = 17;
+                for (int i = 0; i < elements.length; i++) {
+                    h = 37 * h + elements[i].hashCode();
+                }
+                hash = h;
+            }
+            return h;
+        }
+
+        /**
+         * Compares the specified object with this path for equality.
+         *
+         * @param obj the object to be compared for equality with this path.
+         * @return <tt>true</tt> if the specified object is equal to this path.
+         */
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof PathImpl) {
+                Path other = (Path) obj;
+                return Arrays.equals(elements, other.getElements());
+            }
+            return false;
+        }
+    }
+
+    //-------------------------------------------------------< Path.Element >---
+    /**
+     * Object representation of a single JCR path element.
+     *
+     * @see Path.Element
+     */
+    private static class Element implements Path.Element {
+
+        /**
+         * Qualified name of the path element.
+         */
+        private final Name name;
+
+        /**
+         * Optional index of the path element. Set to zero, if not
+         * explicitly specified, otherwise contains the 1-based index.
+         */
+        private final int index;
+
+        /**
+         * Private constructor for creating a path element with the given
+         * qualified name and index. Instead of using this constructor directly
+         * the factory methods {@link PathFactory#createElement(Name)} and
+         * {@link PathFactory#create(Name, int)} should be used.
+         *
+         * @param name  qualified name
+         * @param index index
+         */
+        private Element(Name name, int index) {
+            this.index = index;
+            this.name = name;
+        }
+
+        /**
+         * @see Path.Element#getName()
+         */
+        public Name getName() {
+            return name;
+        }
+
+        /**
+         * @see Path.Element#getIndex()
+         */
+        public int getIndex() {
+            return index;
+        }
+
+        /**
+         * @see Path.Element#getNormalizedIndex()
+         */
+        public int getNormalizedIndex() {
+            if (index == Path.INDEX_UNDEFINED) {
+                return Path.INDEX_DEFAULT;
+            } else {
+                return index;
+            }
+        }
+
+        /**
+         * @return always returns false.
+         * @see Path.Element#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return false;
+        }
+
+        /**
+         * @return always returns false.
+         * @see Path.Element#denotesParent()
+         */
+        public boolean denotesParent() {
+            return false;
+        }
+
+        /**
+         * @return always returns false.
+         * @see Path.Element#denotesCurrent()
+         */
+        public boolean denotesCurrent() {
+            return false;
+        }
+
+        /**
+         * @return always returns true.
+         * @see Path.Element#denotesName()
+         */
+        public boolean denotesName() {
+            return true;
+        }
+
+        /**
+         * Returns a string representation of this path element. Note that
+         * the path element name is expressed using the <code>{uri}name</code>
+         * syntax.
+         *
+         * @return string representation of the path element
+         * @see Object#toString()
+         */
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            // name
+            sb.append(name.toString());
+            // index
+            if (index > Path.INDEX_DEFAULT) {
+                sb.append('[');
+                sb.append(index);
+                sb.append(']');
+            }
+            return sb.toString();
+        }
+
+        /**
+         * Computes a hash code for this path element.
+         *
+         * @return hash code
+         * @see Object#hashCode()
+         */
+        public int hashCode() {
+            int h = 17;
+            h = 37 * h + getNormalizedIndex();
+            h = 37 * h + name.hashCode();
+            return h;
+        }
+
+        /**
+         * Check for path element equality. Returns true if the given
+         * object is a PathElement and contains the same name and index
+         * as this one.
+         *
+         * @param obj the object to compare with
+         * @return <code>true</code> if the path elements are equal
+         * @see Object#equals(Object)
+         */
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof Path.Element) {
+                Path.Element other = (Path.Element) obj;
+                return name.equals(other.getName())
+                        && getNormalizedIndex() == other.getNormalizedIndex();
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Object representation of a special JCR path element notably the root, the
+     * current and the parent element.
+     */
+    private static final class SpecialElement extends Element {
+
+        private final static int ROOT = 1;
+        private final static int CURRENT = 2;
+        private final static int PARENT = 4;
+
+        private final int type;
+
+        private SpecialElement(Name name) {
+            super(name, Path.INDEX_UNDEFINED);
+            if (ROOT_NAME.equals(name)) {
+                type = ROOT;
+            } else if (CURRENT_NAME.equals(name)) {
+                type = CURRENT;
+            } else if (PARENT_NAME.equals(name)) {
+                type = PARENT;
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        /**
+         * @return true if this is the {@link #ROOT root element}.
+         * @see Path.Element#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return type == ROOT;
+        }
+
+        /**
+         * @return true if this is the {@link #PARENT parent element}.
+         * @see Path.Element#denotesParent()
+         */
+        public boolean denotesParent() {
+            return type == PARENT;
+        }
+
+        /**
+         * @return true if this is the {@link #CURRENT current element}.
+         * @see Path.Element#denotesCurrent()
+         */
+        public boolean denotesCurrent() {
+            return type == CURRENT;
+        }
+
+        /**
+         * @return Always returns false.
+         * @see Path.Element#denotesParent()
+         */
+        public boolean denotesName() {
+            return false;
+        }
+    }
+
+    /**
+     * Builder internal class
+     */
+    private static final class Builder {
+
+        /**
+         * the lpath elements of the constructed path
+         */
+        private final Path.Element[] elements;
+
+        /**
+         * 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;
+
+        /**
+         * Creates a new Builder and initialized it with the given path
+         * elements.
+         *
+         * @param elemList
+         * @throws IllegalArgumentException if the given elements array is null
+         * or has a length less than 1;
+         */
+        private Builder(List elemList) {
+            this((Path.Element[]) elemList.toArray(new Path.Element[elemList.size()]));
+        }
+
+        /**
+         * Creates a new Builder and initialized it with the given path
+         * elements.
+         *
+         * @param elements
+         * @throws IllegalArgumentException if the given elements array is null
+         * or has a length less than 1;
+         */
+        private Builder(Path.Element[] elements) {
+            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());
+            }
+        }
+
+        /**
+         * Assembles the built path and returns a new {@link Path}.
+         *
+         * @return a new {@link Path}
+         */
+        private Path getPath() {
+            // no need to check the path format, assuming all names correct
+            return new PathImpl(elements, isNormalized);
+        }
+    }
+}
\ No newline at end of file

Property changes on: src\main\java\org\apache\jackrabbit\name\PathFactoryImpl.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java	(revision 0)
@@ -0,0 +1,227 @@
+/*
+ * 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.name;
+
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.Name;
+
+/**
+ * <code>NameFactoryImpl</code>...
+ */
+public class NameFactoryImpl implements NameFactory {
+
+    private static NameFactory FACTORY;
+
+    private NameFactoryImpl() {};
+
+    public static NameFactory getInstance() {
+        if (FACTORY == null) {
+            FACTORY = new NameFactoryImpl();
+        }
+        return FACTORY;
+    }
+
+    //--------------------------------------------------------< NameFactory >---
+    /**
+     * @see NameFactory#create(String, String)
+     */
+    public Name create(String namespaceURI, String localName) throws IllegalArgumentException {
+        // NOTE: an empty localName and/or URI is valid (e.g. the root node name)
+        if (namespaceURI == null) {
+            throw new IllegalArgumentException("invalid namespaceURI specified");
+        }
+        if (localName == null) {
+            throw new IllegalArgumentException("invalid localName specified");
+        }
+        return new NameImpl(namespaceURI, localName);
+    }
+
+    /**
+     * @see NameFactory#create(String)
+     */
+    public Name create(String nameString) throws IllegalArgumentException {
+        if (nameString == null || "".equals(nameString)) {
+            throw new IllegalArgumentException("Invalid Name literal.");
+        }
+        if (nameString.charAt(0) != '{') {
+            throw new IllegalArgumentException("Invalid Name literal");
+        }
+        int i = nameString.indexOf('}');
+        if (i == -1) {
+            throw new IllegalArgumentException("Invalid Name literal");
+        }
+        if (i == nameString.length() - 1) {
+            throw new IllegalArgumentException("Invalid Name literal");
+        } else {
+            return new NameImpl(nameString.substring(1, i), nameString.substring(i + 1));
+        }
+    }
+
+    //--------------------------------------------------------< inner class >---
+    /**
+     * Inner class implementing the <code>Name</code> interface.
+     */
+    private static class NameImpl implements Name {
+
+        /** The memorized hash code of this qualified name. */
+        private transient int hash;
+
+        /** The memorized string representation of this qualified name. */
+        private transient String string;
+
+        /** The internalized namespace URI of this qualified name. */
+        private final String namespaceURI;
+
+        /** The local part of this qualified name. */
+        private final String localName;
+
+        private NameImpl(String namespaceURI, String localName) {
+            // internalize namespaceURI to improve performance of comparisons.            
+            this.namespaceURI = namespaceURI.intern();
+            // localName is not internalized in order not to risk huge perm
+            // space for large repositories
+            this.localName = localName;
+            hash = 0;
+        }
+
+        //-----------------------------------------------------------< Name >---
+        /**
+         * @see Name#getLocalName()
+         */
+        public String getLocalName() {
+            return localName;
+        }
+
+        /**
+         * @see Name#getNamespaceURI()
+         */
+        public String getNamespaceURI() {
+            return namespaceURI;
+        }
+
+        //---------------------------------------------------------< Object >---
+        /**
+         * Returns the string representation of this <code>Name</code> in the
+         * following format:
+         * <p/>
+         * <code><b>{</b>namespaceURI<b>}</b>localName</code>
+         *
+         * @return the string representation of this <code>QName</code>.
+         * @see NameFactory#create(String)
+         * @see Object#toString()
+         */
+        public String toString() {
+            // Name is immutable, we can store the string representation
+            if (string == null) {
+                string = '{' + namespaceURI + '}' + localName;
+            }
+            return string;
+        }
+
+        /**
+         * Compares two qualified names for equality. Returns <code>true</code>
+         * if the given object is a qualified name and has the same namespace URI
+         * and local part as this qualified name.
+         *
+         * @param obj the object to compare this qualified name with
+         * @return <code>true</code> if the object is equal to this qualified name,
+         *         <code>false</code> otherwise
+         * @see Object#equals(Object)
+         */
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof NameImpl) {
+                NameImpl other = (NameImpl) obj;
+                // we can use == operator for namespaceURI since it is internalized
+                return namespaceURI == other.namespaceURI && localName.equals(other.localName);
+            }
+            // some other Name implementation
+            if (obj instanceof Name) {
+                Name other = (Name) obj;
+                return namespaceURI.equals(other.getNamespaceURI()) && localName.equals(other.getLocalName());
+            }
+            return false;
+        }
+
+        /**
+         * Returns the hash code of this qualified name. The hash code is
+         * computed from the namespace URI and local part of the qualified
+         * name and memorized for better performance.
+         *
+         * @return hash code
+         * @see Object#hashCode()
+         */
+        public int hashCode() {
+            // Name is immutable, we can store the computed hash code value
+            int h = hash;
+            if (h == 0) {
+                h = 17;
+                h = 37 * h + namespaceURI.hashCode();
+                h = 37 * h + localName.hashCode();
+                hash = h;
+            }
+            return h;
+        }
+
+        //------------------------------------------------------< Cloneable >---
+        /**
+         * Creates a clone of this qualified name.
+         * Overriden in order to make <code>clone()</code> public.
+         *
+         * @return a clone of this instance
+         * @throws CloneNotSupportedException never thrown
+         * @see Object#clone()
+         */
+        public Object clone() throws CloneNotSupportedException {
+            // Name is immutable, no special handling required
+            return super.clone();
+        }
+
+        //-----------------------------------------------------< Comparable >---
+        /**
+         * Compares two qualified names.
+         *
+         * @param o the object to compare this qualified name with
+         * @return comparison result
+         * @throws ClassCastException if the given object is not a qualified name
+         * @see Comparable#compareTo(Object)
+         */
+        public int compareTo(Object o) {
+            if (this == o) {
+                return 0;
+            }
+            Name other = (Name) o;
+            if (namespaceURI.equals(other.getNamespaceURI())) {
+                return localName.compareTo(other.getLocalName());
+            } else {
+                return namespaceURI.compareTo(other.getNamespaceURI());
+            }
+        }
+
+        //---------------------------------------------------< Serializable >---
+        /**
+         * Creates a new <code>QName</code> instance using the proper constructor
+         * during deserialization in order to make sure that internalized strings
+         * are used where appropriate.
+         */
+        private Object readResolve() {
+            return new NameImpl(namespaceURI, localName);
+        }
+    }
+}
\ No newline at end of file

Property changes on: src\main\java\org\apache\jackrabbit\name\NameFactoryImpl.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/name/PathMap.java
===================================================================
--- src/main/java/org/apache/jackrabbit/name/PathMap.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/name/PathMap.java	(revision 0)
@@ -0,0 +1,650 @@
+/*
+ * 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.name;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.conversion.MalformedPathException;
+
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+/**
+ * Generic path map that associates information with the individual path elements
+ * of a path.
+ */
+public class PathMap {
+
+    private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+
+    /**
+     * Root element
+     */
+    private final Element root = new Element(PATH_FACTORY.getRootPath().getNameElement());
+
+    /**
+     * Map a path to a child. If <code>exact</code> is <code>false</code>,
+     * returns the last available item along the path that is stored in the map.
+     * @param path path to map
+     * @param exact flag indicating whether an exact match is required
+     * @return child, maybe <code>null</code> if <code>exact</code> is
+     *         <code>true</code>
+     */
+    public Element map(Path path, boolean exact) {
+        Path.Element[] elements = path.getElements();
+        Element current = root;
+
+        for (int i = 1; i < elements.length; i++) {
+            Element next = current.getChild(elements[i]);
+            if (next == null) {
+                if (exact) {
+                    return null;
+                }
+                break;
+            }
+            current = next;
+        }
+        return current;
+    }
+
+    /**
+     * Create an element given by its path. The path map will create any necessary
+     * intermediate elements.
+     * @param path path to child
+     * @param obj object to store at destination
+     */
+    public Element put(Path path, Object obj) {
+        Element element = put(path);
+        element.obj = obj;
+        return element;
+    }
+
+    /**
+     * Put an element given by its path. The path map will create any necessary
+     * intermediate elements.
+     * @param path path to child
+     * @param element element to store at destination
+     */
+    public void put(Path path, Element element) {
+        Path.Element[] elements = path.getElements();
+        Element current = root;
+
+        for (int i = 1; i < elements.length - 1; i++) {
+            Element next = current.getChild(elements[i]);
+            if (next == null) {
+                next = current.createChild(elements[i]);
+            }
+            current = next;
+        }
+        current.put(path.getNameElement(), element);
+    }
+
+    /**
+     * Create an empty child given by its path.
+     * @param path path to child
+     */
+    public Element put(Path path) {
+        Path.Element[] elements = path.getElements();
+        Element current = root;
+
+        for (int i = 1; i < elements.length; i++) {
+            Element next = current.getChild(elements[i]);
+            if (next == null) {
+                next = current.createChild(elements[i]);
+            }
+            current = next;
+        }
+        return current;
+    }
+
+    /**
+     * Traverse the path map and call back requester. This method visits the root
+     * first, then its children.
+     * @param includeEmpty if <code>true</code> invoke call back on every child
+     *                     regardless, whether the associated object is empty
+     *                     or not; otherwise call back on non-empty children
+     *                     only
+     */
+    public void traverse(ElementVisitor visitor, boolean includeEmpty) {
+        root.traverse(visitor, includeEmpty);
+    }
+
+    /**
+     * Internal class holding the object associated with a certain
+     * path element.
+     */
+    public static class Element {
+
+        /**
+         * Parent element
+         */
+        private Element parent;
+
+        /**
+         * Map of immediate children
+         */
+        private Map children;
+
+        /**
+         * Number of non-empty children
+         */
+        private int childrenCount;
+
+        /**
+         * Object associated with this element
+         */
+        private Object obj;
+
+        /**
+         * QName associated with this element
+         */
+        private Name name;
+
+        /**
+         * 1-based index associated with this element where index=0 is
+         * equivalent to index=1.
+         */
+        private int index;
+
+        /**
+         * Create a new instance of this class with a path element.
+         * @param nameIndex path element of this child
+         */
+        private Element(Path.Element nameIndex) {
+            this.name = nameIndex.getName();
+            this.index = nameIndex.getIndex();
+        }
+
+        /**
+         * Create a child of this node inside the path map.
+         * @param nameIndex position where child is created
+         * @return child
+         */
+        private Element createChild(Path.Element nameIndex) {
+            Element element = new Element(nameIndex);
+            put(nameIndex, element);
+            return element;
+        }
+
+        /**
+         * Insert an empty child. Will shift all children having an index
+         * greater than or equal to the child inserted to the right.
+         * @param nameIndex position where child is inserted
+         */
+        public void insert(Path.Element nameIndex) {
+            // convert 1-based index value to 0-base value
+            int index = getZeroBasedIndex(nameIndex);
+            if (children != null) {
+                ArrayList list = (ArrayList) children.get(nameIndex.getName());
+                if (list != null && list.size() > index) {
+                    for (int i = index; i < list.size(); i++) {
+                        Element element = (Element) list.get(i);
+                        if (element != null) {
+                            element.index = element.getNormalizedIndex() + 1;
+                        }
+                    }
+                    list.add(index, null);
+                }
+            }
+        }
+
+        /**
+         * Return an element matching a name and index.
+         * @param nameIndex position where child is located
+         * @return element matching <code>nameIndex</code> or <code>null</code> if
+         *         none exists.
+         */
+        private Element getChild(Path.Element nameIndex) {
+            // convert 1-based index value to 0-base value
+            int index = getZeroBasedIndex(nameIndex);
+            Element element = null;
+
+            if (children != null) {
+                ArrayList list = (ArrayList) children.get(nameIndex.getName());
+                if (list != null && list.size() > index) {
+                    element = (Element) list.get(index);
+                }
+            }
+            return element;
+        }
+
+        /**
+         * Link a child of this node. Position is given by <code>nameIndex</code>.
+         * @param nameIndex position where child should be located
+         * @param element element to add
+         */
+        public void put(Path.Element nameIndex, Element element) {
+            // convert 1-based index value to 0-base value
+            int index = getZeroBasedIndex(nameIndex);
+            if (children == null) {
+                children = new HashMap();
+            }
+            ArrayList list = (ArrayList) children.get(nameIndex.getName());
+            if (list == null) {
+                list = new ArrayList();
+                children.put(nameIndex.getName(), list);
+            }
+            while (list.size() < index) {
+                list.add(null);
+            }
+            if (list.size() == index) {
+                list.add(element);
+            } else {
+                list.set(index, element);
+            }
+
+            element.parent = this;
+            element.name = nameIndex.getName();
+            element.index = nameIndex.getIndex();
+
+            childrenCount++;
+        }
+
+        /**
+         * Remove a child. Will shift all children having an index greater than
+         * the child removed to the left. If there are no more children left in
+         * this element and no object is associated with this element, the
+         * element itself gets removed.
+         *
+         * @param nameIndex child's path element
+         * @return removed child, may be <code>null</code>
+         */
+        public Element remove(Path.Element nameIndex) {
+            return remove(nameIndex, true, true);
+        }
+
+        /**
+         * Remove a child. If <code>shift</code> is set to <code>true</code>,
+         * will shift all children having an index greater than the child
+         * removed to the left. If <code>removeIfEmpty</code> is set to
+         * <code>true</code> and there are no more children left in
+         * this element and no object is associated with this element, the
+         * element itself gets removed.
+         *
+         * @param nameIndex child's path element
+         * @param shift whether to shift same name siblings having a greater
+         *              index to the left
+         * @param removeIfEmpty remove this element itself if it contains
+         *                      no more children and is not associated to
+         *                      an element
+         * @return removed child, may be <code>null</code>
+         */
+        private Element remove(Path.Element nameIndex, boolean shift,
+                               boolean removeIfEmpty) {
+
+            // convert 1-based index value to 0-base value
+            int index = getZeroBasedIndex(nameIndex);
+            if (children == null) {
+                return null;
+            }
+            ArrayList list = (ArrayList) children.get(nameIndex.getName());
+            if (list == null || list.size() <= index) {
+                return null;
+            }
+            Element element = (Element) list.set(index, null);
+            if (shift) {
+                for (int i = index + 1; i < list.size(); i++) {
+                    Element sibling = (Element) list.get(i);
+                    if (sibling != null) {
+                        sibling.index--;
+                    }
+                }
+                list.remove(index);
+            }
+            if (element != null) {
+                element.parent = null;
+                childrenCount--;
+            }
+            if (removeIfEmpty && childrenCount == 0 && obj == null && parent != null) {
+                parent.remove(getPathElement(), shift, true);
+            }
+            return element;
+        }
+
+        /**
+         * Remove this element. Delegates the call to the parent item.
+         * Index of same name siblings will be shifted!
+         */
+        public void remove() {
+            remove(true);
+        }
+
+        /**
+         * Remove this element. Delegates the call to the parent item.
+         * @param shift if index of same name siblings will be shifted.
+         */
+        public void remove(boolean shift) {
+            if (parent != null) {
+                parent.remove(getPathElement(), shift, true);
+            } else {
+                // Removing the root node is not possible: if it has become
+                // invalid, remove all its children and the associated object
+                children = null;
+                childrenCount = 0;
+                obj = null;
+            }
+        }
+
+        /**
+         * Remove all children of this element. Removes this element itself
+         * if this element does not contain associated information.
+         */
+        public void removeAll() {
+            children = null;
+            childrenCount = 0;
+
+            if (obj == null && parent != null) {
+                parent.remove(getPathElement(), false, true);
+            }
+        }
+
+        /**
+         * Sets a new list of children of this element.
+         *
+         * @param children map of children; keys are of type
+         *                 <code>Path.PathElement</code> and values
+         *                 are of type <code>Element</code>
+         */
+        public void setChildren(Map children) {
+            // Remove all children without removing the element itself
+            this.children = null;
+            childrenCount = 0;
+
+            // Now add back all items
+            Iterator entries = children.entrySet().iterator();
+            while (entries.hasNext()) {
+                Map.Entry entry = (Map.Entry) entries.next();
+
+                Path.Element nameIndex = (Path.Element) entry.getKey();
+                Element element = (Element) entry.getValue();
+                put(nameIndex, element);
+            }
+
+            // Special case: if map was empty, handle like removeAll()
+            if (childrenCount == 0 && obj == null && parent != null) {
+                parent.remove(getPathElement(), false, true);
+            }
+        }
+
+        /**
+         * Return the object associated with this element
+         * @return object associated with this element
+         */
+        public Object get() {
+            return obj;
+        }
+
+        /**
+         * Set the object associated with this element
+         * @param obj object associated with this element
+         */
+        public void set(Object obj) {
+            this.obj = obj;
+
+            if (obj == null && childrenCount == 0 && parent != null) {
+                parent.remove(getPathElement(), false, true);
+            }
+        }
+
+        /**
+         * Return the name of this element
+         * @return name
+         */
+        public Name getName() {
+            return name;
+        }
+
+        /**
+         * Return the non-normalized 1-based index of this element. Note that
+         * this method can return a value of 0 which should be treated as 1.
+         * @return index
+         * @see #getNormalizedIndex()
+         */
+        public int getIndex() {
+            return index;
+        }
+
+        /**
+         * Return the 1-based index of this element.
+         * Same as {@link #getIndex()} except that an {@link Path#INDEX_UNDEFINED
+         * undefined index} value is automatically converted to the
+         * {@link Path#INDEX_DEFAULT default index} value.
+         * @return 1-based index
+         */
+        public int getNormalizedIndex() {
+            if (index == Path.INDEX_UNDEFINED) {
+                return Path.INDEX_DEFAULT;
+            } else {
+                return index;
+            }
+        }
+
+        /**
+         * Return a path element pointing to this element
+         * @return path element
+         */
+        public Path.Element getPathElement() {
+            return PATH_FACTORY.create(name, index).getNameElement();
+        }
+
+        /**
+         * Return the path of this element.
+         * @return path
+         * @throws MalformedPathException if building the path fails
+         */
+        public Path getPath() throws MalformedPathException {
+            if (parent == null) {
+                return PATH_FACTORY.getRootPath();
+            }
+
+            PathBuilder builder = new PathBuilder();
+            getPath(builder);
+            return builder.getPath();
+        }
+
+        /**
+         * Internal implementation of {@link #getPath()} that populates entries
+         * in a builder. On exit, <code>builder</code> contains the path
+         * of this element
+         */
+        private void getPath(PathBuilder builder) {
+            if (parent == null) {
+                builder.addRoot();
+                return;
+            }
+            parent.getPath(builder);
+            if (index == Path.INDEX_UNDEFINED || index == Path.INDEX_DEFAULT) {
+                builder.addLast(name);
+            } else {
+                builder.addLast(name, index);
+            }
+        }
+
+        /**
+         * Checks whether this element has the specified path. Introduced to
+         * avoid catching a <code>MalformedPathException</code> for simple
+         * path comparisons.
+         * @param path path to compare to
+         * @return <code>true</code> if this child has the path
+         *         <code>path</code>, <code>false</code> otherwise
+         */
+        public boolean hasPath(Path path) {
+            return hasPath(path.getElements(), path.getLength());
+        }
+
+        /**
+         * Checks whether this element has the specified path, given by
+         * path elements.
+         * @param elements path elements to compare to
+         * @param len number of elements to compare to
+         * @return <code>true</code> if this element has the path given;
+         *         otherwise <code>false</code>
+         */
+        private boolean hasPath(Path.Element[] elements, int len) {
+            if (getPathElement().equals(elements[len - 1])) {
+                if (parent != null) {
+                    return parent.hasPath(elements, len - 1);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Return 0-based index of a path element.
+         */
+        private static int getZeroBasedIndex(Path.Element nameIndex) {
+            return nameIndex.getNormalizedIndex() - 1;
+        }
+
+        /**
+         * Recursively invoked traversal method. This method visits the element
+         * first, then its children.
+         * @param visitor visitor to invoke
+         * @param includeEmpty if <code>true</code> invoke call back on every
+         *        element regardless, whether the associated object is empty
+         *        or not; otherwise call back on non-empty children only
+         */
+        public void traverse(ElementVisitor visitor, boolean includeEmpty) {
+            if (includeEmpty || obj != null) {
+                visitor.elementVisited(this);
+            }
+            if (children != null) {
+                Iterator iter = children.values().iterator();
+                while (iter.hasNext()) {
+                    ArrayList list = (ArrayList) iter.next();
+                    for (int i = 0; i < list.size(); i++) {
+                        Element element = (Element) list.get(i);
+                        if (element != null) {
+                            element.traverse(visitor, includeEmpty);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Return the depth of this element. Defined to be <code>0</code> for the
+         * root element and <code>n + 1</code> for some element if the depth of
+         * its parent is <code>n</code>.
+         */
+        public int getDepth() {
+            if (parent != null) {
+                return parent.getDepth() + 1;
+            }
+            // Root
+            return Path.ROOT_DEPTH;
+        }
+
+        /**
+         * Return a flag indicating whether the specified node is a
+         * child of this node.
+         * @param other node to check
+         */
+        public boolean isAncestorOf(Element other) {
+            Element parent = other.parent;
+            while (parent != null) {
+                if (parent == this) {
+                    return true;
+                }
+                parent = parent.parent;
+            }
+            return false;
+        }
+
+        /**
+         * Return the parent of this element
+         * @return parent or <code>null</code> if this is the root element
+         */
+        public Element getParent() {
+            return parent;
+        }
+
+        /**
+         * Return the children count of this element
+         * @return children count
+         */
+        public int getChildrenCount() {
+            return childrenCount;
+        }
+
+        /**
+         * Return an iterator over all of this element's children. Every
+         * element returned by this iterator is of type {@link #Element}.
+         */
+        public Iterator getChildren() {
+            ArrayList result = new ArrayList();
+
+            if (children != null) {
+                Iterator iter = children.values().iterator();
+                while (iter.hasNext()) {
+                    ArrayList list = (ArrayList) iter.next();
+                    for (int i = 0; i < list.size(); i++) {
+                        Element element = (Element) list.get(i);
+                        if (element != null) {
+                            result.add(element);
+                        }
+                    }
+                }
+            }
+            return result.iterator();
+        }
+
+        /**
+         * Map a relPath starting at <code>this</code> Element. If
+         * <code>exact</code> is <code>false</code>, returns the last available
+         * item along the relPath that is stored in the map.
+         *
+         * @param relPath relPath to map
+         * @param exact   flag indicating whether an exact match is required
+         * @return descendant, maybe <code>null</code> if <code>exact</code> is
+         *         <code>true</code>
+         */
+        public Element getDescendant(Path relPath, boolean exact) {
+            Path.Element[] elements = relPath.getElements();
+            Element current = this;
+
+            for (int i = 0; i < elements.length; i++) {
+                Element next = current.getChild(elements[i]);
+                if (next == null) {
+                    if (exact) {
+                        return null;
+                    }
+                    break;
+                }
+                current = next;
+            }
+            return current;
+        }
+    }
+
+    /**
+     * Element visitor used in {@link PathMap#traverse}
+     */
+    public interface ElementVisitor {
+
+        /**
+         * Invoked for every element visited on a tree traversal
+         * @param element element visited
+         */
+        void elementVisited(Element element);
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\name\PathMap.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/name/NameConstants.java
===================================================================
--- src/main/java/org/apache/jackrabbit/name/NameConstants.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/name/NameConstants.java	(revision 0)
@@ -0,0 +1,445 @@
+/*
+ * 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.name;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+
+/**
+ * <code>NameConstants</code>...
+ */
+public class NameConstants {
+
+    private static final NameFactory FACTORY = NameFactoryImpl.getInstance();
+
+    /**
+     * Extra Name for the root node
+     */
+    public static final Name ROOT = FACTORY.create(Name.NS_DEFAULT_URI,"");
+
+    /**
+     * jcr:system
+     */
+    public static final Name JCR_SYSTEM = FACTORY.create(Name.NS_JCR_URI, "system");
+
+    /**
+     * jcr:nodeTypes
+     */
+    public static final Name JCR_NODETYPES = FACTORY.create(Name.NS_JCR_URI, "nodeTypes");
+
+    /**
+     * jcr:uuid
+     */
+    public static final Name JCR_UUID = FACTORY.create(Name.NS_JCR_URI, "uuid");
+
+    /**
+     * jcr:primaryType
+     */
+    public static final Name JCR_PRIMARYTYPE = FACTORY.create(Name.NS_JCR_URI, "primaryType");
+
+    /**
+     * jcr:mixinTypes
+     */
+    public static final Name JCR_MIXINTYPES = FACTORY.create(Name.NS_JCR_URI, "mixinTypes");
+
+    /**
+     * jcr:created
+     */
+    public static final Name JCR_CREATED = FACTORY.create(Name.NS_JCR_URI, "created");
+
+    /**
+     * jcr:lastModified
+     */
+    public static final Name JCR_LASTMODIFIED = FACTORY.create(Name.NS_JCR_URI, "lastModified");
+
+    /**
+     * jcr:encoding
+     */
+    public static final Name JCR_ENCODING = FACTORY.create(Name.NS_JCR_URI, "encoding");
+
+    /**
+     * jcr:mimeType
+     */
+    public static final Name JCR_MIMETYPE = FACTORY.create(Name.NS_JCR_URI, "mimeType");
+
+    /**
+     * jcr:data
+     */
+    public static final Name JCR_DATA = FACTORY.create(Name.NS_JCR_URI, "data");
+
+    /**
+     * jcr:content
+     */
+    public static final Name JCR_CONTENT = FACTORY.create(Name.NS_JCR_URI, "content");
+
+    //--------------------------------------< xml related item name constants >
+
+    /**
+     * jcr:root (dummy name for root node used in XML serialization)
+     */
+    public static final Name JCR_ROOT = FACTORY.create(Name.NS_JCR_URI, "root");
+
+    /**
+     * jcr:xmltext
+     */
+    public static final Name JCR_XMLTEXT = FACTORY.create(Name.NS_JCR_URI, "xmltext");
+
+    /**
+     * jcr:xmlcharacters
+     */
+    public static final Name JCR_XMLCHARACTERS = FACTORY.create(Name.NS_JCR_URI, "xmlcharacters");
+
+    //-----------------------------------------< query related name constants >
+
+    /**
+     * jcr:score
+     */
+    public static final Name JCR_SCORE = FACTORY.create(Name.NS_JCR_URI, "score");
+
+    /**
+     * jcr:path
+     */
+    public static final Name JCR_PATH = FACTORY.create(Name.NS_JCR_URI, "path");
+
+    /**
+     * jcr:statement
+     */
+    public static final Name JCR_STATEMENT = FACTORY.create(Name.NS_JCR_URI, "statement");
+
+    /**
+     * jcr:language
+     */
+    public static final Name JCR_LANGUAGE = FACTORY.create(Name.NS_JCR_URI, "language");
+
+    //----------------------------------< locking related item name constants >
+
+    /**
+     * jcr:lockOwner
+     */
+    public static final Name JCR_LOCKOWNER = FACTORY.create(Name.NS_JCR_URI, "lockOwner");
+
+    /**
+     * jcr:lockIsDeep
+     */
+    public static final Name JCR_LOCKISDEEP = FACTORY.create(Name.NS_JCR_URI, "lockIsDeep");
+
+    //-------------------------------< versioning related item name constants >
+
+    /**
+     * jcr:versionStorage
+     */
+    public static final Name JCR_VERSIONSTORAGE = FACTORY.create(Name.NS_JCR_URI, "versionStorage");
+
+    /**
+     * jcr:mergeFailed
+     */
+    public static final Name JCR_MERGEFAILED = FACTORY.create(Name.NS_JCR_URI, "mergeFailed");
+
+    /**
+     * jcr:frozenNode
+     */
+    public static final Name JCR_FROZENNODE = FACTORY.create(Name.NS_JCR_URI, "frozenNode");
+
+    /**
+     * jcr:frozenUuid
+     */
+    public static final Name JCR_FROZENUUID = FACTORY.create(Name.NS_JCR_URI, "frozenUuid");
+
+    /**
+     * jcr:frozenPrimaryType
+     */
+    public static final Name JCR_FROZENPRIMARYTYPE = FACTORY.create(Name.NS_JCR_URI, "frozenPrimaryType");
+
+    /**
+     * jcr:frozenMixinTypes
+     */
+    public static final Name JCR_FROZENMIXINTYPES = FACTORY.create(Name.NS_JCR_URI, "frozenMixinTypes");
+
+    /**
+     * jcr:predecessors
+     */
+    public static final Name JCR_PREDECESSORS = FACTORY.create(Name.NS_JCR_URI, "predecessors");
+
+    /**
+     * jcr:versionLabels
+     */
+    public static final Name JCR_VERSIONLABELS = FACTORY.create(Name.NS_JCR_URI, "versionLabels");
+
+    /**
+     * jcr:successors
+     */
+    public static final Name JCR_SUCCESSORS = FACTORY.create(Name.NS_JCR_URI, "successors");
+
+    /**
+     * jcr:isCheckedOut
+     */
+    public static final Name JCR_ISCHECKEDOUT = FACTORY.create(Name.NS_JCR_URI, "isCheckedOut");
+
+    /**
+     * jcr:versionHistory
+     */
+    public static final Name JCR_VERSIONHISTORY = FACTORY.create(Name.NS_JCR_URI, "versionHistory");
+
+    /**
+     * jcr:baseVersion
+     */
+    public static final Name JCR_BASEVERSION = FACTORY.create(Name.NS_JCR_URI, "baseVersion");
+
+    /**
+     * jcr:childVersionHistory
+     */
+    public static final Name JCR_CHILDVERSIONHISTORY = FACTORY.create(Name.NS_JCR_URI, "childVersionHistory");
+
+    /**
+     * jcr:rootVersion
+     */
+    public static final Name JCR_ROOTVERSION = FACTORY.create(Name.NS_JCR_URI, "rootVersion");
+
+    /**
+     * jcr:versionableUuid
+     */
+    public static final Name JCR_VERSIONABLEUUID = FACTORY.create(Name.NS_JCR_URI, "versionableUuid");
+
+    //--------------------------------< node type related item name constants >
+
+    /**
+     * jcr:nodeTypeName
+     */
+    public static final Name JCR_NODETYPENAME = FACTORY.create(Name.NS_JCR_URI, "nodeTypeName");
+
+    /**
+     * jcr:hasOrderableChildNodes
+     */
+    public static final Name JCR_HASORDERABLECHILDNODES = FACTORY.create(Name.NS_JCR_URI, "hasOrderableChildNodes");
+
+    /**
+     * jcr:isMixin
+     */
+    public static final Name JCR_ISMIXIN = FACTORY.create(Name.NS_JCR_URI, "isMixin");
+
+    /**
+     * jcr:supertypes
+     */
+    public static final Name JCR_SUPERTYPES = FACTORY.create(Name.NS_JCR_URI, "supertypes");
+
+    /**
+     * jcr:propertyDefinition
+     */
+    public static final Name JCR_PROPERTYDEFINITION = FACTORY.create(Name.NS_JCR_URI, "propertyDefinition");
+
+    /**
+     * jcr:name
+     */
+    public static final Name JCR_NAME = FACTORY.create(Name.NS_JCR_URI, "name");
+
+    /**
+     * jcr:mandatory
+     */
+    public static final Name JCR_MANDATORY = FACTORY.create(Name.NS_JCR_URI, "mandatory");
+
+    /**
+     * jcr:protected
+     */
+    public static final Name JCR_PROTECTED = FACTORY.create(Name.NS_JCR_URI, "protected");
+
+    /**
+     * jcr:requiredType
+     */
+    public static final Name JCR_REQUIREDTYPE = FACTORY.create(Name.NS_JCR_URI, "requiredType");
+
+    /**
+     * jcr:onParentVersion
+     */
+    public static final Name JCR_ONPARENTVERSION = FACTORY.create(Name.NS_JCR_URI, "onParentVersion");
+
+    /**
+     * jcr:primaryItemName
+     */
+    public static final Name JCR_PRIMARYITEMNAME = FACTORY.create(Name.NS_JCR_URI, "primaryItemName");
+
+    /**
+     * jcr:multiple
+     */
+    public static final Name JCR_MULTIPLE = FACTORY.create(Name.NS_JCR_URI, "multiple");
+
+    /**
+     * jcr:valueConstraints
+     */
+    public static final Name JCR_VALUECONSTRAINTS = FACTORY.create(Name.NS_JCR_URI, "valueConstraints");
+
+    /**
+     * jcr:defaultValues
+     */
+    public static final Name JCR_DEFAULTVALUES = FACTORY.create(Name.NS_JCR_URI, "defaultValues");
+
+    /**
+     * jcr:autoCreated
+     */
+    public static final Name JCR_AUTOCREATED = FACTORY.create(Name.NS_JCR_URI, "autoCreated");
+
+    /**
+     * jcr:childNodeDefinition
+     */
+    public static final Name JCR_CHILDNODEDEFINITION = FACTORY.create(Name.NS_JCR_URI, "childNodeDefinition");
+
+    /**
+     * jcr:sameNameSiblings
+     */
+    public static final Name JCR_SAMENAMESIBLINGS = FACTORY.create(Name.NS_JCR_URI, "sameNameSiblings");
+
+    /**
+     * jcr:defaultPrimaryType
+     */
+    public static final Name JCR_DEFAULTPRIMARYTYPE = FACTORY.create(Name.NS_JCR_URI, "defaultPrimaryType");
+
+    /**
+     * jcr:requiredPrimaryTypes
+     */
+    public static final Name JCR_REQUIREDPRIMARYTYPES = FACTORY.create(Name.NS_JCR_URI, "requiredPrimaryTypes");
+
+    //-------------------------------------------< node type name constants >---
+    /**
+     * nt:unstructured
+     */
+    public static final Name NT_UNSTRUCTURED = FACTORY.create(Name.NS_NT_URI, "unstructured");
+
+    /**
+     * nt:base
+     */
+    public static final Name NT_BASE = FACTORY.create(Name.NS_NT_URI, "base");
+
+    /**
+     * nt:hierarchyNode
+     */
+    public static final Name NT_HIERARCHYNODE = FACTORY.create(Name.NS_NT_URI, "hierarchyNode");
+
+    /**
+     * nt:resource
+     */
+    public static final Name NT_RESOURCE = FACTORY.create(Name.NS_NT_URI, "resource");
+
+    /**
+     * nt:file
+     */
+    public static final Name NT_FILE = FACTORY.create(Name.NS_NT_URI, "file");
+
+    /**
+     * nt:folder
+     */
+    public static final Name NT_FOLDER = FACTORY.create(Name.NS_NT_URI, "folder");
+
+    /**
+     * nt:query
+     */
+    public static final Name NT_QUERY = FACTORY.create(Name.NS_NT_URI, "query");
+
+    /**
+     * mix:referenceable
+     */
+    public static final Name MIX_REFERENCEABLE = FACTORY.create(Name.NS_MIX_URI, "referenceable");
+    /**
+     * mix:referenceable
+     */
+    public static final Name MIX_LOCKABLE = FACTORY.create(Name.NS_MIX_URI, "lockable");
+    /**
+     * mix:versionable
+     */
+    public static final Name MIX_VERSIONABLE = FACTORY.create(Name.NS_MIX_URI, "versionable");
+    /**
+     * nt:versionHistory
+     */
+    public static final Name NT_VERSIONHISTORY = FACTORY.create(Name.NS_NT_URI, "versionHistory");
+    /**
+     * nt:version
+     */
+    public static final Name NT_VERSION = FACTORY.create(Name.NS_NT_URI, "version");
+    /**
+     * nt:versionLabels
+     */
+    public static final Name NT_VERSIONLABELS = FACTORY.create(Name.NS_NT_URI, "versionLabels");
+    /**
+     * nt:versionedChild
+     */
+    public static final Name NT_VERSIONEDCHILD = FACTORY.create(Name.NS_NT_URI, "versionedChild");
+    /**
+     * nt:frozenNode
+     */
+    public static final Name NT_FROZENNODE = FACTORY.create(Name.NS_NT_URI, "frozenNode");
+    /**
+     * nt:nodeType
+     */
+    public static final Name NT_NODETYPE = FACTORY.create(Name.NS_NT_URI, "nodeType");
+    /**
+     * nt:propertyDefinition
+     */
+    public static final Name NT_PROPERTYDEFINITION = FACTORY.create(Name.NS_NT_URI, "propertyDefinition");
+    /**
+     * nt:childNodeDefinition
+     */
+    public static final Name NT_CHILDNODEDEFINITION = FACTORY.create(Name.NS_NT_URI, "childNodeDefinition");
+
+
+    //--------------------------------------------------------------------------
+    /**
+     * rep:root
+     */
+    public static final Name REP_ROOT = FACTORY.create(Name.NS_REP_URI, "root");
+
+    /**
+     * rep:system
+     */
+    public static final Name REP_SYSTEM = FACTORY.create(Name.NS_REP_URI, "system");
+
+    /**
+     * rep:versionStorage
+     */
+    public static final Name REP_VERSIONSTORAGE = FACTORY.create(Name.NS_REP_URI, "versionStorage");
+
+    /**
+     * rep:versionStorage
+     */
+    public static final Name REP_NODETYPES = FACTORY.create(Name.NS_REP_URI, "nodeTypes");
+
+    /**
+     * The special wildcard name used as the name of residual item definitions.
+     */
+    public static final Name ANY_NAME = FACTORY.create("", "*");
+
+    //------------------------------------------< system view name constants >
+    /**
+     * sv:node
+     */
+    public static final Name SV_NODE = FACTORY.create(Name.NS_SV_URI, "node");
+    /**
+     * sv:property
+     */
+    public static final Name SV_PROPERTY = FACTORY.create(Name.NS_SV_URI, "property");
+    /**
+     * sv:value
+     */
+    public static final Name SV_VALUE = FACTORY.create(Name.NS_SV_URI, "value");
+    /**
+     * sv:type
+     */
+    public static final Name SV_TYPE = FACTORY.create(Name.NS_SV_URI, "type");
+    /**
+     * sv:name
+     */
+    public static final Name SV_NAME = FACTORY.create(Name.NS_SV_URI, "name");
+
+
+}
\ No newline at end of file

Property changes on: src\main\java\org\apache\jackrabbit\name\NameConstants.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/identifier/IdFactoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/identifier/IdFactoryImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/identifier/IdFactoryImpl.java	(working copy)
@@ -17,17 +17,27 @@
 package org.apache.jackrabbit.identifier;
 
 import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.name.PathFactoryImpl;
 
 /**
  * <code>IdFactoryImpl</code>...
  */
 public final class IdFactoryImpl extends AbstractIdFactory {
 
-    private static final IdFactory INSTANCE = new IdFactoryImpl();
+    private static IdFactory INSTANCE;
 
-    private IdFactoryImpl() {}
+    private IdFactoryImpl() {
+    }
 
     public static IdFactory getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = new IdFactoryImpl();
+        }
         return IdFactoryImpl.INSTANCE;
     }
+
+    protected PathFactory getPathFactory() {
+        return PathFactoryImpl.getInstance();
+    }
 }
Index: src/main/java/org/apache/jackrabbit/identifier/AbstractIdFactory.java
===================================================================
--- src/main/java/org/apache/jackrabbit/identifier/AbstractIdFactory.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/identifier/AbstractIdFactory.java	(working copy)
@@ -20,10 +20,11 @@
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.ItemId;
-import org.apache.jackrabbit.name.Path;
-import org.apache.jackrabbit.name.MalformedPathException;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
 
+import javax.jcr.RepositoryException;
 import java.io.Serializable;
 
 /**
@@ -32,13 +33,20 @@
 public abstract class AbstractIdFactory implements IdFactory {
 
     /**
+     * Subclassed need to define a PathFactory used to create IDs
+     *
+     * @return a implementation of <code>PathFactory</code>.
+     */
+    protected abstract PathFactory getPathFactory();
+
+    /**
      * {@inheritDoc}
      * @see IdFactory#createNodeId(NodeId, Path)
      */
     public NodeId createNodeId(NodeId parentId, Path path) {
         try {
             return new NodeIdImpl(parentId, path);
-        } catch (MalformedPathException e) {
+        } catch (RepositoryException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
     }
@@ -61,18 +69,18 @@
 
     /**
      * {@inheritDoc}
-     * @see IdFactory#createPropertyId(NodeId, QName)
+     * @see IdFactory#createPropertyId(NodeId,Name)
      */
-    public PropertyId createPropertyId(NodeId parentId, QName propertyName) {
+    public PropertyId createPropertyId(NodeId parentId, Name propertyName) {
         try {
             return new PropertyIdImpl(parentId, propertyName);
-        } catch (MalformedPathException e) {
+        } catch (RepositoryException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
     }
 
     //------------------------------------------------------< Inner classes >---
-    private static abstract class ItemIdImpl implements ItemId, Serializable {
+    private abstract class ItemIdImpl implements ItemId, Serializable {
 
         private final String uniqueID;
         private final Path path;
@@ -87,16 +95,16 @@
             this.path = path;
         }
 
-        private ItemIdImpl(NodeId parentId, QName name) throws MalformedPathException {
+        private ItemIdImpl(NodeId parentId, Name name) throws RepositoryException {
             if (parentId == null || name == null) {
                 throw new IllegalArgumentException("Invalid ItemIdImpl: parentId and name must not be null.");
             }
             this.uniqueID = parentId.getUniqueID();
             Path parentPath = parentId.getPath();
             if (parentPath != null) {
-                this.path = Path.create(parentPath, name, true);
+                this.path = getPathFactory().create(parentPath, name, true);
             } else {
-                this.path = Path.create(name, Path.INDEX_UNDEFINED);
+                this.path = getPathFactory().create(name);
             }
         }
 
@@ -164,7 +172,7 @@
         }
     }
 
-    private static class NodeIdImpl extends ItemIdImpl implements NodeId {
+    private class NodeIdImpl extends ItemIdImpl implements NodeId {
 
         public NodeIdImpl(String uniqueID) {
             super(uniqueID, null);
@@ -174,8 +182,8 @@
             super(uniqueID, path);
         }
 
-        public NodeIdImpl(NodeId parentId, Path path) throws MalformedPathException {
-            super(parentId.getUniqueID(), (parentId.getPath() != null) ? Path.create(parentId.getPath(), path, true) : path);
+        public NodeIdImpl(NodeId parentId, Path path) throws RepositoryException {
+            super(parentId.getUniqueID(), (parentId.getPath() != null) ? getPathFactory().create(parentId.getPath(), path, true) : path);
         }
 
         public boolean denotesNode() {
@@ -193,11 +201,11 @@
         }
     }
 
-    private static class PropertyIdImpl extends ItemIdImpl implements PropertyId {
+    private class PropertyIdImpl extends ItemIdImpl implements PropertyId {
 
         private final NodeId parentId;
 
-        private PropertyIdImpl(NodeId parentId, QName name) throws MalformedPathException {
+        private PropertyIdImpl(NodeId parentId, Name name) throws RepositoryException {
             super(parentId, name);
             this.parentId = parentId;
         }
@@ -210,7 +218,7 @@
             return parentId;
         }
 
-        public QName getQName() {
+        public Name getName() {
             return getPath().getNameElement().getName();
         }
 
Index: src/main/java/org/apache/jackrabbit/conversion/CachingNameResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/CachingNameResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/CachingNameResolver.java	(revision 0)
@@ -0,0 +1,101 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Name;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Name resolver decorator that uses a generational cache to speed up
+ * parsing and formatting of JCR names. Uncached names are resolved using
+ * the underlying decorated name resolver.
+ */
+public class CachingNameResolver implements NameResolver {
+
+    /**
+     * Decorated name resolver.
+     */
+    private final NameResolver resolver;
+
+    /**
+     * Generational cache.
+     */
+    private final GenerationalCache cache;
+
+    /**
+     * Creates a caching decorator for the given name resolver. The given
+     * generational cache is used for caching.
+     *
+     * @param resolver decorated name resolver
+     * @param cache generational cache
+     */
+    public CachingNameResolver(NameResolver resolver, GenerationalCache cache) {
+        this.resolver = resolver;
+        this.cache = cache;
+    }
+
+    /**
+     * Creates a caching decorator for the given name resolver.
+     *
+     * @param resolver name resolver
+     */
+    public CachingNameResolver(NameResolver resolver) {
+        this(resolver, new GenerationalCache());
+    }
+
+    //-------------------------------------------------------< NameResolver >---
+    /**
+     * Returns the qualified name for the given prefixed JCR name. The name
+     * is first looked up form the generational cache and the call gets
+     * delegated to the decorated name resolver only if the cache misses.
+     *
+     * @param name prefixed JCR name
+     * @return qualified name
+     * @throws NameException if the JCR name format is invalid
+     * @throws NamespaceException if the namespace prefix can not be resolved
+     */
+    public Name getQName(String name)
+            throws NameException, NamespaceException {
+        Name qname = (Name) cache.get(name);
+        if (qname == null) {
+            qname = resolver.getQName(name);
+            cache.put(name, qname);
+        }
+        return qname;
+    }
+
+
+    /**
+     * Returns the prefixed JCR name for the given qualified name. The name
+     * is first looked up form the generational cache and the call gets
+     * delegated to the decorated name resolver only if the cache misses.
+     *
+     * @param qname qualified name
+     * @return prefixed JCR name
+     * @throws NamespaceException if the namespace URI can not be resolved
+     */
+    public String getJCRName(Name qname) throws NamespaceException {
+        String name = (String) cache.get(qname);
+        if (name == null) {
+            name = resolver.getJCRName(qname);
+            cache.put(qname, name);
+        }
+        return name;
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\CachingNameResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/NameException.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/NameException.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/NameException.java	(revision 0)
@@ -0,0 +1,47 @@
+/*
+ * 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.conversion;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Base class for exceptions about malformed or otherwise
+ * invalid JCR names and paths.
+ */
+public class NameException extends RepositoryException {
+
+    /**
+     * Creates a NameException with the given error message.
+     *
+     * @param message error message
+     */
+    public NameException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a NameException with the given error message and
+     * root cause exception.
+     *
+     * @param message   error message
+     * @param rootCause root cause exception
+     */
+    public NameException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\NameException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/NamePathResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/NamePathResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/NamePathResolver.java	(revision 0)
@@ -0,0 +1,26 @@
+/*
+ * 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.conversion;
+
+/**
+ * Combined name and path resolver. Can be used instead of the component
+ * name and path resolver interfaces when a single dependency for both
+ * name and path resolution is needed.
+ *
+ */
+public interface NamePathResolver extends NameResolver, PathResolver {
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\NamePathResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/MalformedPathException.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/MalformedPathException.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/MalformedPathException.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * 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.conversion;
+
+/**
+ * Thrown when a malformed JCR path string is encountered. This exception is
+ * thrown when attempting to parse a JCR path string that does not match the
+ * JCR patch syntax, contains an invalid path elements, or is otherwise not
+ * well formed.
+ * <p>
+ * See the section 4.6 of the JCR 1.0 specification for details of the
+ * JCR path syntax.
+ */
+public class MalformedPathException extends NameException {
+
+    /**
+     * Creates a MalformedPathException with the given error message.
+     *
+     * @param message error message
+     */
+    public MalformedPathException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a MalformedPathException with the given error message
+     * and root cause exception.
+     *
+     * @param message error message
+     * @param rootCause root cause exception
+     */
+    public MalformedPathException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\MalformedPathException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/NameResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/NameResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/NameResolver.java	(revision 0)
@@ -0,0 +1,48 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Name;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Resolver for prefixed JCR names and namespace-qualified
+ * {@link Name Names}.
+ */
+public interface NameResolver {
+
+    /**
+     * Returns the qualified name for the given prefixed JCR name.
+     *
+     * @param name prefixed JCR name
+     * @return qualified name
+     * @throws NameException if the JCR name format is invalid
+     * @throws NamespaceException if the namespace prefix can not be resolved
+     */
+    Name getQName(String name) throws NameException, NamespaceException;
+
+    /**
+     * Returns the prefixed JCR name for the given qualified name.
+     *
+     * @param name qualified name
+     * @return prefixed JCR name
+     * @throws NamespaceException if the namespace URI can not be resolved
+     */
+    String getJCRName(Name name) throws NamespaceException;
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\NameResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/ParsingPathResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/ParsingPathResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/ParsingPathResolver.java	(revision 0)
@@ -0,0 +1,169 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.PathFactory;
+
+import javax.jcr.NamespaceException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Path resolver that parsers and formats prefixed JCR paths.
+ * A {@link NameResolver} is used for resolving the path element names.
+ */
+public class ParsingPathResolver implements PathResolver {
+
+    /**
+     * Path factory.
+     */
+    private final PathFactory pathFactory;
+
+    /**
+     * Name resolver.
+     */
+    private final NameResolver resolver;
+
+    /**
+     * Creates a parsing path resolver.
+     *
+     * @param resolver name resolver
+     */
+    public ParsingPathResolver(PathFactory pathFactory, NameResolver resolver) {
+        this.pathFactory = pathFactory;
+        this.resolver = resolver;
+    }
+
+    /**
+     * Parses and returns a single path element.
+     *
+     * @param path JCR path
+     * @param begin begin index of the path element
+     * @param end end index of the path element
+     * @return path element
+     * @throws NameException if the path element format is invalid
+     * @throws NamespaceException if the namespace prefix can not be resolved
+     */
+    private Path.Element getElement(String path, int begin, int end)
+            throws NameException, NamespaceException {
+        if (begin == end) {
+            throw new MalformedPathException("Empty path element: " + path);
+        } else if (begin + 1 == end && path.charAt(begin) == '.') {
+            return pathFactory.getCurrentElement();
+        } else if (begin + 2 == end && path.charAt(begin) == '.'
+                   && path.charAt(begin + 1) == '.') {
+            return pathFactory.getParentElement();
+        } else if (begin + 3 < end && path.charAt(end - 1) == ']') {
+            int index = 0;
+            int factor = 1;
+            end -= 2;
+            char ch = path.charAt(end);
+            while (begin < end && '0' <= ch && ch <= '9') {
+                index += factor * (ch - '0');
+                factor *= 10;
+                ch = path.charAt(--end);
+            }
+            if (index == 0 || ch != '[') {
+                throw new MalformedPathException("Invalid path index: " + path);
+            }
+            Name qname = resolver.getQName(path.substring(begin, end));
+            return pathFactory.createElement(qname, index);
+        } else {
+            Name qname = resolver.getQName(path.substring(begin, end));
+            return pathFactory.createElement(qname);
+        }
+    }
+
+    /**
+     * Parses the prefixed JCR path and returns the resolved qualified path.
+     *
+     * @param path prefixed JCR path
+     * @return qualified path
+     * @throws NameException if the JCR path format is invalid
+     * @throws NamespaceException if a namespace prefix can not be resolved
+     */
+    public Path getQPath(String path) throws NameException, NamespaceException {
+        List elems = new ArrayList();
+        int length = path.length();
+        int position = 0;
+        int slash = path.indexOf('/');
+
+        if (slash == 0) {
+            if (length == 1) {
+                return pathFactory.getRootPath();
+            }
+            elems.add(pathFactory.getRootElement());
+            position = 1;
+            slash = path.indexOf('/', 1);
+        }
+        while (slash != -1) {
+            elems.add(getElement(path, position, slash));
+            position = slash + 1;
+            slash = path.indexOf('/', position);
+        }
+        if (position != length) {
+            elems.add(getElement(path, position, length));
+        }
+
+        if (elems.size() == 0) {
+            throw new MalformedPathException("'" +path+ "' is not a valid path.");
+        }
+        return pathFactory.create((Path.Element[]) elems.toArray(new Path.Element[elems.size()]));
+    }
+
+
+    /**
+     * Returns the prefixed JCR path for the given qualified path.
+     *
+     * @param path qualified path
+     * @return prefixed JCR path
+     * @throws NamespaceException if a namespace URI can not be resolved
+     */
+    public String getJCRPath(Path path) throws NamespaceException {
+        StringBuffer buffer = new StringBuffer();
+
+        Path.Element[] elements = path.getElements();
+        for (int i = 0; i < elements.length; i++) {
+            if (i > 0) {
+                buffer.append('/');
+            }
+            if (i == 0 && elements.length == 1 && elements[i].denotesRoot()) {
+                buffer.append('/');
+            } else if (elements[i].denotesCurrent()) {
+                buffer.append('.');
+            } else if (elements[i].denotesParent()) {
+                buffer.append("..");
+            } else {
+                buffer.append(resolver.getJCRName(elements[i].getName()));
+                /**
+                 * FIXME the [1] subscript should only be suppressed if the
+                 * item in question can't have same-name siblings.
+                 */
+                if (elements[i].getIndex() > Path.INDEX_DEFAULT) {
+                    buffer.append('[');
+                    buffer.append(elements[i].getIndex());
+                    buffer.append(']');
+                }
+            }
+        }
+
+        return buffer.toString();
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\ParsingPathResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/DefaultNamePathResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/DefaultNamePathResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/DefaultNamePathResolver.java	(revision 0)
@@ -0,0 +1,64 @@
+/*
+ * 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.conversion;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.namespace.NamespaceResolver;
+import org.apache.jackrabbit.name.NameFactoryImpl;
+import org.apache.jackrabbit.name.PathFactoryImpl;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * <code>DefaultNamePathResolver</code>...
+ */
+public class DefaultNamePathResolver implements NamePathResolver {
+
+    private static Logger log = LoggerFactory.getLogger(DefaultNamePathResolver.class);
+
+    private final NameResolver nResolver;
+    private final PathResolver pResolver;
+
+    public DefaultNamePathResolver(NamespaceResolver nsResolver) {
+        this.nResolver = new ParsingNameResolver(NameFactoryImpl.getInstance(), nsResolver);
+        this.pResolver = new ParsingPathResolver(PathFactoryImpl.getInstance(), nResolver);
+    }
+
+    public DefaultNamePathResolver(NameResolver nResolver, PathResolver pResolver) {
+        this.nResolver = nResolver;
+        this.pResolver = pResolver;
+    }
+
+    public Name getQName(String name) throws NameException, NamespaceException {
+        return nResolver.getQName(name);
+    }
+
+    public String getJCRName(Name name) throws NamespaceException {
+        return nResolver.getJCRName(name);
+    }
+
+    public Path getQPath(String path) throws NameException, NamespaceException {
+        return pResolver.getQPath(path);
+    }
+
+    public String getJCRPath(Path path) throws NamespaceException {
+        return pResolver.getJCRPath(path);
+    }
+}
\ No newline at end of file

Property changes on: src\main\java\org\apache\jackrabbit\conversion\DefaultNamePathResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/NameCache.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/NameCache.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/NameCache.java	(revision 0)
@@ -0,0 +1,60 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Name;
+
+/**
+ * The name cache defines an interface for caching {@link Name}s.
+ * <p/>
+ * Please note, that the redundant naming of the methods is intentionally in
+ * order to allow a class to implement several caches.
+ */
+public interface NameCache {
+
+    /**
+     * Retrieves a qualified name from the cache for the given jcr name. If the
+     * name is not cached <code>null</code> is returned.
+     *
+     * @param jcrName the jcr name
+     * @return the qualified name or <code>null</code>
+     */
+    public Name retrieveName(String jcrName);
+
+    /**
+     * Retrieves a jcr name from the cache for the given qualified name. If the
+     * name is not cached <code>null</code> is returned.
+     *
+     * @param name the qualified name
+     * @return the jcr name or <code>null</code>
+     */
+    public String retrieveName(Name name);
+
+    /**
+     * Puts a name into the cache.
+     *
+     * @param jcrName the jcr name
+     * @param name the qualified name
+     */
+    public void cacheName(String jcrName, Name name);
+
+    /**
+     * Evicts all names from the cache, i.e. clears it.
+     */
+    public void evictAllNames();
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\NameCache.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/ParsingNameResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/ParsingNameResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/ParsingNameResolver.java	(revision 0)
@@ -0,0 +1,159 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.namespace.NamespaceResolver;
+import org.apache.jackrabbit.util.XMLChar;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Name resolver that parsers and formats prefixed JCR names.
+ * A {@link NamespaceResolver} is used for resolving the namespace prefixes.
+ */
+public class ParsingNameResolver implements NameResolver {
+
+    /**
+     * Set of invalid or potentially invalid name characters.
+     * Organized as a boolean array indexed by the ASCII character
+     * code for maximum lookup performance.
+     */
+    private static final boolean[] MAYBE_INVALID = new boolean[128];
+
+    /**
+     * Initializes the set of invalid or potentially invalid name characters.
+     */
+    static {
+        for (char ch = 0; ch < MAYBE_INVALID.length; ch++) {
+            MAYBE_INVALID[ch] = Character.isSpaceChar(ch);
+        }
+        MAYBE_INVALID['.'] = true;
+        MAYBE_INVALID[':'] = true;
+        MAYBE_INVALID['/'] = true;
+        MAYBE_INVALID['['] = true;
+        MAYBE_INVALID[']'] = true;
+        MAYBE_INVALID['*'] = true;
+        MAYBE_INVALID['|'] = true;
+        MAYBE_INVALID['"'] = true;
+        MAYBE_INVALID['\''] = true;
+    }
+
+    /**
+     * Checks whether the given character is an invalid or potentially an
+     * invalid name character.
+     *
+     * @param ch character
+     * @return <code>true</code> if the character maybe is invalid in a name,
+     *         <code>false</code> if it is valid in any place within a name
+     */
+    private static boolean maybeInvalid(char ch) {
+        if (ch < MAYBE_INVALID.length) {
+            return MAYBE_INVALID[ch];
+        } else {
+            return Character.isSpaceChar(ch);
+        }
+    }
+
+    /**
+     * Name factory.
+     */
+    private final NameFactory nameFactory;
+
+    /**
+     * Namespace resolver.
+     */
+    private final NamespaceResolver resolver;
+
+    /**
+     * Creates a parsing name resolver.
+     *
+     * @param resolver namespace resolver
+     */
+    public ParsingNameResolver(NameFactory nameFactory, NamespaceResolver resolver) {
+        this.nameFactory = nameFactory;
+        this.resolver = resolver;
+    }
+
+    //--------------------------------------------------------< NameResolver >
+
+    /**
+     * Parses the prefixed JCR name and returns the resolved qualified name.
+     *
+     * @param name prefixed JCR name
+     * @return qualified name
+     * @throws NameException if the JCR name format is invalid
+     * @throws NamespaceException if the namespace prefix can not be resolved
+     */
+    public Name getQName(String name) throws NameException, NamespaceException {
+        int length = name.length();
+        int colon = -1;
+
+        for (int i = 0; i < length; i++) {
+            char ch = name.charAt(i);
+            if (maybeInvalid(ch)) {
+                if (ch == ':' && colon == -1 && 0 < i && i + 1 < length) {
+                    colon = i; // Detect the position of the first colon
+                } else if (ch == ' ' && colon + 1 < i && i + 1 < length) {
+                    // Allow spaces in the middle of the local name
+                } else if (ch == '.'
+                           && (i > 0 || length > 2
+                               || (length == 2 && name.charAt(1) != '.'))) {
+                    // Allow dots when the name is not "." or ".."
+                } else {
+                    throw new IllegalNameException("Invalid name: " + name);
+                }
+            }
+        }
+
+        if (length == 0) {
+            throw new IllegalNameException("Empty name");
+        } else if (colon == -1) {
+            return nameFactory.create(Name.NS_DEFAULT_URI, name);
+        } else {
+            // Resolve the prefix (and validate the prefix format!)
+            String prefix = name.substring(0, colon);
+            if (!XMLChar.isValidNCName(prefix)) {
+                // illegal syntax for prefix
+                throw new IllegalNameException("'" + name + "' contains an illegal prefix");
+            }
+            String uri = resolver.getURI(prefix);
+            return nameFactory.create(uri, name.substring(colon + 1));
+        }
+    }
+
+    /**
+     * Returns the prefixed JCR name for the given qualified name.
+     * If the name is in the default namespace, then the local name
+     * is returned without a prefix. Otherwise the prefix for the
+     * namespace is resolved and used to construct returned the JCR name.
+     *
+     * @param name qualified name
+     * @return prefixed JCR name
+     * @throws NamespaceException if the namespace URI can not be resolved
+     */
+    public String getJCRName(Name name) throws NamespaceException {
+        String uri = name.getNamespaceURI();
+        if (uri.length() == 0) {
+            return name.getLocalName();
+        } else {
+            return resolver.getPrefix(uri) + ":" + name.getLocalName();
+        }
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\ParsingNameResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/IllegalNameException.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/IllegalNameException.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/IllegalNameException.java	(revision 0)
@@ -0,0 +1,51 @@
+/*
+ * 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.conversion;
+
+/**
+ * Thrown when an illegal JCR name string is encountered. This exception is
+ * thrown when attempting to parse a JCR name string that does not match the
+ * JCR name syntax, or is otherwise not a legal name. Note that an
+ * {@link javax.jcr.NamespaceException} is thrown if the prefix of the JCR name
+ * string is syntactically valid but not bound to any namespace.
+ * <p>
+ * See the section 4.6 of the JCR 1.0 specification for details of the
+ * JCR name syntax.
+ */
+public class IllegalNameException extends NameException {
+
+    /**
+     * Creates an IllegalNameException with the given error message.
+     *
+     * @param message error message
+     */
+    public IllegalNameException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an IllegalNameException with the given error message and
+     * root cause exception.
+     *
+     * @param message error message
+     * @param rootCause root cause exception
+     */
+    public IllegalNameException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\IllegalNameException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/CachingPathResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/CachingPathResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/CachingPathResolver.java	(revision 0)
@@ -0,0 +1,101 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Path;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Path resolver decorator that uses a generational cache to speed up
+ * parsing and formatting of JCR paths. Uncached paths are resolved using
+ * the underlying decorated path resolver.
+ */
+public class CachingPathResolver implements PathResolver {
+
+    /**
+     * Decorated path resolver.
+     */
+    private final PathResolver resolver;
+
+    /**
+     * Generational cache.
+     */
+    private final GenerationalCache cache;
+
+    /**
+     * Creates a caching decorator for the given path resolver. The given
+     * generational cache is used for caching.
+     *
+     * @param resolver decorated path resolver
+     * @param cache generational cache
+     */
+    public CachingPathResolver(PathResolver resolver, GenerationalCache cache) {
+        this.resolver = resolver;
+        this.cache = cache;
+    }
+
+    /**
+     * Creates a caching decorator for the given path resolver.
+     *
+     * @param resolver name resolver
+     */
+    public CachingPathResolver(PathResolver resolver) {
+        this(resolver, new GenerationalCache());
+    }
+
+    //--------------------------------------------------------< PathResolver >
+
+    /**
+     * Returns the qualified path for the given prefixed JCR path. The path
+     * is first looked up form the generational cache and the call gets
+     * delegated to the decorated path resolver only if the cache misses.
+     *
+     * @param path prefixed JCR path
+     * @return qualified path
+     * @throws NameException if the JCR path format is invalid
+     * @throws NamespaceException if a namespace prefix can not be resolved
+     */
+    public Path getQPath(String path) throws NameException, NamespaceException {
+        Path qpath = (Path) cache.get(path);
+        if (qpath == null) {
+            qpath = resolver.getQPath(path);
+            cache.put(path, qpath);
+        }
+        return qpath;
+    }
+
+
+    /**
+     * Returns the prefixed JCR path for the given qualified path. The path
+     * is first looked up form the generational cache and the call gets
+     * delegated to the decorated path resolver only if the cache misses.
+     *
+     * @param qpath qualified path
+     * @return prefixed JCR path
+     * @throws NamespaceException if a namespace URI can not be resolved
+     */
+    public String getJCRPath(Path qpath) throws NamespaceException {
+        String path = (String) cache.get(qpath);
+        if (path == null) {
+            path = resolver.getJCRPath(qpath);
+            cache.put(qpath, path);
+        }
+        return path;
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\CachingPathResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/GenerationalCache.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/GenerationalCache.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/GenerationalCache.java	(revision 0)
@@ -0,0 +1,162 @@
+/*
+ * 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.conversion;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Generational cache. The cache implemented by this class consists of three
+ * parts: a long term cache and two generations of recent entries. The two
+ * generations are used to collect recent new entries, and those entries that
+ * are used within two successive generations get promoted to the long term
+ * cache. The entries within the long term cache are discarded only when the
+ * size of the cache exceeds the given maximum cache size.
+ */
+class GenerationalCache {
+
+    /**
+     * Default maximum cache size.
+     */
+    private static final int DEFAULT_CACHE_SIZE = 1000;
+
+    /**
+     * Divisor used to determine the default generation age from the
+     * maximum cache size.
+     */
+    private static final int DEFAULT_SIZE_AGE_RATIO = 10;
+
+    /**
+     * Maximum size of the name cache.
+     */
+    private final int maxSize;
+
+    /**
+     * Maximum age of a cache generation.
+     */
+    private final int maxAge;
+
+    /**
+     * Long term cache. Read only.
+     */
+    private Map cache = new HashMap();
+
+    /**
+     * Old cache generation.
+     */
+    private Map old = new HashMap();
+
+    /**
+     * Young cache generation.
+     */
+    private Map young = new HashMap();
+
+    /**
+     * Age of the young cache generation.
+     */
+    private int age = 0;
+
+    /**
+     * Creates a caching resolver.
+     *
+     * @param maxSize maximum size of the long term cache
+     * @param maxAge maximum age of a cache generation
+     */
+    public GenerationalCache(int maxSize, int maxAge) {
+        this.maxSize = maxSize;
+        this.maxAge = maxAge;
+    }
+
+    /**
+     * Creates a caching resolver using the default generation age for
+     * the given cache size.
+     *
+     * @param maxSize maximum size of the long term cache
+     */
+    public GenerationalCache(int maxSize) {
+        this(maxSize, maxSize / DEFAULT_SIZE_AGE_RATIO);
+    }
+
+    /**
+     * Creates a caching resolver using the default size and generation age.
+     */
+    public GenerationalCache() {
+        this(DEFAULT_CACHE_SIZE);
+    }
+
+    /**
+     * Returns the cached value (if any) for the given key. The value is
+     * looked up both from the long term cache and the old cache generation.
+     * If the value is only found in the old cache generation, it gets added
+     * to the young generation via a call to {@link #put(Object, Object)}.
+     *
+     * @param key key of the cache entry
+     * @return value of the cache entry, or <code>null</code>
+     */
+    public Object get(Object key) {
+        Object value = cache.get(key);
+        if (value == null) {
+            value = old.get(key);
+            if (value != null) {
+                put(key, value);
+            }
+        }
+        return value;
+    }
+
+    /**
+     * Caches the given key-value pair and increases the age of the current
+     * cache generation. When the maximum age of a generation is reached,
+     * the following steps are taken:
+     * <ol>
+     *   <li>The union of the two cache generations is calculated</li>
+     *   <li>The union is added to the long term name cache</li>
+     *   <li>If the cache size exceeds the maximum, only the union is kept</li>
+     *   <li>A new cache generation is started</li>
+     * </ol>
+     *
+     * @param key key of the cache entry
+     * @param value value of the cache entry
+     */
+    public synchronized void put(Object key, Object value) {
+        young.put(key, value);
+
+        if (++age == maxAge) {
+            Map union = new HashMap();
+            Iterator iterator = old.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                if (young.containsKey(entry.getKey())) {
+                    union.put(entry.getKey(), entry.getValue());
+                }
+            }
+
+            if (!union.isEmpty()) {
+                if (cache.size() + union.size() <= maxSize) {
+                    union.putAll(cache);
+                }
+                cache = union;
+            }
+
+            old = young;
+            young = new HashMap();
+            age = 0;
+        }
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\GenerationalCache.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/conversion/PathResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/conversion/PathResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/conversion/PathResolver.java	(revision 0)
@@ -0,0 +1,47 @@
+/*
+ * 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.conversion;
+
+import org.apache.jackrabbit.spi.Path;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Resolver for JCR paths.
+ */
+public interface PathResolver {
+
+    /**
+     * Returns the qualified path for the given prefixed JCR path.
+     *
+     * @param path prefixed JCR path
+     * @return qualified path
+     * @throws NameException if the JCR path format is invalid
+     * @throws NamespaceException if a namespace prefix can not be resolved
+     */
+    Path getQPath(String path) throws NameException, NamespaceException;
+
+    /**
+     * Returns the prefixed JCR path for the given qualified path.
+     *
+     * @param path qualified path
+     * @return prefixed JCR path
+     * @throws NamespaceException if a namespace URI can not be resolved
+     */
+    String getJCRPath(Path path) throws NamespaceException;
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\conversion\PathResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/nodetype/NodeTypeConflictException.java
===================================================================
--- src/main/java/org/apache/jackrabbit/nodetype/NodeTypeConflictException.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/nodetype/NodeTypeConflictException.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * 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.nodetype;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * The <code>NodeTypeConflictException</code> ...
+ */
+public class NodeTypeConflictException extends RepositoryException {
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public NodeTypeConflictException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message and root cause.
+     *
+     * @param message   the detail message. The detail message is saved for
+     *                  later retrieval by the {@link #getMessage()} method.
+     * @param rootCause root failure cause
+     */
+    public NodeTypeConflictException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\nodetype\NodeTypeConflictException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/nodetype/InvalidConstraintException.java
===================================================================
--- src/main/java/org/apache/jackrabbit/nodetype/InvalidConstraintException.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/nodetype/InvalidConstraintException.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * 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.nodetype;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * The <code>InvalidConstraintException</code> ...
+ */
+public class InvalidConstraintException extends RepositoryException {
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public InvalidConstraintException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message and root cause.
+     *
+     * @param message   the detail message. The detail message is saved for
+     *                  later retrieval by the {@link #getMessage()} method.
+     * @param rootCause root failure cause
+     */
+    public InvalidConstraintException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\nodetype\InvalidConstraintException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/nodetype/InvalidNodeTypeDefException.java
===================================================================
--- src/main/java/org/apache/jackrabbit/nodetype/InvalidNodeTypeDefException.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/nodetype/InvalidNodeTypeDefException.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * 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.nodetype;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * The <code>NodeTypeConflictException</code> ...
+ */
+public class InvalidNodeTypeDefException extends RepositoryException {
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public InvalidNodeTypeDefException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message and root cause.
+     *
+     * @param message   the detail message. The detail message is saved for
+     *                  later retrieval by the {@link #getMessage()} method.
+     * @param rootCause root failure cause
+     */
+    public InvalidNodeTypeDefException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\nodetype\InvalidNodeTypeDefException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/lock/Locked.java
===================================================================
--- src/main/java/org/apache/jackrabbit/lock/Locked.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/lock/Locked.java	(revision 0)
@@ -0,0 +1,270 @@
+/*
+ * 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.lock;
+
+import org.apache.jackrabbit.namespace.SessionNamespaceResolver;
+import org.apache.jackrabbit.name.NameConstants;
+import org.apache.jackrabbit.conversion.NamePathResolver;
+import org.apache.jackrabbit.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.conversion.NameException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Session;
+import javax.jcr.Repository;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.Event;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+
+/**
+ * <code>Locked</code> is a utility to synchronize modifications on a lockable
+ * node. The modification is applied while the lock on the node is held, thus
+ * ensuring that the modification will never fail with an {@link
+ * javax.jcr.InvalidItemStateException}. This utility can be used with any
+ * JCR Repository, not just Jackrabbit.
+ * <p/>
+ * The following example shows how this utility can be used to implement
+ * a persistent counter:
+ * <pre>
+ * Node counter = ...;
+ * long nextValue = ((Long) new Locked() {
+ *     protected Object run(Node counter) throws RepositoryException {
+ *         Property seqProp = counter.getProperty("value");
+ *         long value = seqProp.getLong();
+ *         seqProp.setValue(++value);
+ *         seqProp.save();
+ *         return new Long(value);
+ *     }
+ * }.with(counter, false)).longValue();
+ * </pre>
+ * If you specify a <code>timeout</code> you need to check the return value
+ * whether the <code>run</code> method could be executed within the timeout
+ * period:
+ * <pre>
+ * Node counter = ...;
+ * Object ret = new Locked() {
+ *     protected Object run(Node counter) throws RepositoryException {
+ *         Property seqProp = counter.getProperty("value");
+ *         long value = seqProp.getLong();
+ *         seqProp.setValue(++value);
+ *         seqProp.save();
+ *         return new Long(value);
+ *     }
+ * }.with(counter, false);
+ * if (ret == Locked.TIMED_OUT) {
+ *     // do whatever you think is appropriate in this case
+ * } else {
+ *     // get the value
+ *     long nextValue = ((Long) ret).longValue();
+ * }
+ * </pre>
+ */
+public abstract class Locked {
+
+    /**
+     * Object returned when timeout is reached without being able to call
+     * {@link #run} while holding the lock.
+     */
+    public static final Object TIMED_OUT = new Object();
+
+    /**
+     * Executes {@link #run} while the lock on <code>lockable</code> is held.
+     * This method will block until {@link #run} is executed while holding the
+     * lock on node <code>lockable</code>.
+     *
+     * @param lockable a lockable node.
+     * @param isDeep   <code>true</code> if <code>lockable</code> will be locked
+     *                 deep.
+     * @return the object returned by {@link #run}.
+     * @throws IllegalArgumentException if <code>lockable</code> is not
+     *      <i>mix:lockable</i>.
+     * @throws RepositoryException  if {@link #run} throws an exception.
+     * @throws InterruptedException if this thread is interrupted while waiting
+     *                              for the lock on node <code>lockable</code>.
+     */
+    public Object with(Node lockable, boolean isDeep)
+            throws RepositoryException, InterruptedException {
+        return with(lockable, isDeep, Long.MAX_VALUE);
+    }
+
+    /**
+     * Executes the method {@link #run} within the scope of a lock held on
+     * <code>lockable</code>.
+     *
+     * @param lockable the node where the lock is obtained from.
+     * @param isDeep   <code>true</code> if <code>lockable</code> will be locked
+     *                 deep.
+     * @param timeout  time in milliseconds to wait at most to aquire the lock.
+     * @return the object returned by {@link #run} or {@link #TIMED_OUT} if the
+     *         lock on <code>lockable</code> could not be aquired within the
+     *         specified timeout.
+     * @throws IllegalArgumentException if <code>timeout</code> is negative or
+     *                                  <code>lockable</code> is not
+     *                                  <i>mix:lockable</i>.
+     * @throws RepositoryException      if {@link #run} throws an exception.
+     * @throws UnsupportedRepositoryOperationException
+     *                                  if this repository does not support
+     *                                  locking.
+     * @throws InterruptedException     if this thread is interrupted while
+     *                                  waiting for the lock on node
+     *                                  <code>lockable</code>.
+     */
+    public Object with(Node lockable, boolean isDeep, long timeout)
+            throws UnsupportedRepositoryOperationException, RepositoryException, InterruptedException {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("timeout must be >= 0");
+        }
+
+        Session session = lockable.getSession();
+        NamePathResolver resolver = new DefaultNamePathResolver(new SessionNamespaceResolver(session));
+
+        Lock lock;
+        EventListener listener = null;
+        try {
+            // check whether the lockable can be locked at all
+            if (!lockable.isNodeType(resolver.getJCRName(NameConstants.MIX_LOCKABLE))) {
+                throw new IllegalArgumentException("Node is not lockable");
+            }
+
+            lock = tryLock(lockable, isDeep);
+            if (lock != null) {
+                return runAndUnlock(lock);
+            }
+
+            if (timeout == 0) {
+                return TIMED_OUT;
+            }
+
+            long timelimit;
+            if (timeout == Long.MAX_VALUE) {
+                timelimit = Long.MAX_VALUE;
+            } else {
+                timelimit = System.currentTimeMillis() + timeout;
+            }
+
+            // node is locked by other session -> register event listener if possible
+            if (isObservationSupported(session)) {
+                ObservationManager om = session.getWorkspace().getObservationManager();
+                listener = new EventListener() {
+                    public void onEvent(EventIterator events) {
+                        synchronized (this) {
+                            this.notify();
+                        }
+                    }
+                };
+                om.addEventListener(listener, Event.PROPERTY_REMOVED,
+                        lockable.getPath(), false, null, null, true);
+            }
+
+            // now keep trying to aquire the lock
+            // using 'this' as a monitor allows the event listener to notify
+            // the current thread when the lockable node is possibly unlocked
+            for (; ;) {
+                synchronized (this) {
+                    lock = tryLock(lockable, isDeep);
+                    if (lock != null) {
+                        return runAndUnlock(lock);
+                    } else {
+                        // check timeout
+                        if (System.currentTimeMillis() > timelimit) {
+                            return TIMED_OUT;
+                        }
+                        if (listener != null) {
+                            // event listener *should* wake us up, however
+                            // there is a chance that removal of the lockOwner
+                            // property is notified before the node is acutally
+                            // unlocked. therefore we use a safety net to wait
+                            // at most 1000 millis.
+                            this.wait(Math.min(1000, timeout));
+                        } else {
+                            // repository does not support observation
+                            // wait at most 50 millis then retry
+                            this.wait(Math.min(50, timeout));
+                        }
+                    }
+                }
+            }
+        } catch (NameException e) {
+            throw new RepositoryException(e);
+        } finally {
+            if (listener != null) {
+                session.getWorkspace().getObservationManager().removeEventListener(listener);
+            }
+        }
+    }
+
+    /**
+     * This method is executed while holding the lock.
+     * @param node The <code>Node</code> on which the lock is placed.
+     * @return an object which is then returned by {@link #with with()}.
+     * @throws RepositoryException if an error occurs.
+     */
+    protected abstract Object run(Node node) throws RepositoryException;
+
+    /**
+     * Executes {@link #run} and unlocks the lockable node in any case, even
+     * when an exception is thrown.
+     *
+     * @param lock The <code>Lock</code> to unlock in any case before returning.
+     *
+     * @return the object returned by {@link #run}.
+     * @throws RepositoryException if an error occurs.
+     */
+    private Object runAndUnlock(Lock lock) throws RepositoryException {
+        try {
+            return run(lock.getNode());
+        } finally {
+            lock.getNode().unlock();
+        }
+    }
+
+    /**
+     * Tries to aquire a session scoped lock on <code>lockable</code>.
+     *
+     * @param lockable the lockable node
+     * @param isDeep   <code>true</code> if the lock should be deep
+     * @return The <code>Lock</code> or <code>null</code> if the
+     *         <code>lockable</code> cannot be locked.
+     * @throws UnsupportedRepositoryOperationException
+     *                             if this repository does not support locking.
+     * @throws RepositoryException if an error occurs
+     */
+    private static Lock tryLock(Node lockable, boolean isDeep)
+            throws UnsupportedRepositoryOperationException, RepositoryException {
+        try {
+            return lockable.lock(isDeep, true);
+        } catch (LockException e) {
+            // locked by some other session
+        }
+        return null;
+    }
+
+    /**
+     * Returns <code>true</code> if the repository supports observation.
+     *
+     * @param s a session of the repository.
+     * @return <code>true</code> if the repository supports observation.
+     */
+    private static boolean isObservationSupported(Session s) {
+        return "true".equalsIgnoreCase(s.getRepository().getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED));
+    }
+
+}

Property changes on: src\main\java\org\apache\jackrabbit\lock\Locked.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/spi/commons/QPropertyDefinitionImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/QPropertyDefinitionImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/QPropertyDefinitionImpl.java	(working copy)
@@ -18,7 +18,7 @@
 
 import org.apache.jackrabbit.spi.QPropertyDefinition;
 import org.apache.jackrabbit.spi.QValue;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
 
 import java.util.Arrays;
 
@@ -56,7 +56,7 @@
      * @param propDef some other property definition.
      */
     public QPropertyDefinitionImpl(QPropertyDefinition propDef) {
-        this(propDef.getQName(), propDef.getDeclaringNodeType(),
+        this(propDef.getName(), propDef.getDeclaringNodeType(),
                 propDef.isAutoCreated(), propDef.isMandatory(),
                 propDef.getOnParentVersion(), propDef.isProtected(),
                 propDef.getDefaultValues(), propDef.isMultiple(),
@@ -81,7 +81,7 @@
      * @throws NullPointerException if <code>valueConstraints</code> is
      *                              <code>null</code>.
      */
-    public QPropertyDefinitionImpl(QName name, QName declaringNodeType,
+    public QPropertyDefinitionImpl(Name name, Name declaringNodeType,
                                    boolean isAutoCreated, boolean isMandatory,
                                    int onParentVersion, boolean isProtected,
                                    QValue[] defaultValues, boolean isMultiple,
@@ -176,7 +176,7 @@
             if (definesResidual()) {
                 sb.append('*');
             } else {
-                sb.append(getQName().toString());
+                sb.append(getName().toString());
             }
             sb.append('/');
             sb.append(getRequiredType());
Index: src/main/java/org/apache/jackrabbit/spi/commons/ChildInfoImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/ChildInfoImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/ChildInfoImpl.java	(working copy)
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.spi.commons;
 
 import org.apache.jackrabbit.spi.ChildInfo;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
 
 import java.io.Serializable;
 
@@ -29,7 +29,7 @@
     /**
      * The name of this child info.
      */
-    private final QName name;
+    private final Name name;
 
     /**
      * The unique id for this child info or <code>null</code> if it does not
@@ -49,7 +49,7 @@
      * @param uniqueId the unique id of the child node or <code>null</code>.
      * @param index    the index of the child node.
      */
-    public ChildInfoImpl(QName name, String uniqueId, int index) {
+    public ChildInfoImpl(Name name, String uniqueId, int index) {
         this.name = name;
         this.uniqueId = uniqueId;
         this.index = index;
@@ -58,7 +58,7 @@
     /**
      * {@inheritDoc}
      */
-    public QName getName() {
+    public Name getName() {
         return name;
     }
 
Index: src/main/java/org/apache/jackrabbit/spi/commons/QNodeDefinitionImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/QNodeDefinitionImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/QNodeDefinitionImpl.java	(working copy)
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.spi.commons;
 
 import org.apache.jackrabbit.spi.QNodeDefinition;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
 
 import java.util.Arrays;
 import java.util.TreeSet;
@@ -25,19 +25,17 @@
 /**
  * <code>QNodeDefinitionImpl</code> implements a <code>QNodeDefinition</code>.
  */
-public class QNodeDefinitionImpl
-        extends QItemDefinitionImpl
-        implements QNodeDefinition {
+public class QNodeDefinitionImpl extends QItemDefinitionImpl implements QNodeDefinition {
 
     /**
      * The name of the default primary type.
      */
-    private final QName defaultPrimaryType;
+    private final Name defaultPrimaryType;
 
     /**
      * The names of the required primary types.
      */
-    private final QName[] requiredPrimaryTypes;
+    private final Name[] requiredPrimaryTypes;
 
     /**
      * The 'allowsSameNameSiblings' flag.
@@ -50,7 +48,7 @@
      * @param nodeDef some other node definition.
      */
     public QNodeDefinitionImpl(QNodeDefinition nodeDef) {
-        this(nodeDef.getQName(), nodeDef.getDeclaringNodeType(),
+        this(nodeDef.getName(), nodeDef.getDeclaringNodeType(),
                 nodeDef.isAutoCreated(), nodeDef.isMandatory(),
                 nodeDef.getOnParentVersion(), nodeDef.isProtected(),
                 nodeDef.getDefaultPrimaryType(),
@@ -68,11 +66,11 @@
      * @param onParentVersion   the on parent version behaviour.
      * @param isProtected       if this item is protected.
      */
-    public QNodeDefinitionImpl(QName name, QName declaringNodeType,
-                        boolean isAutoCreated, boolean isMandatory,
-                        int onParentVersion, boolean isProtected,
-                        QName defaultPrimaryType, QName[] requiredPrimaryTypes,
-                        boolean allowsSameNameSiblings) {
+    public QNodeDefinitionImpl(Name name, Name declaringNodeType,
+                               boolean isAutoCreated, boolean isMandatory,
+                               int onParentVersion, boolean isProtected,
+                               Name defaultPrimaryType, Name[] requiredPrimaryTypes,
+                               boolean allowsSameNameSiblings) {
         super(name, declaringNodeType, isAutoCreated, isMandatory,
                 onParentVersion, isProtected);
         this.defaultPrimaryType = defaultPrimaryType;
@@ -84,14 +82,14 @@
     /**
      * {@inheritDoc}
      */
-    public QName getDefaultPrimaryType() {
+    public Name getDefaultPrimaryType() {
         return defaultPrimaryType;
     }
 
     /**
      * {@inheritDoc}
      */
-    public QName[] getRequiredPrimaryTypes() {
+    public Name[] getRequiredPrimaryTypes() {
         return requiredPrimaryTypes;
     }
 
@@ -155,12 +153,12 @@
             if (definesResidual()) {
                 sb.append('*');
             } else {
-                sb.append(getQName().toString());
+                sb.append(getName().toString());
             }
             sb.append('/');
             // set of required node type names, sorted in ascending order
             TreeSet set = new TreeSet();
-            QName[] names = getRequiredPrimaryTypes();
+            Name[] names = getRequiredPrimaryTypes();
             for (int i = 0; i < names.length; i++) {
                 set.add(names[i]);
             }
Index: src/main/java/org/apache/jackrabbit/spi/commons/PropertyInfoImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/PropertyInfoImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/PropertyInfoImpl.java	(working copy)
@@ -21,8 +21,8 @@
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 
 import java.io.Serializable;
 
@@ -67,8 +67,8 @@
             parentId = idFactory.createNodeId(
                     parentId.getUniqueID(), parentId.getPath());
             PropertyId propId = idFactory.createPropertyId(
-                    parentId, propertyInfo.getId().getQName());
-            return new PropertyInfoImpl(parentId, propertyInfo.getQName(),
+                    parentId, propertyInfo.getId().getName());
+            return new PropertyInfoImpl(parentId, propertyInfo.getName(),
                     propertyInfo.getPath(), propId, propertyInfo.getType(),
                     propertyInfo.isMultiValued(), propertyInfo.getValues());
         }
@@ -85,7 +85,7 @@
      * @param isMultiValued whether this property is multi-valued.
      * @param values        the values.
      */
-    public PropertyInfoImpl(NodeId parentId, QName name, Path path,
+    public PropertyInfoImpl(NodeId parentId, Name name, Path path,
                             PropertyId id, int type, boolean isMultiValued,
                             QValue[] values) {
         super(parentId, name, path, false);
Index: src/main/java/org/apache/jackrabbit/spi/commons/EventImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/EventImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/EventImpl.java	(working copy)
@@ -19,8 +19,8 @@
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.NodeId;
-import org.apache.jackrabbit.name.Path;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 
 import java.io.Serializable;
 
@@ -54,12 +54,12 @@
     /**
      * The name of the primary node type of the 'associated' node of this event.
      */
-    private final QName primaryNodeTypeName;
+    private final Name primaryNodeTypeName;
 
     /**
      * The names of the mixin types of the 'associated' node of this event.
      */
-    private final QName[] mixinTypeNames;
+    private final Name[] mixinTypeNames;
 
     /**
      * The user ID connected with this event.
@@ -70,7 +70,7 @@
      * Creates a new serializable event.
      */
     public EventImpl(int type, Path path, ItemId itemId, NodeId parentId,
-                     QName primaryNodeTypeName, QName[] mixinTypeNames,
+                     Name primaryNodeTypeName, Name[] mixinTypeNames,
                      String userId) {
         this.type = type;
         this.path = path;
@@ -91,7 +91,7 @@
     /**
      * {@inheritDoc}
      */
-    public Path getQPath() {
+    public Path getPath() {
         return path;
     }
 
@@ -112,15 +112,15 @@
     /**
      * {@inheritDoc}
      */
-    public QName getPrimaryNodeTypeName() {
+    public Name getPrimaryNodeTypeName() {
         return primaryNodeTypeName;
     }
 
     /**
      * {@inheritDoc}
      */
-    public QName[] getMixinTypeNames() {
-        QName[] mixins = new QName[mixinTypeNames.length];
+    public Name[] getMixinTypeNames() {
+        Name[] mixins = new Name[mixinTypeNames.length];
         System.arraycopy(mixinTypeNames, 0, mixins, 0, mixinTypeNames.length);
         return mixins;
     }
Index: src/main/java/org/apache/jackrabbit/spi/commons/NodeInfoImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/NodeInfoImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/NodeInfoImpl.java	(working copy)
@@ -20,8 +20,8 @@
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.util.IteratorHelper;
 
 import java.util.List;
@@ -48,12 +48,12 @@
     /**
      * The name of the primary node type.
      */
-    private final QName primaryTypeName;
+    private final Name primaryTypeName;
 
     /**
      * The names of assigned mixins.
      */
-    private final QName[] mixinNames;
+    private final Name[] mixinNames;
 
     /**
      * The list of {@link PropertyId}s that reference this node info.
@@ -82,7 +82,7 @@
                 NodeId parentId = refs[i].getParentId();
                 parentId = idFactory.createNodeId(
                         parentId.getUniqueID(), parentId.getPath());
-                serRefs.add(idFactory.createPropertyId(parentId, refs[i].getQName()));
+                serRefs.add(idFactory.createPropertyId(parentId, refs[i].getName()));
             }
             NodeId parentId = null;
             if (nodeInfo.getParentId() != null) {
@@ -92,7 +92,7 @@
             }
             NodeId nodeId = nodeInfo.getId();
             nodeId = idFactory.createNodeId(nodeId.getUniqueID(), nodeId.getPath());
-            return new NodeInfoImpl(parentId, nodeInfo.getQName(),
+            return new NodeInfoImpl(parentId, nodeInfo.getName(),
                     nodeInfo.getPath(), nodeId,
                     nodeInfo.getIndex(), nodeInfo.getNodetype(),
                     nodeInfo.getMixins(), serRefs.iterator(),
@@ -103,7 +103,7 @@
                             idFactory.createNodeId(
                                     parentId.getUniqueID(), parentId.getPath());
                             return idFactory.createPropertyId(
-                                    parentId, propId.getQName());
+                                    parentId, propId.getName());
                         }
                     });
         }
@@ -123,8 +123,8 @@
      * @param references      the references to this node.
      * @param propertyIds     the properties of this node.
      */
-    public NodeInfoImpl(NodeId parentId, QName name, Path path, NodeId id,
-                         int index, QName primaryTypeName, QName[] mixinNames,
+    public NodeInfoImpl(NodeId parentId, Name name, Path path, NodeId id,
+                         int index, Name primaryTypeName, Name[] mixinNames,
                          Iterator references, Iterator propertyIds) {
         super(parentId, name, path, true);
         this.id = id;
@@ -160,15 +160,15 @@
     /**
      * {@inheritDoc}
      */
-    public QName getNodetype() {
+    public Name getNodetype() {
         return primaryTypeName;
     }
 
     /**
      * {@inheritDoc}
      */
-    public QName[] getMixins() {
-        QName[] ret = new QName[mixinNames.length];
+    public Name[] getMixins() {
+        Name[] ret = new Name[mixinNames.length];
         System.arraycopy(mixinNames, 0, ret, 0, mixinNames.length);
         return ret;
     }
Index: src/main/java/org/apache/jackrabbit/spi/commons/QNodeTypeDefinitionImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/QNodeTypeDefinitionImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/QNodeTypeDefinitionImpl.java	(working copy)
@@ -19,7 +19,9 @@
 import org.apache.jackrabbit.spi.QNodeTypeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
 import org.apache.jackrabbit.spi.QNodeDefinition;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.name.NameFactoryImpl;
 
 import javax.jcr.PropertyType;
 import java.util.Collection;
@@ -36,12 +38,12 @@
     /**
      * The name of the node definition.
      */
-    private final QName name;
+    private final Name name;
 
     /**
      * The names of the declared super types of this node type definition.
      */
-    private final QName[] supertypes;
+    private final Name[] supertypes;
 
     /**
      * Indicates whether this is a mixin node type definition.
@@ -56,7 +58,7 @@
     /**
      * The name of the primary item or <code>null</code> if none is defined.
      */
-    private final QName primaryItemName;
+    private final Name primaryItemName;
 
     /**
      * The list of property definitions.
@@ -69,7 +71,7 @@
     private final QNodeDefinition[] childNodeDefs;
 
     /**
-     * Unmodifiable collection of dependent node type <code>QName</code>s.
+     * Unmodifiable collection of dependent node type <code>Name</code>s.
      * @see #getDependencies()
      */
     private transient Collection dependencies;
@@ -80,7 +82,7 @@
      * @param nt the qualified node type definition.
      */
     public QNodeTypeDefinitionImpl(QNodeTypeDefinition nt) {
-        this(nt.getQName(), nt.getSupertypes(), nt.isMixin(),
+        this(nt.getName(), nt.getSupertypes(), nt.isMixin(),
                 nt.hasOrderableChildNodes(), nt.getPrimaryItemName(),
                 nt.getPropertyDefs(), nt.getChildNodeDefs());
     }
@@ -98,11 +100,11 @@
      * @param declaredPropDefs       the declared property definitions.
      * @param declaredNodeDefs       the declared child node definitions.
      */
-    public QNodeTypeDefinitionImpl(QName name,
-                                   QName[] supertypes,
+    public QNodeTypeDefinitionImpl(Name name,
+                                   Name[] supertypes,
                                    boolean isMixin,
                                    boolean hasOrderableChildNodes,
-                                   QName primaryItemName,
+                                   Name primaryItemName,
                                    QPropertyDefinition[] declaredPropDefs,
                                    QNodeDefinition[] declaredNodeDefs) {
         this.name = name;
@@ -117,15 +119,15 @@
     /**
      * {@inheritDoc}
      */
-    public QName getQName() {
+    public Name getName() {
         return name;
     }
 
     /**
      * {@inheritDoc}
      */
-    public QName[] getSupertypes() {
-        QName[] sTypes = new QName[supertypes.length];
+    public Name[] getSupertypes() {
+        Name[] sTypes = new Name[supertypes.length];
         System.arraycopy(supertypes, 0, sTypes, 0, supertypes.length);
         return sTypes;
     }
@@ -147,7 +149,7 @@
     /**
      * {@inheritDoc}
      */
-    public QName getPrimaryItemName() {
+    public Name getPrimaryItemName() {
         return primaryItemName;
     }
 
@@ -182,12 +184,12 @@
             // child node definitions
             for (int i = 0; i < childNodeDefs.length; i++) {
                 // default primary type
-                QName ntName = childNodeDefs[i].getDefaultPrimaryType();
+                Name ntName = childNodeDefs[i].getDefaultPrimaryType();
                 if (ntName != null && !name.equals(ntName)) {
                     deps.add(ntName);
                 }
                 // required primary type
-                QName[] ntNames = childNodeDefs[i].getRequiredPrimaryTypes();
+                Name[] ntNames = childNodeDefs[i].getRequiredPrimaryTypes();
                 for (int j = 0; j < ntNames.length; j++) {
                     if (ntNames[j] != null && !name.equals(ntNames[j])) {
                         deps.add(ntNames[j]);
@@ -201,7 +203,8 @@
                     String[] ca = propertyDefs[i].getValueConstraints();
                     if (ca != null) {
                         for (int j = 0; j < ca.length; j++) {
-                            QName ntName = QName.valueOf(ca[j]);
+                            NameFactory factory = NameFactoryImpl.getInstance();
+                            Name ntName = factory.create(ca[j]);
                             if (!name.equals(ntName)) {
                                 deps.add(ntName);
                             }
Index: src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/SerializableBatch.java	(working copy)
@@ -21,7 +21,7 @@
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.ItemId;
-import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.Name;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.ValueFormatException;
@@ -83,19 +83,19 @@
     //----------------------------< Batch >-------------------------------------
 
     public void addNode(NodeId parentId,
-                        QName nodeName,
-                        QName nodetypeName,
+                        Name nodeName,
+                        Name nodetypeName,
                         String uuid) {
         recording.add(new AddNode(parentId, nodeName, nodetypeName, uuid));
     }
 
-    public void addProperty(NodeId parentId, QName propertyName, QValue value) {
+    public void addProperty(NodeId parentId, Name propertyName, QValue value) {
         recording.add(new AddProperty(parentId, propertyName,
                 new QValue[]{value}, false));
     }
 
     public void addProperty(NodeId parentId,
-                            QName propertyName,
+                            Name propertyName,
                             QValue[] values) {
         recording.add(new AddProperty(parentId, propertyName, values, true));
     }
@@ -118,13 +118,13 @@
         recording.add(new ReorderNodes(parentId, srcNodeId, beforeNodeId));
     }
 
-    public void setMixins(NodeId nodeId, QName[] mixinNodeTypeIds) {
+    public void setMixins(NodeId nodeId, Name[] mixinNodeTypeIds) {
         recording.add(new SetMixins(nodeId, mixinNodeTypeIds));
     }
 
     public void move(NodeId srcNodeId,
                      NodeId destParentNodeId,
-                     QName destName) {
+                     Name destName) {
         recording.add(new Move(srcNodeId, destParentNodeId, destName));
     }
 
@@ -146,13 +146,13 @@
 
         private final NodeId parentId;
 
-        private final QName nodeName;
+        private final Name nodeName;
 
-        private final QName nodetypeName;
+        private final Name nodetypeName;
 
         private final String uuid;
 
-        AddNode(NodeId parentId, QName nodeName, QName nodetypeName, String uuid) {
+        AddNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) {
             this.parentId = parentId;
             this.nodeName = nodeName;
             this.nodetypeName = nodetypeName;
@@ -171,13 +171,13 @@
 
         private final NodeId parentId;
 
-        private final QName propertyName;
+        private final Name propertyName;
 
         private final QValue[] values;
 
         private final boolean isMultiValued;
 
-        AddProperty(NodeId parentId, QName propertyName,
+        AddProperty(NodeId parentId, Name propertyName,
                     QValue[] values, boolean isMultiValued) {
             this.parentId = parentId;
             this.propertyName = propertyName;
@@ -265,9 +265,9 @@
 
         private final NodeId nodeId;
 
-        private final QName[] mixinNodeTypeIds;
+        private final Name[] mixinNodeTypeIds;
 
-        SetMixins(NodeId nodeId, QName[] mixinNodeTypeIds) {
+        SetMixins(NodeId nodeId, Name[] mixinNodeTypeIds) {
             this.nodeId = nodeId;
             this.mixinNodeTypeIds = mixinNodeTypeIds;
         }
@@ -286,9 +286,9 @@
 
         private final NodeId destParentNodeId;
 
-        private final QName destName;
+        private final Name destName;
 
-        Move(NodeId srcNodeId, NodeId destParentNodeId, QName destName) {
+        Move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) {
             this.srcNodeId = srcNodeId;
             this.destParentNodeId = destParentNodeId;
             this.destName = destName;
Index: src/main/java/org/apache/jackrabbit/spi/commons/EventFilterImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/EventFilterImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/EventFilterImpl.java	(working copy)
@@ -19,10 +19,9 @@
 import org.apache.jackrabbit.spi.EventFilter;
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.NodeId;
-import org.apache.jackrabbit.name.Path;
-import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.spi.Path;
 
-import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
 import java.util.Set;
 import java.util.HashSet;
 import java.util.Arrays;
@@ -54,7 +53,7 @@
      * @param absPath       filter events that are below this path.
      * @param isDeep        whether this filter is applied deep.
      * @param uuids         the jcr:uuid of the nodes this filter allows.
-     * @param nodeTypeNames the QNames of the already resolved node types this
+     * @param nodeTypeNames the Names of the already resolved node types this
      *                      filter allows.
      * @param noLocal       whether this filter accepts local events or not.
      */
@@ -120,9 +119,9 @@
             // node where the property belongs to.
             Path eventPath;
             if (type == Event.NODE_ADDED || type == Event.NODE_REMOVED) {
-                eventPath = event.getQPath();
+                eventPath = event.getPath();
             } else {
-                eventPath = event.getQPath().getAncestor(1);
+                eventPath = event.getPath().getAncestor(1);
             }
 
             boolean match = eventPath.equals(absPath);
@@ -130,10 +129,8 @@
                 match = eventPath.isDescendantOf(absPath);
             }
             return match;
-        } catch (MalformedPathException e) {
+        } catch (RepositoryException e) {
             // should never get here
-        } catch (PathNotFoundException e) {
-            // should never get here
         }
         // if we get here an exception occurred while checking for the path
         return false;
Index: src/main/java/org/apache/jackrabbit/spi/commons/QItemDefinitionImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/QItemDefinitionImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/QItemDefinitionImpl.java	(working copy)
@@ -16,10 +16,11 @@
  */
 package org.apache.jackrabbit.spi.commons;
 
-import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.spi.QItemDefinition;
 import org.apache.jackrabbit.spi.QNodeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.name.NameFactoryImpl;
 
 import java.io.Serializable;
 
@@ -32,18 +33,19 @@
 
     /**
      * The special wildcard name used as the name of residual item definitions.
+     * TODO don't rely on specific factory impl
      */
-    public static final QName ANY_NAME = new QName("", "*");
+    public static final Name ANY_NAME = NameFactoryImpl.getInstance().create("", "*");
 
     /**
      * The name of the child item.
      */
-    private final QName name;
+    private final Name name;
 
     /**
      * The name of the declaring node type.
      */
-    private final QName declaringNodeType;
+    private final Name declaringNodeType;
 
     /**
      * The 'autoCreated' flag.
@@ -80,7 +82,7 @@
      * @param onParentVersion   the on parent version behaviour.
      * @param isProtected       if this item is protected.
      */
-    QItemDefinitionImpl(QName name, QName declaringNodeType,
+    QItemDefinitionImpl(Name name, Name declaringNodeType,
                         boolean isAutoCreated, boolean isMandatory,
                         int onParentVersion, boolean isProtected) {
         this.name = name;
@@ -95,14 +97,14 @@
     /**
      * {@inheritDoc}
      */
-    public QName getDeclaringNodeType() {
+    public Name getDeclaringNodeType() {
         return declaringNodeType;
     }
 
     /**
      * {@inheritDoc}
      */
-    public QName getQName() {
+    public Name getName() {
         return name;
     }
 
@@ -138,7 +140,7 @@
      * {@inheritDoc}
      */
     public boolean definesResidual() {
-        return name.equals(ANY_NAME);
+        return Name.NS_DEFAULT_URI.equals(name.getNamespaceURI()) && "*".equals(name.getLocalName());
     }
 
     //-------------------------------------------< java.lang.Object overrides >
@@ -161,7 +163,7 @@
             return (declaringNodeType == null
                     ? other.getDeclaringNodeType() == null
                     : declaringNodeType.equals(other.getDeclaringNodeType()))
-                    && (name == null ? other.getQName() == null : name.equals(other.getQName()))
+                    && (name == null ? other.getName() == null : name.equals(other.getName()))
                     && autoCreated == other.isAutoCreated()
                     && onParentVersion == other.getOnParentVersion()
                     && writeProtected == other.isProtected()
Index: src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/spi/commons/ItemInfoImpl.java	(working copy)
@@ -18,8 +18,8 @@
 
 import org.apache.jackrabbit.spi.ItemInfo;
 import org.apache.jackrabbit.spi.NodeId;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 
 import java.io.Serializable;
 
@@ -38,7 +38,7 @@
     /**
      * The name of this item info.
      */
-    private final QName name;
+    private final Name name;
 
     /**
      * The path of this item info.
@@ -59,7 +59,7 @@
      * @param path     the path to this item.
      * @param isNode   if this item is a node.
      */
-    public ItemInfoImpl(NodeId parentId, QName name, Path path, boolean isNode) {
+    public ItemInfoImpl(NodeId parentId, Name name, Path path, boolean isNode) {
         this.parentId = parentId;
         this.name = name;
         this.path = path;
@@ -76,7 +76,7 @@
     /**
      * {@inheritDoc}
      */
-    public QName getQName() {
+    public Name getName() {
         return name;
     }
 
Index: src/main/java/org/apache/jackrabbit/value/ValueFactoryImplEx.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/ValueFactoryImplEx.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/value/ValueFactoryImplEx.java	(working copy)
@@ -1,104 +0,0 @@
-/*
- * 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.value;
-
-import javax.jcr.ValueFactory;
-import javax.jcr.Value;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.ValueFormatException;
-import javax.jcr.PropertyType;
-
-/**
- * <code>ValueFactoryImplEx</code> extends the implementation from the
- * jcr-commons module and allows a less restricted UUID format.
- */
-public class ValueFactoryImplEx extends ValueFactoryImpl {
-
-    private static final ValueFactory INSTANCE = new ValueFactoryImplEx();
-
-    /**
-     * Constructs a <code>ValueFactory</code> object.
-     */
-    private ValueFactoryImplEx() {
-    }
-
-    public static ValueFactory getInstance() {
-        return INSTANCE;
-    }
-
-    public Value createValue(Node value) throws RepositoryException {
-        return new ReferenceValue(value);
-    }
-
-    public Value createValue(String value, int type) throws ValueFormatException {
-        Value val;
-        switch (type) {
-            case PropertyType.REFERENCE:
-                val = ReferenceValue.valueOf(value);
-                break;
-            default:
-                val = super.createValue(value, type);
-        }
-        return val;
-    }
-
-    /**
-     * A <code>ReferenceValue</code> provides an implementation
-     * of the <code>Value</code> interface representing a <code>REFERENCE</code> value
-     * (a jcr:uuid property of an existing, referenceable node).
-     */
-    private static class ReferenceValue extends org.apache.jackrabbit.value.ReferenceValue {
-
-        private ReferenceValue(String uuid) {
-            super(uuid);
-        }
-
-        /**
-         * Constructs a <code>ReferenceValue</code> object representing the UUID of
-         * an existing node.
-         *
-         * @param target the node to be referenced
-         * @throws IllegalArgumentException If <code>target</code> is nonreferenceable.
-         * @throws RepositoryException      If another error occurs.
-         */
-        private ReferenceValue(Node target) throws RepositoryException {
-            super(target);
-        }
-
-        /**
-         * Returns a new <code>ReferenceValue</code> initialized to the value
-         * represented by the specified <code>String</code>.
-         * <p/>
-         * The specified <code>String</code> must denote the jcr:uuid property
-         * of an existing node.
-         *
-         * @param s the string to be parsed.
-         * @return a newly constructed <code>ReferenceValue</code> representing
-         * the specified value.
-         * @throws ValueFormatException If the <code>String</code> is
-         * null or empty String.
-         */
-        public static org.apache.jackrabbit.value.ReferenceValue valueOf(String s) throws ValueFormatException {
-            if (s != null && !"".equals(s)) {
-                return new ReferenceValue(s);
-            } else {
-                throw new ValueFormatException("Invalid format for jcr:uuid");
-            }
-        }
-    }
-}
Index: src/main/java/org/apache/jackrabbit/value/ValueFormat.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/ValueFormat.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/value/ValueFormat.java	(working copy)
@@ -16,13 +16,10 @@
  */
 package org.apache.jackrabbit.value;
 
-import org.apache.jackrabbit.name.NamespaceResolver;
-import org.apache.jackrabbit.name.NameException;
-import org.apache.jackrabbit.name.NoPrefixDeclaredException;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.Path;
-import org.apache.jackrabbit.name.PathFormat;
-import org.apache.jackrabbit.name.NameFormat;
+import org.apache.jackrabbit.conversion.NamePathResolver;
+import org.apache.jackrabbit.conversion.NameException;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.QValueFactory;
 
@@ -42,12 +39,12 @@
     /**
      *
      * @param jcrValue
-     * @param nsResolver
+     * @param resolver
      * @param factory
      * @return
      * @throws RepositoryException
      */
-    public static QValue getQValue(Value jcrValue, NamespaceResolver nsResolver,
+    public static QValue getQValue(Value jcrValue, NamePathResolver resolver,
                                    QValueFactory factory) throws RepositoryException {
         if (jcrValue == null) {
             throw new IllegalArgumentException("null value");
@@ -61,20 +58,20 @@
         } else if (jcrValue.getType() == PropertyType.DATE) {
             return factory.create(jcrValue.getDate());
         } else {
-            return getQValue(jcrValue.getString(), jcrValue.getType(), nsResolver, factory);
+            return getQValue(jcrValue.getString(), jcrValue.getType(), resolver, factory);
         }
     }
 
     /**
      *
      * @param jcrValues
-     * @param nsResolver
+     * @param resolver
      * @param factory
      * @return
      * @throws RepositoryException
      */
     public static QValue[] getQValues(Value[] jcrValues,
-                                      NamespaceResolver nsResolver,
+                                      NamePathResolver resolver,
                                       QValueFactory factory) throws RepositoryException {
         if (jcrValues == null) {
             throw new IllegalArgumentException("null value");
@@ -82,7 +79,7 @@
         List qValues = new ArrayList();
         for (int i = 0; i < jcrValues.length; i++) {
             if (jcrValues[i] != null) {
-                qValues.add(getQValue(jcrValues[i], nsResolver, factory));
+                qValues.add(getQValue(jcrValues[i], resolver, factory));
             }
         }
         return (QValue[]) qValues.toArray(new QValue[qValues.size()]);
@@ -92,13 +89,13 @@
      * 
      * @param jcrValue
      * @param propertyType
-     * @param nsResolver
+     * @param resolver
      * @param factory
      * @return
      * @throws RepositoryException
      */
     public static QValue getQValue(String jcrValue, int propertyType,
-                                   NamespaceResolver nsResolver,
+                                   NamePathResolver resolver,
                                    QValueFactory factory) throws RepositoryException {
         QValue qValue;
         switch (propertyType) {
@@ -115,7 +112,7 @@
                 break;
             case PropertyType.NAME:
                 try {
-                    QName qName = NameFormat.parse(jcrValue, nsResolver);
+                    Name qName = resolver.getQName(jcrValue);
                     qValue = factory.create(qName);
                 } catch (NameException e) {
                     throw new RepositoryException(e);
@@ -123,7 +120,7 @@
                 break;
             case PropertyType.PATH:
                 try {
-                    Path qPath = PathFormat.parse(jcrValue, nsResolver).getNormalizedPath();
+                    Path qPath = resolver.getQPath(jcrValue).getNormalizedPath();
                     qValue = factory.create(qPath);
                 } catch (NameException e) {
                     throw new RepositoryException(e);
@@ -136,12 +133,13 @@
     }
 
     /**
-     * @param nsResolver
+     * @param qualifiedValue
+     * @param resolver
      * @return
      * @throws RepositoryException
      */
     public static Value getJCRValue(QValue qualifiedValue,
-                                    NamespaceResolver nsResolver,
+                                    NamePathResolver resolver,
                                     ValueFactory factory) throws RepositoryException {
         Value jcrValue;
         int propertyType = qualifiedValue.getType();
@@ -154,22 +152,12 @@
                 jcrValue = factory.createValue(qualifiedValue.getString(), propertyType);
                 break;
             case PropertyType.PATH:
-                try {
-                    Path qPath = qualifiedValue.getPath();
-                    jcrValue = factory.createValue(PathFormat.format(qPath, nsResolver), propertyType);
-                } catch (NoPrefixDeclaredException npde) {
-                    // should never get here...
-                    throw new RepositoryException("internal error: encountered unregistered namespace", npde);
-                }
+                Path qPath = qualifiedValue.getPath();
+                jcrValue = factory.createValue(resolver.getJCRPath(qPath), propertyType);
                 break;
             case PropertyType.NAME:
-                try {
-                    QName qName = qualifiedValue.getQName();
-                    jcrValue = factory.createValue(NameFormat.format(qName, nsResolver), propertyType);
-                } catch (NoPrefixDeclaredException npde) {
-                    // should never get here...
-                    throw new RepositoryException("internal error: encountered unregistered namespace", npde);
-                }
+                Name qName = qualifiedValue.getName();
+                jcrValue = factory.createValue(resolver.getJCRName(qName), propertyType);
                 break;
             case PropertyType.BINARY:
                 jcrValue = factory.createValue(qualifiedValue.getStream());
Index: src/main/java/org/apache/jackrabbit/value/QValueFactoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/QValueFactoryImpl.java	(revision 582823)
+++ src/main/java/org/apache/jackrabbit/value/QValueFactoryImpl.java	(working copy)
@@ -16,10 +16,14 @@
  */
 package org.apache.jackrabbit.value;
 
+import org.apache.jackrabbit.name.PathFactoryImpl;
+import org.apache.jackrabbit.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.QValueFactory;
-import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.NameFactory;
 import org.apache.jackrabbit.util.TransientFileFactory;
 import org.apache.jackrabbit.util.ISO8601;
 
@@ -47,13 +51,16 @@
  */
 public final class QValueFactoryImpl implements QValueFactory {
 
-    private static final QValueFactory instance = new QValueFactoryImpl();
+    private static final QValueFactory INSTANCE = new QValueFactoryImpl();
 
+    private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+    private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+
     private QValueFactoryImpl() {
     }
 
     public static QValueFactory getInstance() {
-        return instance;
+        return INSTANCE;
     }
 
     //------------------------------------------------------< QValueFactory >---
@@ -74,9 +81,9 @@
             case PropertyType.LONG:
                 return new QValueImpl(Long.valueOf(value));
             case PropertyType.PATH:
-                return new QValueImpl(Path.valueOf(value));
+                return new QValueImpl(PATH_FACTORY.create(value));
             case PropertyType.NAME:
-                return new QValueImpl(QName.valueOf(value));
+                return new QValueImpl(NAME_FACTORY.create(value));
             case PropertyType.STRING:
             case PropertyType.REFERENCE:
                 return new QValueImpl(value, type);
@@ -99,9 +106,9 @@
     }
 
     /**
-     * @see QValueFactory#create(QName)
+     * @see QValueFactory#create(Name)
      */
-    public QValue create(QName value) {
+    public QValue create(Name value) {
         if (value == null) {
             throw new IllegalArgumentException("Cannot create QValue from null value.");
         }
@@ -191,7 +198,7 @@
             type = PropertyType.BOOLEAN;
         }
 
-        private QValueImpl(QName value) {
+        private QValueImpl(Name value) {
             val = value;
             type = PropertyType.NAME;
         }
@@ -236,13 +243,13 @@
         }
 
         /**
-         * @see QValue#getQName()
+         * @see QValue#getName()
          */
-        public QName getQName() throws RepositoryException {
+        public Name getName() throws RepositoryException {
             if (type == PropertyType.NAME) {
-                return (QName) val;
+                return (Name) val;
             } else {
-                return QName.valueOf(getString());
+                return NAME_FACTORY.create(getString());
             }
         }
 
@@ -264,7 +271,7 @@
             if (type == PropertyType.PATH) {
                 return (Path) val;
             } else {
-                return Path.valueOf(getString());
+                return PATH_FACTORY.create(getString());
             }
         }
 
@@ -592,9 +599,9 @@
         }
 
         /**
-         * @see QValue#getQName()
+         * @see QValue#getName()
          */
-        public QName getQName() throws RepositoryException {
+        public Name getName() throws RepositoryException {
             throw new UnsupportedOperationException();
         }
 
Index: src/main/java/org/apache/jackrabbit/namespace/AbstractNamespaceResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/AbstractNamespaceResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/AbstractNamespaceResolver.java	(revision 0)
@@ -0,0 +1,178 @@
+/*
+ * 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.namespace;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * Provides default implementations for the methods:
+ * <ul>
+ * <li>{@link #addListener(NamespaceListener)}</li>
+ * <li>{@link #removeListener(NamespaceListener)}</li>
+ * </ul>
+ * Subclasses may overwrite those methods with more efficient implementations
+ * e.g. using caching. This class also adds optional support for
+ * {@link NamespaceListener}s. To enable listener support call the constructor
+ * with <code>supportListeners</code> set to <code>true</code>. The default
+ * constructor will not enable listener support and all listener related
+ * methods will throw an {@link UnsupportedOperationException} in that case.
+ */
+public abstract class AbstractNamespaceResolver implements NamespaceResolver {
+
+    private final Set listeners;
+
+    /**
+     * Creates a <code>AbstractNamespaceResolver</code> without listener
+     * support.
+     */
+    public AbstractNamespaceResolver() {
+        this(false);
+    }
+
+    /**
+     * Creates a <code>AbstractNamespaceResolver</code> with listener support if
+     * <code>supportListeners</code> is set to <code>true</code>.
+     *
+     * @param supportListeners if <code>true</code> listener are supported by
+     *                         this instance.
+     */
+    public AbstractNamespaceResolver(boolean supportListeners) {
+        if (supportListeners) {
+            listeners = new HashSet();
+        } else {
+            listeners = null;
+        }
+    }
+
+    //--------------------------------------------< NamespaceListener support >
+
+    /**
+     * Registers <code>listener</code> to get notifications when namespace
+     * mappings change.
+     *
+     * @param listener the listener to register.
+     * @throws UnsupportedOperationException if listener support is not enabled
+     *                                       for this <code>AbstractNamespaceResolver</code>.
+     */
+    public void addListener(NamespaceListener listener) {
+        if (listeners == null) {
+            throw new UnsupportedOperationException("addListener");
+        }
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+    }
+
+    /**
+     * Removes the <code>listener</code> from this <code>NamespaceRegistery</code>.
+     *
+     * @param listener the listener to remove.
+     * @throws UnsupportedOperationException if listener support is not enabled
+     *                                       for this <code>AbstractNamespaceResolver</code>.
+     */
+    public void removeListener(NamespaceListener listener) {
+        if (listeners == null) {
+            throw new UnsupportedOperationException("removeListener");
+        }
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+    }
+
+    /**
+     * Notifies the listeners that a new namespace <code>uri</code> has been
+     * added and mapped to <code>prefix</code>.
+     *
+     * @param prefix the prefix.
+     * @param uri    the namespace uri.
+     */
+    protected void notifyNamespaceAdded(String prefix, String uri) {
+        if (listeners == null) {
+            throw new UnsupportedOperationException("notifyNamespaceAdded");
+        }
+        // addition is infrequent compared to listener registration
+        // -> use copy-on-read
+        NamespaceListener[] currentListeners;
+        synchronized (listeners) {
+            int i = 0;
+            currentListeners = new NamespaceListener[listeners.size()];
+            for (Iterator it = listeners.iterator(); it.hasNext();) {
+                currentListeners[i++] = (NamespaceListener) it.next();
+            }
+        }
+        for (int i = 0; i < currentListeners.length; i++) {
+            currentListeners[i].namespaceAdded(prefix, uri);
+        }
+    }
+
+    /**
+     * Notifies listeners that an existing namespace uri has been remapped
+     * to a new prefix.
+     *
+     * @param oldPrefix the old prefix.
+     * @param newPrefix the new prefix.
+     * @param uri the associated namespace uri.
+     */
+    protected void notifyNamespaceRemapped(String oldPrefix,
+                                           String newPrefix,
+                                           String uri) {
+        if (listeners == null) {
+            throw new UnsupportedOperationException("notifyNamespaceRemapped");
+        }
+        // remapping is infrequent compared to listener registration
+        // -> use copy-on-read
+        NamespaceListener[] currentListeners;
+        synchronized (listeners) {
+            int i = 0;
+            currentListeners = new NamespaceListener[listeners.size()];
+            for (Iterator it = listeners.iterator(); it.hasNext();) {
+                currentListeners[i++] = (NamespaceListener) it.next();
+            }
+        }
+        for (int i = 0; i < currentListeners.length; i++) {
+            currentListeners[i].namespaceRemapped(oldPrefix, newPrefix, uri);
+        }
+    }
+
+    /**
+     * Notifies the listeners that the namespace with the given <code>uri</code>
+     * has been removed from the mapping.
+     *
+     * @param uri the namespace uri.
+     * @see NamespaceListener#namespaceRemoved(String)
+     */
+    protected void notifyNamespaceRemoved(String uri) {
+        if (listeners == null) {
+            throw new UnsupportedOperationException("notifyNamespaceRemapped");
+        }
+        // removal is infrequent compared to listener registration
+        // -> use copy-on-read
+        NamespaceListener[] currentListeners;
+        synchronized (listeners) {
+            int i = 0;
+            currentListeners = new NamespaceListener[listeners.size()];
+            for (Iterator it = listeners.iterator(); it.hasNext();) {
+                currentListeners[i++] = (NamespaceListener) it.next();
+            }
+        }
+        for (int i = 0; i < currentListeners.length; i++) {
+            currentListeners[i].namespaceRemoved(uri);
+        }
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\AbstractNamespaceResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/namespace/NamespaceListener.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/NamespaceListener.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/NamespaceListener.java	(revision 0)
@@ -0,0 +1,50 @@
+/*
+ * 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.namespace;
+
+/**
+ * Receives notifications when a namespace mapping changes.
+ */
+public interface NamespaceListener {
+
+    /**
+     * Notifies the listeners that an existing namespace <code>uri</code> has
+     * been re-mapped from <code>oldPrefix</code> to <code>newPrefix</code>.
+     *
+     * @param oldPrefix the old prefix.
+     * @param newPrefix the new prefix.
+     * @param uri       the associated namespace uri.
+     */
+    public void namespaceRemapped(String oldPrefix, String newPrefix, String uri);
+
+    /**
+     * Notifies the listeners that a new namespace <code>uri</code> has been
+     * added and mapped to <code>prefix</code>.
+     *
+     * @param prefix the prefix.
+     * @param uri    the namespace uri.
+     */
+    public void namespaceAdded(String prefix, String uri);
+
+    /**
+     * Notifies the listeners that the namespace with the given uri has been
+     * unregistered.
+     *
+     * @param uri    the namespace uri.
+     */
+    public void namespaceRemoved(String uri);
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\NamespaceListener.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/namespace/SessionNamespaceResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/SessionNamespaceResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/SessionNamespaceResolver.java	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * 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.namespace;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+/**
+ * helper class that exposes the <code>NamespaceResolver</code>
+ * interface on a <code>Session</code>.
+ */
+public class SessionNamespaceResolver extends AbstractNamespaceResolver {
+
+    /**
+     * the session for the namespace lookups
+     */
+    private final Session session;
+
+    /**
+     * Creates a new namespace resolver based on a session
+     * @param session
+     */
+    public SessionNamespaceResolver(Session session) {
+        this.session = session;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPrefix(String uri) throws NamespaceException {
+        try {
+            return session.getNamespacePrefix(uri);
+        } catch (RepositoryException e) {
+            // should never get here...
+            throw new NamespaceException("internal error: failed to resolve namespace uri", e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        try {
+            return session.getNamespaceURI(prefix);
+        } catch (RepositoryException e) {
+            // should never get here...
+            throw new NamespaceException("internal error: failed to resolve namespace prefix", e);
+        }
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\SessionNamespaceResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/namespace/NamespaceMapping.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/NamespaceMapping.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/NamespaceMapping.java	(revision 0)
@@ -0,0 +1,165 @@
+/*
+ * 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.namespace;
+
+import javax.jcr.NamespaceException;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.Iterator;
+
+/**
+ * A Simple Namespace Mapping table. Mappings can be added
+ * and then the object can be used as a NamespaceResolver. Additionally, it can
+ * be based on a underlying NamespaceResolver
+ */
+public class NamespaceMapping extends AbstractNamespaceResolver {
+
+    /** local uris */
+    private final Properties prefixToURI = new Properties();
+
+    /** local prefix */
+    private final Properties URIToPrefix = new Properties();
+
+    /** base */
+    private final NamespaceResolver base;
+
+    public NamespaceMapping() {
+        this.base = null;
+    }
+
+    /**
+     * Constructor
+     */
+    public NamespaceMapping(NamespaceResolver base) {
+        this.base = base;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPrefix(String uri) throws NamespaceException {
+        if (URIToPrefix.containsKey(uri)) {
+            return URIToPrefix.getProperty(uri);
+        } else if (base == null) {
+            throw new NamespaceException("No prefix for URI '" + uri + "' declared.");
+        } else {
+            return base.getPrefix(uri);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        if (prefixToURI.containsKey(prefix)) {
+            return prefixToURI.getProperty(prefix);
+        } else if (base == null) {
+            throw new NamespaceException("No URI for pefix '" + prefix + "' declared.");
+        } else {
+            return base.getURI(prefix);
+        }
+    }
+
+    /**
+     * Returns true if prefix is already mapped to some URI. Returns false otherwise.
+     */
+    public boolean hasPrefix(String prefix) {
+        return prefixToURI.containsKey(prefix);
+    }
+
+    /**
+     * Set a prefix == URI one-to-one mapping
+     *
+     * @param prefix
+     * @param uri
+     * @throws NamespaceException
+     */
+    public void setMapping(String prefix, String uri) throws NamespaceException {
+        if (prefix == null) {
+            throw new NamespaceException("Prefix must not be null");
+        }
+        if (uri == null) {
+            throw new NamespaceException("URI must not be null");
+        }
+        if (URIToPrefix.containsKey(uri)) {
+            // remove mapping
+            prefixToURI.remove(URIToPrefix.remove(uri));
+        }
+        if (prefixToURI.containsKey(prefix)) {
+            // remove mapping
+            URIToPrefix.remove(prefixToURI.remove(prefix));
+        }
+        prefixToURI.put(prefix, uri);
+        URIToPrefix.put(uri, prefix);
+    }
+
+    /**
+     * Return a Map of prefix to URI mappings currently registered.
+     * The returned Map is a copy of the internal Map.
+     * @return Map
+     */
+    public Map getPrefixToURIMapping() {
+        return new HashMap(prefixToURI);
+    }
+
+    /**
+     * Return a Map of URI to prefix mappings currently registered.
+     * The returned Map is a copy of the internal Map.
+     * @return Map
+     */
+    public Map getURIToPrefixMapping() {
+        return new HashMap(URIToPrefix);
+    }
+
+     /**
+     * Override equals()
+      *
+     * @param obj
+     * @return boolean
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NamespaceMapping) {
+            NamespaceMapping other = (NamespaceMapping) obj;
+            return this.getPrefixToURIMapping().equals(other.getPrefixToURIMapping())
+                   && this.getURIToPrefixMapping().equals(other.getURIToPrefixMapping());
+        }
+        return false;
+    }
+
+    /**
+     * Override toString()
+     *
+     * @return String
+     */
+    public String toString() {
+        String s = "";
+        Set mapping = prefixToURI.entrySet();
+        for (Iterator i = mapping.iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            String prefix = (String) entry.getKey();
+            String uri = (String) entry.getValue();
+            s += "'" + prefix + "' == '" + uri + "'\n";
+        }
+        return s;
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\NamespaceMapping.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/namespace/NamespaceExtractor.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/NamespaceExtractor.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/NamespaceExtractor.java	(revision 0)
@@ -0,0 +1,109 @@
+/*
+ * 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.namespace;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.XMLReader;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.XMLReaderFactory;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.jcr.NamespaceException;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.FileInputStream;
+
+/**
+ * Extracts namespace mapping information from an XML file.
+ * XML file is parsed and all startPrefixMapping events
+ * are intercepted. Scoping of prefix mapping within the XML file
+ * may result in multiple namespace using the same prefix. This
+ * is handled by mangling the prefix when required.
+ *
+ * The resulting NamespaceMapping implements NamespaceResolver
+ * and can be used by tools (such as o.a.j.tools.nodetype.CompactNodeTypeDefWriter)
+ * to resolve namespaces.
+ */
+public class NamespaceExtractor {
+    private static Logger log = LoggerFactory.getLogger(NamespaceExtractor.class);
+    private final NamespaceMapping mapping = new NamespaceMapping();
+    private final Map basePrefixes = new HashMap();
+    private String defaultBasePrefix;
+
+    /**
+     * Constructor
+     * @param fileName
+     * @param dpb
+     * @throws NamespaceException
+     */
+    public NamespaceExtractor(String fileName, String dpb) throws NamespaceException {
+        defaultBasePrefix = dpb;
+        try{
+            ContentHandler handler = new NamespaceExtractor.NamespaceHandler();
+            XMLReader parser = XMLReaderFactory.createXMLReader();
+            parser.setContentHandler(handler);
+            parser.parse(new InputSource(new FileInputStream(fileName)));
+        } catch(Exception e){
+            throw new NamespaceException();
+        }
+    }
+
+    /**
+     * getNamespaceMapping
+     * @return a NamespaceMapping
+     */
+    public NamespaceMapping getNamespaceMapping(){
+        return mapping;
+    }
+
+    /**
+     * SAX ContentHandler that reacts to namespace mappings in incoming XML.
+     */
+    private class NamespaceHandler extends DefaultHandler {
+        public void startPrefixMapping(String prefix, String uri) throws SAXException {
+            if (uri == null) uri = "";
+
+            //Replace the empty prefix with the defaultBasePrefix
+            if (prefix == null || prefix.equals("")){
+                prefix = defaultBasePrefix;
+            }
+
+            try{
+                // if prefix already used
+                if (mapping.hasPrefix(prefix)){
+                    int c;
+                    Integer co = (Integer) basePrefixes.get(prefix);
+                    if (co == null) {
+                        basePrefixes.put(prefix, new Integer(1));
+                        c = 1;
+                    } else {
+                        c = co.intValue() + 1;
+                        basePrefixes.put(prefix, new Integer(c));
+                    }
+                    prefix = prefix + "_" + c;
+                }
+                mapping.setMapping(prefix, uri);
+            } catch(NamespaceException e){
+                String msg = e.getMessage();
+                log.debug(msg);
+            }
+        }
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\NamespaceExtractor.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/namespace/NamespaceAdder.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/NamespaceAdder.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/NamespaceAdder.java	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * 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.namespace;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.NamespaceException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.RepositoryException;
+import java.util.Map;
+import java.util.Iterator;
+
+public class NamespaceAdder {
+
+    private final NamespaceRegistry registry;
+
+    public NamespaceAdder(NamespaceRegistry nsr) {
+        registry = nsr;
+    }
+
+    public void addNamespaces(NamespaceMapping nsm)
+            throws NamespaceException, UnsupportedRepositoryOperationException, RepositoryException {
+        Map m = nsm.getPrefixToURIMapping();
+        for (Iterator i = m.values().iterator(); i.hasNext();) {
+            Map.Entry e = (Map.Entry) i.next();
+            String prefix = (String) e.getKey();
+            String uri = (String) e.getKey();
+            registry.registerNamespace(prefix, uri);
+        }
+    }
+
+    public void addNamespace(String prefix, String uri)
+        throws NamespaceException, UnsupportedRepositoryOperationException, RepositoryException {
+        registry.registerNamespace(prefix, uri);
+    }
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\NamespaceAdder.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/namespace/NamespaceResolver.java
===================================================================
--- src/main/java/org/apache/jackrabbit/namespace/NamespaceResolver.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/namespace/NamespaceResolver.java	(revision 0)
@@ -0,0 +1,48 @@
+/*
+ * 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.namespace;
+
+import javax.jcr.NamespaceException;
+
+/**
+ * Interface for resolving namespace URIs and prefixes. Unlike the JCR
+ * {@link javax.jcr.NamespaceRegistry} interface, this interface contains
+ * no functionality other than the basic namespace URI and prefix resolution
+ * methods. This interface is therefore used internally in many places where
+ * the full namespace registry is either not available or some other mechanism
+ * is used for resolving namespaces.
+ */
+public interface NamespaceResolver {
+
+    /**
+     * Returns the URI to which the given prefix is mapped.
+     *
+     * @param prefix namespace prefix
+     * @return the namespace URI to which the given prefix is mapped.
+     * @throws NamespaceException if the prefix is unknown.
+     */
+    String getURI(String prefix) throws NamespaceException;
+
+    /**
+     * Returns the prefix which is mapped to the given URI.
+     *
+     * @param uri namespace URI
+     * @return the prefix mapped to the given URI.
+     * @throws NamespaceException if the URI is unknown.
+     */
+    String getPrefix(String uri) throws NamespaceException;
+}

Property changes on: src\main\java\org\apache\jackrabbit\namespace\NamespaceResolver.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

