You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2006/07/21 12:43:06 UTC

svn commit: r424258 - in /jackrabbit/trunk/jackrabbit/src: main/java/org/apache/jackrabbit/name/ test/java/org/apache/jackrabbit/name/

Author: tripod
Date: Fri Jul 21 03:43:04 2006
New Revision: 424258

URL: http://svn.apache.org/viewvc?rev=424258&view=rev
Log:
- improve Path and QName classes
- improve PathElement handling 
- add convenience methods to NameFormat

Modified:
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/NameFormat.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/Path.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/QName.java
    jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/name/PathTest.java

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/NameFormat.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/NameFormat.java?rev=424258&r1=424257&r2=424258&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/NameFormat.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/NameFormat.java Fri Jul 21 03:43:04 2006
@@ -80,6 +80,39 @@
     }
 
     /**
+     * Parses an array of <code>jcrName</code> and returns the respective
+     * array of <code>QName</code>. If the passed <code>resolver</code> also an
+     * instance of {@link NameCache} then the parsing is first read from the cache.
+     *
+     * @param jcrNames the array of names to be parsed
+     * @param resolver <code>NamespaceResolver</code> use to retrieve the
+     * namespace URI from the prefix contained in the given JCR name.
+     * @return the new array of <code>QName</code>
+     * @throws IllegalNameException If <code>jcrName</code> is not a valid
+     * JCR-style name.
+     */
+    public static QName[] parse(String jcrNames[], NamespaceResolver resolver)
+            throws IllegalNameException, UnknownPrefixException {
+
+        QName[] ret = new QName[jcrNames.length];
+        if (resolver instanceof NameCache) {
+            for (int i=0; i<ret.length; i++) {
+                QName name = ((NameCache) resolver).retrieveName(jcrNames[i]);
+                if (name == null) {
+                    name = parseNoCache(jcrNames[i], resolver);
+                    ((NameCache) resolver).cacheName(jcrNames[i], name);
+                }
+                ret[i] = name;
+            }
+        } else {
+            for (int i=0; i<ret.length; i++) {
+                ret[i] = parseNoCache(jcrNames[i], resolver);
+            }
+        }
+        return ret;
+    }
+
+    /**
      * Parses the <code>jcrName</code> and returns a new <code>QName</code>,
      * but does not respect possible caches.
      *
@@ -195,6 +228,42 @@
             formatNoCache(qName, resolver, buf);
             return buf.toString();
         }
+    }
+
+    /**
+     * Optimized convenience method that returns an array of string
+     * representations of the given qualified <code>name</code> in the JCR name
+     * format. If the passed <code>resolver</code> also an instance of
+     * {@link NameCache} then the formatting is first read from the cache.
+     *
+     * @param qNames the array of qualified name to resolve.
+     * @param resolver the namespace resolver.
+     * @return the array of jcr names
+     * @throws NoPrefixDeclaredException if a namespace can not be resolved
+     * @see #format(QName, NamespaceResolver, StringBuffer)
+     */
+    public static String[] format(QName[] qNames, NamespaceResolver resolver)
+            throws NoPrefixDeclaredException {
+        String[] ret = new String[qNames.length];
+        if (resolver instanceof NameCache) {
+            for (int i=0; i<ret.length; i++) {
+                String jcrName = ((NameCache) resolver).retrieveName(qNames[i]);
+                if (jcrName == null) {
+                    StringBuffer buf = new StringBuffer();
+                    formatNoCache(qNames[i], resolver, buf);
+                    jcrName = buf.toString();
+                    ((NameCache) resolver).cacheName(jcrName, qNames[i]);
+                }
+                ret[i] = jcrName;
+            }
+        } else {
+            for (int i=0; i<ret.length; i++) {
+                StringBuffer buf = new StringBuffer();
+                formatNoCache(qNames[i], resolver, buf);
+                ret[i] = buf.toString();
+            }
+        }
+        return ret;
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/Path.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/Path.java?rev=424258&r1=424257&r2=424258&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/Path.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/Path.java Fri Jul 21 03:43:04 2006
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.name;
 
-import org.apache.jackrabbit.util.Text;
-
 import javax.jcr.PathNotFoundException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -43,7 +41,7 @@
  * <p/>
  * <code>isCanonical()</code>:<br>
  * A path is canonical if its absolute and normalized.
- *
+ * <p/>
  * <h2>String representations</h2>
  * <p/>
  * The JCR path format is specified by JSR 170 as follows:
@@ -159,6 +157,43 @@
     }
 
     //------------------------------------------------------< factory methods >
