You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2006/07/10 09:08:39 UTC

svn commit: r420449 [2/2] - in /jackrabbit/trunk/jackrabbit/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/lock/ main/java/org/apache/jackrabbit/core/state/ main/java/org/apache/jackrabbit/core/xml/ main/java/org/apache...

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/PathMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/PathMap.java?rev=420449&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/PathMap.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/PathMap.java Mon Jul 10 00:08:37 2006
@@ -0,0 +1,577 @@
+/*
+ * 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.util;
+
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.QName;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Generic path map that associates information with the individual path elements
+ * of a path.
+ */
+public class PathMap {
+
+    /**
+     * Root element
+     */
+    private final Element root = new Element(Path.ROOT.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.PathElement[] 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.PathElement[] 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.PathElement[] 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.
+     * @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 QName 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.PathElement 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.PathElement 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.PathElement 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.PathElement 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.PathElement 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.PathElement nameIndex) {
+            return remove(nameIndex, 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 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
+         * @return removed child, may be <code>null</code>
+         */
+        private Element remove(Path.PathElement nameIndex, boolean shift) {
+            // 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 (childrenCount == 0 && obj == null && parent != null) {
+                parent.remove(getPathElement(), shift);
+            }
+            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);
+            }
+        }
+
+        /**
+         * 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);
+            }
+        }
+
+        /**
+         * 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);
+            }
+        }
+
+        /**
+         * Return the name of this element
+         * @return name
+         */
+        public QName 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.PathElement getPathElement() {
+            return Path.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.ROOT;
+            }
+
+            Path.PathBuilder builder = new Path.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(Path.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.PathElement[] 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.PathElement nameIndex) {
+            return nameIndex.getNormalizedIndex() - 1;
+        }
+
+        /**
+         * Recursively invoked traversal method.
+         * @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 (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);
+                        }
+                    }
+                }
+            }
+            if (includeEmpty || obj != null) {
+                visitor.elementVisited(this);
+            }
+        }
+
+        /**
+         * 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();
+        }
+    }
+
+    /**
+     * 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);
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/PathMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/PathMap.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/Text.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/Text.java?rev=420449&r1=420448&r2=420449&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/Text.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/util/Text.java Mon Jul 10 00:08:37 2006
@@ -532,6 +532,19 @@
     }
 
     /**
+     * Same as {@link #getName(String)} but adding the possibility
+     * to pass paths that end with a trailing '/'
+     *
+     * @see #getName(String)
+     */
+    public static String getName(String path, boolean ignoreTrailingSlash) {
+        if (ignoreTrailingSlash && path.endsWith("/") && path.length() > 1) {
+            path = path.substring(0, path.length()-1);
+        }
+        return getName(path);
+    }
+
+    /**
      * Returns the namespace prefix of the given <code>qname</code>. If the
      * prefix is missing, an empty string is returned. Please note, that this
      * method does not validate the name or prefix.
@@ -634,6 +647,19 @@
             level--;
         }
         return (idx == 0) ? "/" : path.substring(0, idx);
+    }
+
+    /**
+     * Same as {@link #getRelativeParent(String, int)} but adding the possibility
+     * to pass paths that end with a trailing '/'
+     *
+     * @see #getRelativeParent(String, int)
+     */
+    public static String getRelativeParent(String path, int level, boolean ignoreTrailingSlash) {
+        if (ignoreTrailingSlash && path.endsWith("/") && path.length() > 1) {
+            path = path.substring(0, path.length()-1);
+        }
+        return getRelativeParent(path, level);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/NameValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/NameValue.java?rev=420449&r1=420448&r2=420449&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/NameValue.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/NameValue.java Mon Jul 10 00:08:37 2006
@@ -16,8 +16,8 @@
  */
 package org.apache.jackrabbit.value;
 
+import org.apache.jackrabbit.name.NameFormat;
 import org.apache.jackrabbit.name.IllegalNameException;
-import org.apache.jackrabbit.name.QName;
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -50,7 +50,7 @@
     public static NameValue valueOf(String s) throws ValueFormatException {
         if (s != null) {
             try {
-                QName.checkFormat(s);
+                NameFormat.checkFormat(s);
             } catch (IllegalNameException ine) {
                 throw new ValueFormatException(ine.getMessage());
             }

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/PathValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/PathValue.java?rev=420449&r1=420448&r2=420449&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/PathValue.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/PathValue.java Mon Jul 10 00:08:37 2006
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.value;
 
 import org.apache.jackrabbit.name.MalformedPathException;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.PathFormat;
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
@@ -51,7 +51,7 @@
     public static PathValue valueOf(String s) throws ValueFormatException {
         if (s != null) {
             try {
-                Path.checkFormat(s);
+                PathFormat.checkFormat(s);
             } catch (MalformedPathException mpe) {
                 throw new ValueFormatException(mpe.getMessage());
             }

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java?rev=420449&r1=420448&r2=420449&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java Mon Jul 10 00:08:37 2006
@@ -21,20 +21,32 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.ValueFormatException;
+import javax.jcr.PropertyType;
 import java.io.InputStream;
 import java.util.Calendar;
 
 /**
- * This class implements the  <code>ValueFactory</code> interface.
+ * This class implements the <code>ValueFactory</code> interface.
  *
  * @see javax.jcr.Session#getValueFactory()
  */
 public class ValueFactoryImpl implements ValueFactory {
 
+    private static final ValueFactory valueFactory = new ValueFactoryImpl();
+
     /**
      * Constructs a <code>ValueFactory</code> object.
      */
-    public ValueFactoryImpl() {
+    private ValueFactoryImpl() {
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     *
+     * @return
+     */
+    public static ValueFactory getInstance() {
+        return valueFactory;
     }
 
     //---------------------------------------------------------< ValueFactory >
@@ -92,6 +104,38 @@
      */
     public Value createValue(String value, int type)
             throws ValueFormatException {
-        return ValueHelper.convert(value, type);
+        Value val;
+        switch (type) {
+            case PropertyType.STRING:
+                val = new StringValue(value);
+                break;
+            case PropertyType.BOOLEAN:
+                val = BooleanValue.valueOf(value);
+                break;
+            case PropertyType.DOUBLE:
+                val = DoubleValue.valueOf(value);
+                break;
+            case PropertyType.LONG:
+                val = LongValue.valueOf(value);
+                break;
+            case PropertyType.DATE:
+                val = DateValue.valueOf(value);
+                break;
+            case PropertyType.NAME:
+                val = NameValue.valueOf(value);
+                break;
+            case PropertyType.PATH:
+                val = PathValue.valueOf(value);
+                break;
+            case PropertyType.REFERENCE:
+                val = ReferenceValue.valueOf(value);
+                break;
+            case PropertyType.BINARY:
+                val = new BinaryValue(value);
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid type constant: " + type);
+        }
+        return val;
     }
 }

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueHelper.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueHelper.java?rev=420449&r1=420448&r2=420449&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueHelper.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/ValueHelper.java Mon Jul 10 00:08:37 2006
@@ -24,6 +24,7 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
+import javax.jcr.ValueFactory;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.Reader;
@@ -48,38 +49,109 @@
     }
 
     /**
+     * Same as {@link #convert(String, int, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
+     *
      * @param srcValue
      * @param targetType
      * @return
      * @throws ValueFormatException
      * @throws IllegalArgumentException
-     * @see #convert(Value, int)
+     * @deprecated Use {@link #convert(String, int, ValueFactory)} instead.
+     * @see #convert(Value, int, ValueFactory)
      */
     public static Value convert(String srcValue, int targetType)
             throws ValueFormatException, IllegalArgumentException {
+        return convert(srcValue, targetType, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * @param srcValue
+     * @param targetType
+     * @param factory
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalArgumentException
+     * @see #convert(Value, int, ValueFactory)
+     */
+    public static Value convert(String srcValue, int targetType, ValueFactory factory)
+        throws ValueFormatException, IllegalArgumentException {
         if (srcValue == null) {
             return null;
         } else {
-            return convert(new StringValue(srcValue), targetType);
+            return factory.createValue(srcValue, targetType);
         }
     }
 
     /**
+     * Same as {@link #convert(InputStream, int, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
+     *
+     * @param srcValue
+     * @param targetType
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalArgumentException
+     * @deprecated Use {@link #convert(InputStream, int, ValueFactory)} instead.
+     */
+    public static Value convert(InputStream srcValue, int targetType)
+            throws ValueFormatException, IllegalArgumentException {
+        return convert(srcValue, targetType, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * @param srcValue
+     * @param targetType
+     * @param factory
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalArgumentException
+     */
+    public static Value convert(InputStream srcValue, int targetType, ValueFactory factory)
+        throws ValueFormatException, IllegalArgumentException {
+        if (srcValue == null) {
+            return null;
+        } else {
+            return convert(factory.createValue(srcValue), targetType, factory);
+        }
+    }
+
+    /**
+     * Same as {@link #convert(String[], int, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
+     *
      * @param srcValues
      * @param targetType
      * @return
      * @throws ValueFormatException
      * @throws IllegalArgumentException
-     * @see #convert(Value, int)
+     * @deprecated Use {@link #convert(String[], int, ValueFactory)} instead.
+     * @see #convert(Value, int, ValueFactory)
      */
     public static Value[] convert(String[] srcValues, int targetType)
             throws ValueFormatException, IllegalArgumentException {
+        return convert(srcValues, targetType, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * Same as {@link #convert(String[], int, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
+     *
+     * @param srcValues
+     * @param targetType
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalArgumentException
+     * @see #convert(Value, int, ValueFactory)
+     */
+    public static Value[] convert(String[] srcValues, int targetType, ValueFactory factory)
+            throws ValueFormatException, IllegalArgumentException {
         if (srcValues == null) {
             return null;
         }
         Value[] newValues = new Value[srcValues.length];
         for (int i = 0; i < srcValues.length; i++) {
-            newValues[i] = convert(srcValues[i], targetType);
+            newValues[i] = convert(srcValues[i], targetType, factory);
         }
         return newValues;
     }
@@ -90,9 +162,49 @@
      * @return
      * @throws ValueFormatException
      * @throws IllegalArgumentException
-     * @see #convert(Value, int)
+     * @see #convert(Value, int, ValueFactory)
+     */
+    public static Value[] convert(InputStream[] srcValues, int targetType,
+                                  ValueFactory factory)
+            throws ValueFormatException, IllegalArgumentException {
+        if (srcValues == null) {
+            return null;
+        }
+        Value[] newValues = new Value[srcValues.length];
+        for (int i = 0; i < srcValues.length; i++) {
+            newValues[i] = convert(srcValues[i], targetType, factory);
+        }
+        return newValues;
+    }
+
+    /**
+     * Same as {@link #convert(Value[], int, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
+     *
+     * @param srcValues
+     * @param targetType
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalArgumentException
+     * @deprecated Use {@link #convert(Value[], int, ValueFactory)} instead.
+     * @see #convert(Value, int, ValueFactory)
      */
     public static Value[] convert(Value[] srcValues, int targetType)
+        throws ValueFormatException, IllegalArgumentException {
+        return convert(srcValues, targetType, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * @param srcValues
+     * @param targetType
+     * @param factory
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalArgumentException
+     * @see #convert(Value, int, ValueFactory)
+     */
+    public static Value[] convert(Value[] srcValues, int targetType,
+                                  ValueFactory factory)
             throws ValueFormatException, IllegalArgumentException {
         if (srcValues == null) {
             return null;
@@ -114,24 +226,44 @@
                 throw new ValueFormatException(msg);
             }
 
-            newValues[i] = convert(srcValues[i], targetType);
+            newValues[i] = convert(srcValues[i], targetType, factory);
         }
         return newValues;
     }
 
     /**
+     * Same as {@link #convert(Value, int, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
+
+     * @param srcValue
+     * @param targetType
+     * @return
+     * @throws ValueFormatException
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @deprecated Use {@link #convert(Value, int, ValueFactory)} instead.
+     * @see #convert(Value, int, ValueFactory)
+     */
+    public static Value convert(Value srcValue, int targetType)
+        throws ValueFormatException, IllegalStateException,
+        IllegalArgumentException {
+        return convert(srcValue, targetType, ValueFactoryImpl.getInstance());
+    }
+
+    /**
      * Converts the given value to a value of the specified target type.
      * The conversion is performed according to the rules described in
      * "6.2.6 Property Type Conversion" in the JSR 170 specification.
      *
      * @param srcValue
      * @param targetType
+     * @param factory
      * @return
      * @throws ValueFormatException
      * @throws IllegalStateException
      * @throws IllegalArgumentException
      */
-    public static Value convert(Value srcValue, int targetType)
+    public static Value convert(Value srcValue, int targetType, ValueFactory factory)
             throws ValueFormatException, IllegalStateException,
             IllegalArgumentException {
         if (srcValue == null) {
@@ -150,7 +282,7 @@
             case PropertyType.STRING:
                 // convert to STRING
                 try {
-                    val = new StringValue(srcValue.getString());
+                    val = factory.createValue(srcValue.getString());
                 } catch (RepositoryException re) {
                     throw new ValueFormatException("conversion failed: "
                             + PropertyType.nameFromValue(srcType) + " to "
@@ -161,7 +293,7 @@
             case PropertyType.BINARY:
                 // convert to BINARY
                 try {
-                    val = new BinaryValue(srcValue.getStream());
+                    val = factory.createValue(srcValue.getStream());
                 } catch (RepositoryException re) {
                     throw new ValueFormatException("conversion failed: "
                             + PropertyType.nameFromValue(srcType) + " to "
@@ -172,7 +304,7 @@
             case PropertyType.BOOLEAN:
                 // convert to BOOLEAN
                 try {
-                    val = new BooleanValue(srcValue.getBoolean());
+                    val = factory.createValue(srcValue.getBoolean());
                 } catch (RepositoryException re) {
                     throw new ValueFormatException("conversion failed: "
                             + PropertyType.nameFromValue(srcType) + " to "
@@ -183,7 +315,7 @@
             case PropertyType.DATE:
                 // convert to DATE
                 try {
-                    val = new DateValue(srcValue.getDate());
+                    val = factory.createValue(srcValue.getDate());
                 } catch (RepositoryException re) {
                     throw new ValueFormatException("conversion failed: "
                             + PropertyType.nameFromValue(srcType) + " to "
@@ -194,7 +326,7 @@
             case PropertyType.DOUBLE:
                 // convert to DOUBLE
                 try {
-                    val = new DoubleValue(srcValue.getDouble());
+                    val = factory.createValue(srcValue.getDouble());
                 } catch (RepositoryException re) {
                     throw new ValueFormatException("conversion failed: "
                             + PropertyType.nameFromValue(srcType) + " to "
@@ -205,7 +337,7 @@
             case PropertyType.LONG:
                 // convert to LONG
                 try {
-                    val = new LongValue(srcValue.getLong());
+                    val = factory.createValue(srcValue.getLong());
                 } catch (RepositoryException re) {
                     throw new ValueFormatException("conversion failed: "
                             + PropertyType.nameFromValue(srcType) + " to "
@@ -234,7 +366,7 @@
                             throw new ValueFormatException("failed to convert source value to PATH value",
                                     re);
                         }
-                        val = PathValue.valueOf(path);
+                        val = factory.createValue(path, targetType);
                         break;
 
                     case PropertyType.BOOLEAN:
@@ -272,7 +404,7 @@
                             throw new ValueFormatException("failed to convert source value to NAME value",
                                     re);
                         }
-                        val = NameValue.valueOf(name);
+                        val = factory.createValue(name, targetType);
                         break;
 
                     case PropertyType.BOOLEAN:
@@ -306,10 +438,9 @@
                             uuid = srcValue.getString();
                         } catch (RepositoryException re) {
                             // should never happen
-                            throw new ValueFormatException("failed to convert source value to REFERENCE value",
-                                    re);
+                            throw new ValueFormatException("failed to convert source value to REFERENCE value", re);
                         }
-                        val = ReferenceValue.valueOf(uuid);
+                        val = factory.createValue(uuid, targetType);
                         break;
 
                     case PropertyType.BOOLEAN:
@@ -335,11 +466,26 @@
     }
 
     /**
+     * Same as {@link #copy(Value, ValueFactory)} using <code>ValueFactoryImpl</code>.
+     *
      * @param srcValue
      * @return
      * @throws IllegalStateException
+     * @deprecated Use {@link #copy(Value, ValueFactory)} instead.
      */
     public static Value copy(Value srcValue) throws IllegalStateException {
+        return copy(srcValue, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     *
+     * @param srcValue
+     * @param factory
+     * @return
+     * @throws IllegalStateException
+     */
+    public static Value copy(Value srcValue, ValueFactory factory)
+        throws IllegalStateException {
         if (srcValue == null) {
             return null;
         }
@@ -348,39 +494,33 @@
         try {
             switch (srcValue.getType()) {
                 case PropertyType.BINARY:
-                    newVal = new BinaryValue(srcValue.getStream());
+                    newVal = factory.createValue(srcValue.getStream());
                     break;
 
                 case PropertyType.BOOLEAN:
-                    newVal = new BooleanValue(srcValue.getBoolean());
+                    newVal = factory.createValue(srcValue.getBoolean());
                     break;
 
                 case PropertyType.DATE:
-                    newVal = new DateValue(srcValue.getDate());
+                    newVal = factory.createValue(srcValue.getDate());
                     break;
 
                 case PropertyType.DOUBLE:
-                    newVal = new DoubleValue(srcValue.getDouble());
+                    newVal = factory.createValue(srcValue.getDouble());
                     break;
 
                 case PropertyType.LONG:
-                    newVal = new LongValue(srcValue.getLong());
+                    newVal = factory.createValue(srcValue.getLong());
                     break;
 
                 case PropertyType.PATH:
-                    newVal = PathValue.valueOf(srcValue.getString());
-                    break;
-
                 case PropertyType.NAME:
-                    newVal = NameValue.valueOf(srcValue.getString());
-                    break;
-
                 case PropertyType.REFERENCE:
-                    newVal = ReferenceValue.valueOf(srcValue.getString());
+                    newVal = factory.createValue(srcValue.getString(), srcValue.getType());
                     break;
 
                 case PropertyType.STRING:
-                    newVal = new StringValue(srcValue.getString());
+                    newVal = factory.createValue(srcValue.getString());
                     break;
             }
         } catch (RepositoryException re) {
@@ -390,18 +530,32 @@
     }
 
     /**
+     * Same as {@link #copy(Value[], ValueFactory)} using <code>ValueFactoryImpl</code>.
+     *
      * @param srcValues
      * @return
      * @throws IllegalStateException
+     * @deprecated Use {@link #copy(Value[], ValueFactory)} instead.
      */
     public static Value[] copy(Value[] srcValues) throws IllegalStateException {
+        return copy(srcValues, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * @param srcValues
+     * @param factory
+     * @return
+     * @throws IllegalStateException
+     */
+    public static Value[] copy(Value[] srcValues, ValueFactory factory)
+        throws IllegalStateException {
         if (srcValues == null) {
             return null;
         }
 
         Value[] newValues = new Value[srcValues.length];
         for (int i = 0; i < srcValues.length; i++) {
-            newValues[i] = copy(srcValues[i]);
+            newValues[i] = copy(srcValues[i], factory);
         }
         return newValues;
     }
@@ -477,6 +631,8 @@
 
     /**
      * Deserializes the given string to a <code>Value</code> of the given type.
+     * Same as {@link #deserialize(String, int, boolean, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
      *
      * @param value        string to be deserialized
      * @param type         type of value
@@ -488,10 +644,33 @@
      *                              format
      * @throws RepositoryException  if an error occured during the
      *                              deserialization.
+     * @deprecated Use {@link #deserialize(String, int, boolean, ValueFactory)}
+     * instead.
      */
     public static Value deserialize(String value, int type,
                                     boolean decodeBlanks)
             throws ValueFormatException, RepositoryException {
+        return deserialize(value, type, decodeBlanks, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * Deserializes the given string to a <code>Value</code> of the given type.
+     *
+     * @param value        string to be deserialized
+     * @param type         type of value
+     * @param decodeBlanks if <code>true</code> <code>"_x0020_"</code>
+     *                     character sequences will be decoded to single space
+     *                     characters each.
+     * @param factory      ValueFactory used to build the <code>Value</code> object.
+     * @return the deserialized <code>Value</code>
+     * @throws ValueFormatException if the string data is not of the required
+     *                              format
+     * @throws RepositoryException  if an error occured during the
+     *                              deserialization.
+     */
+    public static Value deserialize(String value, int type, boolean decodeBlanks,
+                                    ValueFactory factory)
+            throws ValueFormatException, RepositoryException {
         if (type == PropertyType.BINARY) {
             // base64 encoded binary value;
             // the encodeBlanks flag can be ignored since base64-encoded
@@ -505,19 +684,24 @@
                 throw new RepositoryException("failed to decode binary value",
                         ioe);
             }
+            // NOTE: for performance reasons the BinaryValue is created directly
+            // from the byte-array. This is inconsistent with the other calls,
+            // that delegate the value creation to the ValueFactory.
             return new BinaryValue(baos.toByteArray());
         } else {
             if (decodeBlanks) {
                 // decode encoded blanks in value
                 value = Text.replace(value, "_x0020_", " ");
             }
-            return convert(value, type);
+            return convert(value, type, factory);
         }
     }
 
     /**
      * Deserializes the string data read from the given reader to a
-     * <code>Value</code> of the given type.
+     * <code>Value</code> of the given type. Same as
+     * {@link #deserialize(Reader, int, boolean, ValueFactory)} using
+     * <code>ValueFactoryImpl</code>.
      *
      * @param reader       reader for the string data to be deserialized
      * @param type         type of value
@@ -531,10 +715,36 @@
      *                              format
      * @throws RepositoryException  if an error occured during the
      *                              deserialization.
+     * @deprecated Use {@link #deserialize(Reader, int, boolean, ValueFactory)}
+     * instead.
      */
     public static Value deserialize(Reader reader, int type,
                                     boolean decodeBlanks)
             throws IOException, ValueFormatException, RepositoryException {
+        return deserialize(reader, type, decodeBlanks, ValueFactoryImpl.getInstance());
+    }
+
+    /**
+     * Deserializes the string data read from the given reader to a
+     * <code>Value</code> of the given type.
+     *
+     * @param reader       reader for the string data to be deserialized
+     * @param type         type of value
+     * @param decodeBlanks if <code>true</code> <code>"_x0020_"</code>
+     *                     character sequences will be decoded to single space
+     *                     characters each.
+     * @param factory      ValueFactory used to build the <code>Value</code> object.
+     * @return the deserialized <code>Value</code>
+     * @throws IOException          if an i/o error occured during the
+     *                              serialization
+     * @throws ValueFormatException if the string data is not of the required
+     *                              format
+     * @throws RepositoryException  if an error occured during the
+     *                              deserialization.
+     */
+    public static Value deserialize(Reader reader, int type,
+                                    boolean decodeBlanks, ValueFactory factory)
+            throws IOException, ValueFormatException, RepositoryException {
         if (type == PropertyType.BINARY) {
             // base64 encoded binary value;
             // the encodeBlanks flag can be ignored since base64-encoded
@@ -553,8 +763,8 @@
             // create an InputStream that keeps a hard reference to the temp file
             // in order to prevent its automatic deletion once the associated
             // File object is reclaimed by the garbage collector;
-            // pass InputStream wrapper to BinaryValue constructor
-            return new BinaryValue(new FilterInputStream(new FileInputStream(tmpFile)) {
+            // pass InputStream wrapper to ValueFactory, that creates a BinaryValue.
+            return factory.createValue(new FilterInputStream(new FileInputStream(tmpFile)) {
 
                 public void close() throws IOException {
                     in.close();
@@ -581,7 +791,7 @@
                 // decode encoded blanks in value
                 value = Text.replace(value, "_x0020_", " ");
             }
-            return convert(value, type);
+            return convert(value, type, factory);
         }
     }
 }

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=420449&r1=420448&r2=420449&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 Mon Jul 10 00:08:37 2006
@@ -19,6 +19,8 @@
 import junit.framework.TestCase;
 
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
 
 import org.apache.jackrabbit.util.Text;
 
@@ -273,6 +275,110 @@
         return normalize ? builder.getPath().getNormalizedPath() : builder.getPath();
     }
 
+   public void testNormalizedPaths() throws Exception {
+        List paths = new ArrayList();
+
+        // normalized paths
+        paths.add(PathFormat.parse("/", resolver));
+        paths.add(PathFormat.parse("/foo", resolver));
+        paths.add(PathFormat.parse("/foo/bar", resolver));
+        paths.add(PathFormat.parse("foo/bar", resolver));
+        paths.add(PathFormat.parse("foo", resolver));
+        paths.add(PathFormat.parse("../../foo/bar", resolver));
+        paths.add(PathFormat.parse("..", resolver));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertTrue("path is not normalized: " + PathFormat.format(path, resolver), path.isNormalized());
+        }
+
+        paths.clear();
+
+        // not normalized paths
+        paths.add(PathFormat.parse("/foo/..", resolver));
+        paths.add(PathFormat.parse("/foo/.", resolver));
+        paths.add(PathFormat.parse("/foo/../bar", resolver));
+        paths.add(PathFormat.parse("/foo/./bar", resolver));
+        paths.add(PathFormat.parse("./foo", resolver));
+        paths.add(PathFormat.parse(".", resolver));
+        paths.add(PathFormat.parse("foo/..", resolver));
+        paths.add(PathFormat.parse("../foo/..", resolver));
+        paths.add(PathFormat.parse("../foo/.", resolver));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertFalse("path is normalized: " + PathFormat.format(path, resolver), path.isNormalized());
+        }
+    }
+
+    public void testAbsolutePaths() throws Exception {
+        List paths = new ArrayList();
+
+        // absolute paths
+        paths.add(PathFormat.parse("/", resolver));
+        paths.add(PathFormat.parse("/foo", resolver));
+        paths.add(PathFormat.parse("/foo/bar", resolver));
+        paths.add(PathFormat.parse("/foo/../bar", resolver));
+        paths.add(PathFormat.parse("/foo/..", resolver));
+        paths.add(PathFormat.parse("/foo/./bar", resolver));
+        paths.add(PathFormat.parse("/foo/.././bar/./foo", resolver));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertTrue("path is not absolute: " + PathFormat.format(path, resolver), path.isAbsolute());
+        }
+
+        paths.clear();
+
+        // not absoulute paths
+        paths.add(PathFormat.parse("foo/..", resolver));
+        paths.add(PathFormat.parse("foo/.", resolver));
+        paths.add(PathFormat.parse("foo/../bar", resolver));
+        paths.add(PathFormat.parse("foo/./bar", resolver));
+        paths.add(PathFormat.parse("./foo", resolver));
+        paths.add(PathFormat.parse(".", resolver));
+        paths.add(PathFormat.parse("foo/..", resolver));
+        paths.add(PathFormat.parse("../foo/..", resolver));
+        paths.add(PathFormat.parse("../foo/.", resolver));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertFalse("path is absolute: " + PathFormat.format(path, resolver), path.isAbsolute());
+        }
+    }
+
+    public void testCanonicalPaths() throws Exception {
+        List paths = new ArrayList();
+
+        // canonical paths
+        paths.add(PathFormat.parse("/", resolver));
+        paths.add(PathFormat.parse("/foo", resolver));
+        paths.add(PathFormat.parse("/foo/bar", resolver));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertTrue("path is not canonical: " + PathFormat.format(path, resolver), path.isCanonical());
+        }
+
+        paths.clear();
+
+        // not canonical paths
+        paths.add(PathFormat.parse("/foo/..", resolver));
+        paths.add(PathFormat.parse("/foo/.", resolver));
+        paths.add(PathFormat.parse("/foo/../bar", resolver));
+        paths.add(PathFormat.parse("/foo/./bar", resolver));
+        paths.add(PathFormat.parse("./foo", resolver));
+        paths.add(PathFormat.parse(".", resolver));
+        paths.add(PathFormat.parse("/foo/..", resolver));
+        paths.add(PathFormat.parse("/../foo/..", resolver));
+        paths.add(PathFormat.parse("/../foo/.", resolver));
+
+        for (Iterator it = paths.iterator(); it.hasNext(); ) {
+            Path path = (Path) it.next();
+            assertFalse("path is canonical: " + PathFormat.format(path, resolver), path.isCanonical());
+        }
+    }
+    
     private static class Test {
 
         private final String path;