+
+    /**
+     * Creates a new <code>Path</code> from the given path elements.
+     *
+     * @param elements the path elements that will form the path
+     * @return a new <code>Path</code>
+     */
+    public static Path create(PathElement[] elements) {
+        PathElement[] tmp = new PathElement[elements.length];
+        boolean isNormalized = true;
+        boolean leadingParent = true;
+        for (int i = 0; i < elements.length; i++) {
+            PathElement elem = tmp[i] = elements[i];
+            if (elem.denotesCurrent() || elem.denotesParent()) {
+                leadingParent &= elem.denotesParent();
+                isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
+            }
+        }
+        return new Path(tmp, isNormalized);
+    }
+
+    /**
+     * Creates a new <code>Path</code> from the given path elements but does
+     * not check if the path is normalized or not.
+     * <p/>
+     * Please note that this method should only be called, if the normalized
+     * state is known. Further is the element array not duplicated. Basically
+     * this method should only be called from {@link PathFormat}.
+     *
+     * @param elements     the path elements that will form the path
+     * @param isNormalized flag
+     * @return a new <code>Path</code>
+     */
+    protected static Path create(PathElement[] elements, boolean isNormalized) {
+        return new Path(elements, isNormalized);
+    }
+
     /**
      * Creates a new <code>Path</code> from the given <code>jcrPath</code>
      * string. If <code>normalize</code> is <code>true</code>, the returned
@@ -174,12 +209,9 @@
     public static Path create(String jcrPath, NamespaceResolver resolver,
                               boolean normalize)
             throws MalformedPathException {
-        Path path = PathFormat.parse(jcrPath, resolver);
-        if (normalize) {
-            return path.getNormalizedPath();
-        } else {
-            return path;
-        }
+        return normalize
+                ? PathFormat.parse(null, jcrPath, resolver).getNormalizedPath()
+                : PathFormat.parse(null, jcrPath, resolver);
     }
 
     /**
@@ -198,12 +230,9 @@
     public static Path create(Path parent, String relJCRPath,
                               NamespaceResolver resolver, boolean canonicalize)
             throws MalformedPathException {
-        Path path = PathFormat.parse(parent, relJCRPath, resolver);
-        if (canonicalize) {
-            return path.getCanonicalPath();
-        } else {
-            return path;
-        }
+        return canonicalize
+                ? PathFormat.parse(parent, relJCRPath, resolver).getCanonicalPath()
+                : PathFormat.parse(parent, relJCRPath, resolver);
     }
 
     /**
@@ -223,16 +252,11 @@
         if (relPath.isAbsolute()) {
             throw new MalformedPathException("relPath is not a relative path");
         }
-
         PathBuilder pb = new PathBuilder(parent);
         pb.addAll(relPath.getElements());
-
-        Path path = pb.getPath();
-        if (normalize) {
-            return path.getNormalizedPath();
-        } else {
-            return path;
-        }
+        return normalize
+                ? pb.getPath().getNormalizedPath()
+                : pb.getPath();
     }
 
     /**
@@ -241,30 +265,26 @@
      * the returned path will be normalized (or canonicalized, if the parent
      * path is absolute).
      *
-     * @param parent the parent path
-     * @param name the name of the new path element.
+     * @param parent    the parent path
+     * @param name      the name of the new path element.
      * @param normalize
      * @return the new path.
      */
     public static Path create(Path parent, QName name, boolean normalize) throws MalformedPathException {
         PathBuilder pb = new PathBuilder(parent);
         pb.addLast(name);
-
-        Path path = pb.getPath();
-        if (normalize) {
-            return path.getNormalizedPath();
-        } else {
-            return path;
-        }
+        return normalize
+                ? pb.getPath().getNormalizedPath()
+                : pb.getPath();
     }
 
     /**
      * Creates a new <code>Path</code> out of the given <code>parent<code> path
      * and the give name and index.
      *
-     * @param parent the paren tpath.
-     * @param name the name of the new path element.
-     * @param index the index of the new path element.
+     * @param parent    the paren tpath.
+     * @param name      the name of the new path element.
+     * @param index     the index of the new path element.
      * @param normalize
      * @return the new path.
      */
@@ -272,13 +292,9 @@
             throws MalformedPathException {
         PathBuilder pb = new PathBuilder(parent);
         pb.addLast(name, index);
-
-        Path path = pb.getPath();
-        if (normalize) {
-            return path.getNormalizedPath();
-        } else {
-            return path;
-        }
+        return normalize
+                ? pb.getPath().getNormalizedPath()
+                : pb.getPath();
     }
 
     /**
@@ -291,19 +307,31 @@
      */
     public static Path create(QName name, int index)
             throws IllegalArgumentException {
-        if (index < INDEX_UNDEFINED) {
+        PathElement elem = createPathElement(name, index);
+        return new Path(new PathElement[]{elem}, !elem.denotesCurrent());
+    }
+
+    /**
+     * Create a PathElement from the given QName and index.
+     *
+     * @param qName
+     * @param index
+     * @return new path element
+     * @throws IllegalArgumentException if the index is less than {@link Path#INDEX_UNDEFINED}.
+     */
+    public static PathElement createPathElement(QName qName, int index) {
+        if (index < Path.INDEX_UNDEFINED) {
             throw new IllegalArgumentException("index must not be negative: " + index);
         }
-        PathElement elem;
-        if (index < INDEX_DEFAULT) {
-            elem = new PathElement(name);
+        if (index < Path.INDEX_DEFAULT) {
+            return PathElement.create(qName);
         } else {
-            elem = new PathElement(name, index);
+            return PathElement.create(qName, index);
         }
-        return new Path(new PathElement[]{elem}, !elem.equals(CURRENT_ELEMENT));
     }
 
     //------------------------------------------------------< utility methods >
+
     /**
      * Checks if <code>jcrPath</code> is a valid JCR-style absolute or relative
      * path.
@@ -318,6 +346,7 @@
     }
 
     //-------------------------------------------------------< public methods >
+
     /**
      * Tests whether this path represents the root path, i.e. "/".
      *
@@ -382,26 +411,19 @@
             return this;
         }
         LinkedList queue = new LinkedList();
-        PathElement last = null;
+        PathElement last = PARENT_ELEMENT;
         for (int i = 0; i < elements.length; i++) {
             PathElement elem = elements[i];
-            if (elem.denotesCurrent()) {
-                continue;
-            } else if (elem.denotesParent() && last != null && !last.denotesParent()) {
+            if (elem.denotesParent() && !last.denotesParent()) {
                 if (last.denotesRoot()) {
                     // the first element is the root element;
                     // ".." would refer to the parent of root
                     throw new MalformedPathException("Path can not be canonicalized: unresolvable '..' element");
                 }
                 queue.removeLast();
-                if (queue.isEmpty()) {
-                    last = null;
-                } else {
-                    last = (PathElement) queue.getLast();
-                }
-            } else {
-                last = elem;
-                queue.add(elem);
+                last = queue.isEmpty() ? PARENT_ELEMENT : (PathElement) queue.getLast();
+            } else if (!elem.denotesCurrent()) {
+                queue.add(last = elem);
             }
         }
         if (queue.isEmpty()) {
@@ -462,7 +484,8 @@
 
         // determine length of common path fragment
         int lengthCommon = 0;
-        for (int i = 0; i < p0.getElements().length && i < p1.getElements().length; i++) {
+        for (int i = 0; i < p0.getElements().length && i < p1.getElements().length; i++)
+        {
             if (!p0.getElement(i).equals(p1.getElement(i))) {
                 break;
             }
@@ -508,7 +531,8 @@
      *
      * @param degree the relative degree of the requested ancestor.
      * @return the ancestor path of the specified degree.
-     * @throws javax.jcr.PathNotFoundException    if there is no ancestor of the specified
+     * @throws PathNotFoundException
+     *                                  if there is no ancestor of the specified
      *                                  degree
      * @throws IllegalArgumentException if <code>degree</code> is negative
      */
@@ -668,7 +692,7 @@
      * @param i element index.
      * @return the <code>i</code><sup>th</sup> element of this path.
      * @throws ArrayIndexOutOfBoundsException if this path does not have an
-     * element at index <code>i</code>.
+     *                                        element at index <code>i</code>.
      */
     public PathElement getElement(int i) {
         return elements[i];
@@ -688,6 +712,7 @@
     }
 
     //---------------------------------------------------------------< Object >
+
     /**
      * Returns the internal string representation of this <code>Path</code>.
      * <p/>
@@ -737,12 +762,22 @@
         // split into path elements
 
         // @todo find safe path separator char that does not conflict with chars in serialized QName
-        String[] elements = Text.explode(s, '\t', true);
+        final char delim = '\t';
+        int lastPos = 0;
+        int pos = s.indexOf(delim);
         ArrayList list = new ArrayList();
         boolean isNormalized = true;
         boolean leadingParent = true;
-        for (int i = 0; i < elements.length; i++) {
-            PathElement elem = PathElement.fromString(elements[i]);
+        while (lastPos >= 0) {
+            PathElement elem;
+            if (pos >= 0) {
+                elem = PathElement.fromString(s.substring(lastPos, pos));
+                lastPos = pos + 1;
+                pos = s.indexOf(delim, lastPos);
+            } else {
+                elem = PathElement.fromString(s.substring(lastPos));
+                lastPos = -1;
+            }
             list.add(elem);
             leadingParent &= elem.denotesParent();
             isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
@@ -788,6 +823,7 @@
     }
 
     //--------------------------------------------------------< inner classes >
+
     /**
      * Internal helper class used to build a path from pre-parsed path elements.
      * <p/>
@@ -883,7 +919,7 @@
          * @param name
          */
         public void addFirst(QName name) {
-            addFirst(new PathElement(name));
+            addFirst(PathElement.create(name));
         }
 
         /**
@@ -893,7 +929,7 @@
          * @param index
          */
         public void addFirst(QName name, int index) {
-            addFirst(new PathElement(name, index));
+            addFirst(PathElement.create(name, index));
         }
 
         /**
@@ -913,7 +949,7 @@
          * @param name
          */
         public void addLast(QName name) {
-            addLast(new PathElement(name));
+            addLast(PathElement.create(name));
         }
 
         /**
@@ -923,7 +959,7 @@
          * @param index
          */
         public void addLast(QName name, int index) {
-            addLast(new PathElement(name, index));
+            addLast(PathElement.create(name, index));
         }
 
         /**
@@ -943,198 +979,27 @@
             return new Path(elements, isNormalized);
         }
 
-        public Object clone() {
+        /**
+         * {@inheritDoc}
+         */
+        public Object clone() throws CloneNotSupportedException {
+            super.clone();
             PathBuilder clone = new PathBuilder();
             clone.queue.addAll(queue);
             return clone;
         }
     }
 
-    public static final class RootElement extends PathElement {
-        // use a literal that is an illegal name character to avoid collisions
-        static final String LITERAL = "*";
-
-        private RootElement() {
-            super(QName.ROOT);
-        }
-
-        /**
-         * Returns true.
-         * @return true
-         * @see PathElement#denotesRoot()
-         */
-        public boolean denotesRoot() {
-            return true;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesCurrent()
-         */
-        public boolean denotesCurrent() {
-            return false;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesParent()
-         */
-        public boolean denotesParent() {
-            return false;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesName()
-         */
-        public boolean denotesName() {
-            return false;
-        }
-
-        /**
-         * @return {@link #LITERAL}
-         * @see Object#toString()
-         */
-        public String toString() {
-            return LITERAL;
-        }
-    }
-
-    public static final class CurrentElement extends PathElement {
-        static final String LITERAL = ".";
-
-        private CurrentElement() {
-            super(QName.NS_DEFAULT_URI, LITERAL);
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesRoot()
-         */
-        public boolean denotesRoot() {
-            return false;
-        }
-
-        /**
-         * Returns true.
-         * @return true
-         */
-        public boolean denotesCurrent() {
-            return true;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesParent()
-         */
-        public boolean denotesParent() {
-            return false;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesName()
-         */
-        public boolean denotesName() {
-            return false;
-        }
-
-        /**
-         * Returns the JCR name of this path element.
-         *
-         * @param resolver
-         * @return {@link #LITERAL}
-         */
-        public String toJCRName(NamespaceResolver resolver) {
-            return LITERAL;
-        }
-
-
-        /**
-         * @return {@link #LITERAL}
-         * @see Object#toString()
-         */
-        public String toString() {
-            return LITERAL;
-        }
-    }
-
-    private static final class ParentElement extends PathElement {
-        static final String LITERAL = "..";
-
-        private ParentElement() {
-            super(QName.NS_DEFAULT_URI, LITERAL);
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesRoot()
-         */
-        public boolean denotesRoot() {
-            return false;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesCurrent()
-         */
-        public boolean denotesCurrent() {
-            return false;
-        }
-
-        /**
-         * Returns true.
-         * @return true
-         * @see PathElement#denotesParent()
-         */
-        public boolean denotesParent() {
-            return true;
-        }
-
-        /**
-         * Returns false.
-         * @return false
-         * @see PathElement#denotesName()
-         */
-        public boolean denotesName() {
-            return false;
-        }
-
-        /**
-         * Returns the JCR name of this path element.
-         *
-         * @param resolver
-         * @return {@link #LITERAL}
-         */
-        public String toJCRName(NamespaceResolver resolver) {
-            return LITERAL;
-        }
-
-        /**
-         * @return {@link #LITERAL}
-         * @see Object#toString()
-         */
-        public String toString() {
-            return LITERAL;
-        }
-    }
+    //---------------------------------------------------------< Path Elements >
 
     /**
      * Object representation of a single JCR path element. A PathElement
      * object contains the qualified name and optional index of a single
      * JCR path element.
      * <p/>
-     * Once created, a PathElement object is immutable.
+     * Once created, a NameElement object is immutable.
      */
-    public static class PathElement {
+    public abstract static class PathElement {
 
         /**
          * Qualified name of the path element.
@@ -1147,59 +1012,72 @@
          */
         private final int index;
 
-        /**
-         * Creates a path element with the given qualified name.
-         * The created path element does not contain an explicit index.
-         *
-         * @param namespaceURI namespace URI
-         * @param localName    local name
-         */
-        private PathElement(String namespaceURI, String localName) {
-            this(new QName(namespaceURI, localName));
-        }
 
         /**
          * Creates a path element with the given qualified name and index.
          *
-         * @param namespaceURI namespace URI
-         * @param localName    local name
-         * @param index        index
+         * @param name  qualified name
+         * @param index index
          */
-        private PathElement(String namespaceURI, String localName, int index) {
-            this(new QName(namespaceURI, localName), index);
+        private PathElement(QName name, int index) {
+            this.index = index;
+            this.name = name;
         }
 
         /**
-         * Creates a path element with the given qualified name.
-         * The created path element does not contain an explicit index.
+         * Creates a new path element with the given qualified name and index.
+         * If the name is equals to the name of a special element, like the
+         * {@link PARENT_ELEMENT},{@link CURRENT_ELEMENT} or the
+         * {@link ROOT_ELEMENT}, then it's instance is returned.
+         * <p/>
+         * the private constructor must never be called but from these 2 methods.
          *
-         * @param name qualified name
+         * @param name the name of the element
+         * @return a path element
          * @throws IllegalArgumentException if the name is <code>null</code>
          */
-        private PathElement(QName name) throws IllegalArgumentException {
+        public static PathElement create(QName name) {
             if (name == null) {
                 throw new IllegalArgumentException("name must not be null");
+            } else if (name.equals(PARENT_ELEMENT.getName())) {
+                return PARENT_ELEMENT;
+            } else if (name.equals(CURRENT_ELEMENT.getName())) {
+                return CURRENT_ELEMENT;
+            } else if (name.equals(ROOT_ELEMENT.getName())) {
+                return ROOT_ELEMENT;
+            } else {
+                return new NameElement(name, INDEX_UNDEFINED);
             }
-            this.name = name;
-            this.index = INDEX_UNDEFINED;
         }
 
         /**
-         * Creates a path element with the given qualified name and index.
+         * Creates a new path element with the given qualified name and index.
+         * If the name is equals to the name of a special element, like the
+         * {@link PARENT_ELEMENT},{@link CURRENT_ELEMENT} or the
+         * {@link ROOT_ELEMENT}, then it's instance is returned.
+         * <p/>
+         * the private constructor must never be called but from these 2 methods.
          *
-         * @param name  qualified name
-         * @param index index
-         * @throws IllegalArgumentException if the name is <code>null</code>
+         * @param name  the name of the element
+         * @param index the 1-based index.
+         * @return a path element
+         * @throws IllegalArgumentException if the name is <code>null</code> or
+         *                                  if the given index is less than 1.
          */
-        private PathElement(QName name, int index) throws IllegalArgumentException {
-            if (name == null) {
-                throw new IllegalArgumentException("name must not be null");
-            }
+        public static PathElement create(QName name, int index) {
             if (index < INDEX_DEFAULT) {
-                throw new IllegalArgumentException("index is 1-based");
+                throw new IllegalArgumentException("index is 1-based.");
+            } else if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            } else if (name.equals(PARENT_ELEMENT.getName())) {
+                return PARENT_ELEMENT;
+            } else if (name.equals(CURRENT_ELEMENT.getName())) {
+                return CURRENT_ELEMENT;
+            } else if (name.equals(ROOT_ELEMENT.getName())) {
+                return ROOT_ELEMENT;
+            } else {
+                return new NameElement(name, index);
             }
-            this.index = index;
-            this.name = name;
         }
 
         /**
@@ -1234,54 +1112,10 @@
         }
 
         /**
-         * Returns <code>true</code> if this element denotes the <i>root</i> element,
-         * otherwise returns <code>false</code>.
-         *
-         * @return <code>true</code> if this element denotes the <i>root</i>
-         *         element; otherwise <code>false</code>
-         */
-        public boolean denotesRoot() {
-            return equals(ROOT_ELEMENT);
-        }
-
-        /**
-         * Returns <code>true</code> if this element denotes the <i>parent</i>
-         * ('..') element, otherwise returns <code>false</code>.
-         *
-         * @return <code>true</code> if this element denotes the <i>parent</i>
-         *         element; otherwise <code>false</code>
-         */
-        public boolean denotesParent() {
-            return equals(PARENT_ELEMENT);
-        }
-
-        /**
-         * Returns <code>true</code> if this element denotes the <i>current</i>
-         * ('.') element, otherwise returns <code>false</code>.
-         *
-         * @return <code>true</code> if this element denotes the <i>current</i>
-         *         element; otherwise <code>false</code>
-         */
-        public boolean denotesCurrent() {
-            return equals(CURRENT_ELEMENT);
-        }
-
-        /**
-         * Returns <code>true</code> if this element represents a regular name
-         * (i.e. neither root, '.' nor '..'), otherwise returns <code>false</code>.
-         *
-         * @return <code>true</code> if this element represents a regular name;
-         *         otherwise <code>false</code>
-         */
-        public boolean denotesName() {
-            return !denotesRoot() && !denotesParent() && !denotesCurrent();
-        }
-
-        /**
-         * Returns the JCR name representation of this path element.
-         * Note that strictly speaking the returned value is in fact
-         * a JCR relative path instead of a JCR name, as it contains
-         * the index value if the index is greater than one.
+         * Returns the JCR name representation of this path element.
+         * Note that strictly speaking the returned value is in fact
+         * a JCR relative path instead of a JCR name, as it contains
+         * the index value if the index is greater than one.
          *
          * @param resolver namespace resolver
          * @return JCR name representation of the path element
@@ -1371,7 +1205,7 @@
             int pos = s.indexOf('[');
             if (pos == -1) {
                 QName name = QName.valueOf(s);
-                return new PathElement(name.getNamespaceURI(), name.getLocalName());
+                return new NameElement(name, INDEX_UNDEFINED);
             }
             QName name = QName.valueOf(s.substring(0, pos));
             int pos1 = s.indexOf(']');
@@ -1383,7 +1217,7 @@
                 if (index < 1) {
                     throw new IllegalArgumentException("invalid PathElement literal: " + s + " (index is 1-based)");
                 }
-                return new PathElement(name.getNamespaceURI(), name.getLocalName(), index);
+                return new NameElement(name, index);
             } catch (Throwable t) {
                 throw new IllegalArgumentException("invalid PathElement literal: " + s + " (" + t.getMessage() + ")");
             }
@@ -1420,6 +1254,311 @@
                         // @todo treat index==0 as index==1?
                         && index == other.index;
             }
+            return false;
+        }
+
+        /**
+         * Returns <code>true</code> if this element denotes the <i>root</i> element,
+         * otherwise returns <code>false</code>.
+         *
+         * @return <code>true</code> if this element denotes the <i>root</i>
+         *         element; otherwise <code>false</code>
+         */
+        abstract public boolean denotesRoot();
+
+        /**
+         * Returns <code>true</code> if this element denotes the <i>parent</i>
+         * ('..') element, otherwise returns <code>false</code>.
+         *
+         * @return <code>true</code> if this element denotes the <i>parent</i>
+         *         element; otherwise <code>false</code>
+         */
+        abstract public boolean denotesParent();
+
+        /**
+         * Returns <code>true</code> if this element denotes the <i>current</i>
+         * ('.') element, otherwise returns <code>false</code>.
+         *
+         * @return <code>true</code> if this element denotes the <i>current</i>
+         *         element; otherwise <code>false</code>
+         */
+        abstract public boolean denotesCurrent();
+
+        /**
+         * Returns <code>true</code> if this element represents a regular name
+         * (i.e. neither root, '.' nor '..'), otherwise returns <code>false</code>.
+         *
+         * @return <code>true</code> if this element represents a regular name;
+         *         otherwise <code>false</code>
+         */
+        abstract public boolean denotesName();
+
+    }
+
+    public static final class RootElement extends PathElement {
+        // use a literal that is an illegal name character to avoid collisions
+        static final String LITERAL = "*";
+
+        private RootElement() {
+            super(QName.ROOT, Path.INDEX_UNDEFINED);
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesName()
+         */
+        public boolean denotesName() {
+            return false;
+        }
+
+        /**
+         * Returns true.
+         *
+         * @return true
+         * @see PathElement#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return true;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesParent()
+         */
+        public boolean denotesParent() {
+            return false;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesCurrent()
+         */
+        public boolean denotesCurrent() {
+            return false;
+        }
+
+        /**
+         * Returns the JCR name of this path element.
+         *
+         * @param resolver
+         * @return ""
+         */
+        public String toJCRName(NamespaceResolver resolver) {
+            return "";
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void toJCRName(NamespaceResolver resolver, StringBuffer buf) {
+            // append empty string, i.e. nothing.
+        }
+
+        /**
+         * @return {@link #LITERAL}
+         * @see Object#toString()
+         */
+        public String toString() {
+            return LITERAL;
+        }
+    }
+
+
+    public static final class CurrentElement extends PathElement {
+        static final String LITERAL = ".";
+
+        private CurrentElement() {
+            super(new QName(QName.NS_DEFAULT_URI, LITERAL), Path.INDEX_UNDEFINED);
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesName()
+         */
+        public boolean denotesName() {
+            return false;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return false;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesParent()
+         */
+        public boolean denotesParent() {
+            return false;
+        }
+
+        /**
+         * Returns true.
+         *
+         * @return true
+         * @see PathElement#denotesCurrent()
+         */
+        public boolean denotesCurrent() {
+            return true;
+        }
+
+        /**
+         * Returns the JCR name of this path element.
+         *
+         * @param resolver
+         * @return {@link #LITERAL}
+         */
+        public String toJCRName(NamespaceResolver resolver) {
+            return LITERAL;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void toJCRName(NamespaceResolver resolver, StringBuffer buf) {
+            buf.append(LITERAL);
+        }
+
+        /**
+         * @return {@link #LITERAL}
+         * @see Object#toString()
+         */
+        public String toString() {
+            return LITERAL;
+        }
+    }
+
+    public static final class ParentElement extends PathElement {
+        static final String LITERAL = "..";
+
+        private ParentElement() {
+            super(new QName(QName.NS_DEFAULT_URI, LITERAL), Path.INDEX_UNDEFINED);
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesName()
+         */
+        public boolean denotesName() {
+            return false;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return false;
+        }
+
+        /**
+         * Returns true.
+         *
+         * @return true
+         * @see PathElement#denotesParent()
+         */
+        public boolean denotesParent() {
+            return true;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesCurrent()
+         */
+        public boolean denotesCurrent() {
+            return false;
+        }
+
+        /**
+         * Returns the JCR name of this path element.
+         *
+         * @param resolver
+         * @return {@link #LITERAL}
+         */
+        public String toJCRName(NamespaceResolver resolver) {
+            return LITERAL;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void toJCRName(NamespaceResolver resolver, StringBuffer buf) {
+            buf.append(LITERAL);
+        }
+
+        /**
+         * @return {@link #LITERAL}
+         * @see Object#toString()
+         */
+        public String toString() {
+            return LITERAL;
+        }
+    }
+
+    public static final class NameElement extends PathElement {
+
+        private NameElement(QName name, int index) {
+            super(name, index);
+        }
+
+        /**
+         * Returns true.
+         *
+         * @return true
+         * @see PathElement#denotesName()
+         */
+        public boolean denotesName() {
+            return true;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesRoot()
+         */
+        public boolean denotesRoot() {
+            return false;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesParent()
+         */
+        public boolean denotesParent() {
+            return false;
+        }
+
+        /**
+         * Returns false.
+         *
+         * @return false
+         * @see PathElement#denotesCurrent()
+         */
+        public boolean denotesCurrent() {
             return false;
         }
     }

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/QName.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/QName.java?rev=424258&r1=424257&r2=424258&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/QName.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/name/QName.java Fri Jul 21 03:43:04 2006
@@ -534,13 +534,25 @@
         if (localName == null) {
             throw new IllegalArgumentException("invalid localName specified");
         }
-        // internalize both namespaceURI and localName to improve performance
-        // of QName comparisons
+        // internalize only namespaceURI to improve performance of QName
+        // comparisons. Please note that we do not internalize the localname
+        // since this could blow perm space for big repositories.
         this.namespaceURI = namespaceURI.intern();
-        this.localName = localName.intern();
+        this.localName = localName;
         hash = 0;
     }
 
+    /**
+     * Returns a new instance of this class with the same namespace URI and
+     * local name as has been deserialized.
+     * <p>
+     * If just sing plain serialization, the strings are just created but not
+     * internalized.
+     */
+    private Object readResolve() {
+        return new QName(namespaceURI, localName);
+    }
+
     //------------------------------------------------------< factory methods >
 
     /**
@@ -712,10 +724,10 @@
         }
         if (obj instanceof QName) {
             QName other = (QName) obj;
-            // localName & namespaceURI are internalized,
-            // we only have to compare their references
-            return localName == other.localName
-                    && namespaceURI == other.namespaceURI;
+            // namespaceURI is internalized, we only have to compare their
+            // references
+            return namespaceURI == other.namespaceURI
+                    && localName.equals(other.localName);
         }
         return false;
     }
@@ -763,9 +775,16 @@
      * @throws ClassCastException if the given object is not a qualified name
      * @see Comparable#compareTo(Object)
      */
-    public int compareTo(Object o) throws ClassCastException {
+    public int compareTo(Object o) {
+        if (this == o) {
+            return 0;
+        }
         QName other = (QName) o;
-        int result = namespaceURI.compareTo(other.namespaceURI);
-        return (result != 0) ? result : localName.compareTo(other.localName);
+        // == comparison ok, because of .intern() strings
+        if (namespaceURI == other.namespaceURI) {
+            return localName.compareTo(other.localName);
+        } else {
+            return namespaceURI.compareTo(other.namespaceURI);
+        }
     }
 }

Modified: jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/name/PathTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/name/PathTest.java?rev=424258&r1=424257&r2=424258&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/name/PathTest.java (original)
+++ jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/name/PathTest.java Fri Jul 21 03:43:04 2006
@@ -90,6 +90,7 @@
 
         // 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));