You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2010/05/17 16:00:02 UTC

svn commit: r945164 - in /jackrabbit/trunk: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/util/ jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/

Author: stefan
Date: Mon May 17 14:00:01 2010
New Revision: 945164

URL: http://svn.apache.org/viewvc?rev=945164&view=rev
Log:
JCR-2625: spi2davex: reduce memory footprint of Node/PropertyInfoImpl

Added:
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/util/StringCache.java
Modified:
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ItemInfoJSONHandler.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/NodeInfoImpl.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/PropertyInfoImpl.java
    jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/util/StringCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/util/StringCache.java?rev=945164&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/util/StringCache.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/util/StringCache.java Mon May 17 14:00:01 2010
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.util;
+
+import java.lang.ref.SoftReference;
+
+/**
+ * A few String utility functions.
+ */
+public class StringCache {
+
+    public static final boolean OBJECT_CACHE = getBooleanSetting("jackrabbit.stringCache", true);
+    public static final int OBJECT_CACHE_SIZE = nextPowerOf2(getIntSetting("jackrabbit.stringCacheSize", 1024));
+
+    private static SoftReference<String[]> softCache = new SoftReference<String[]>(null);
+
+    private StringCache() {
+        // utility class
+    }
+
+    private static int nextPowerOf2(int x) {
+        long i = 1;
+        while (i < x && i < (Integer.MAX_VALUE / 2)) {
+            i += i;
+        }
+        return (int) i;
+    }
+
+    private static boolean getBooleanSetting(String name, boolean defaultValue) {
+        String s = getProperty(name);
+        if (s != null) {
+            try {
+                return Boolean.valueOf(s).booleanValue();
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+        return defaultValue;
+    }
+
+    private static int getIntSetting(String name, int defaultValue) {
+        String s = getProperty(name);
+        if (s != null) {
+            try {
+                return Integer.decode(s).intValue();
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+        return defaultValue;
+    }
+
+    private static String getProperty(String name) {
+        try {
+            return System.getProperty(name);
+        } catch (Exception e) {
+            // SecurityException
+            // applets may not do that - ignore
+            return null;
+        }
+    }
+
+    private static String[] getCache() {
+        String[] cache;
+        // softCache can be null due to a Tomcat problem
+        // a workaround is disable the system property org.apache.
+        // catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
+        if (softCache != null) {
+            cache = softCache.get();
+            if (cache != null) {
+                return cache;
+            }
+        }
+        try {
+            cache = new String[OBJECT_CACHE_SIZE];
+        } catch (OutOfMemoryError e) {
+            return null;
+        }
+        softCache = new SoftReference<String[]>(cache);
+        return cache;
+    }
+
+    /**
+     * Get the string from the cache if possible. If the string has not been
+     * found, it is added to the cache. If there is such a string in the cache,
+     * that one is returned.
+     *
+     * @param s the original string
+     * @return a string with the same content, if possible from the cache
+     */
+    public static String cache(String s) {
+        if (!OBJECT_CACHE) {
+            return s;
+        }
+        if (s == null) {
+            return s;
+        } else if (s.length() == 0) {
+            return "";
+        }
+        int hash = s.hashCode();
+        String[] cache = getCache();
+        if (cache != null) {
+            int index = hash & (OBJECT_CACHE_SIZE - 1);
+            String cached = cache[index];
+            if (cached != null) {
+                if (s.equals(cached)) {
+                    return cached;
+                }
+            }
+            cache[index] = s;
+        }
+        return s;
+    }
+
+    /**
+     * Get a string from the cache, and if no such string has been found, create
+     * a new one with only this content. This solves out of memory problems if
+     * the string is a substring of another, large string. In Java, strings are
+     * shared, which could lead to memory problems. This avoid such problems.
+     *
+     * @param s the string
+     * @return a string that is guaranteed not be a substring of a large string
+     */
+    public static String fromCacheOrNew(String s) {
+        if (!OBJECT_CACHE) {
+            return s;
+        }
+        if (s == null) {
+            return s;
+        } else if (s.length() == 0) {
+            return "";
+        }
+        int hash = s.hashCode();
+        String[] cache = getCache();
+        int index = hash & (OBJECT_CACHE_SIZE - 1);
+        if (cache == null) {
+            return s;
+        }
+        String cached = cache[index];
+        if (cached != null) {
+            if (s.equals(cached)) {
+                return cached;
+            }
+        }
+        // create a new object that is not shared
+        // (to avoid out of memory if it is a substring of a big String)
+        // NOPMD
+        s = new String(s);
+        cache[index] = s;
+        return s;
+    }
+
+    /**
+     * Clear the cache. This method is used for testing.
+     */
+    public static void clearCache() {
+        softCache = new SoftReference<String[]>(null);
+    }
+
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ItemInfoJSONHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ItemInfoJSONHandler.java?rev=945164&r1=945163&r2=945164&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ItemInfoJSONHandler.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ItemInfoJSONHandler.java Mon May 17 14:00:01 2010
@@ -38,6 +38,8 @@ import org.apache.jackrabbit.spi.PathFac
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.util.StringCache;
 import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +51,7 @@ class ItemInfoJsonHandler implements Jso
 
     private static Logger log = LoggerFactory.getLogger(ItemInfoJsonHandler.class);
 
-    private static final int SPECIAL_JSON_PAIR = Integer.MAX_VALUE;
+    private static final String LEAF_NODE_HINT = "::NodeIteratorSize";
 
     private final List<ItemInfo> itemInfos;
     private final NamePathResolver resolver;
@@ -59,12 +61,19 @@ class ItemInfoJsonHandler implements Jso
     private final PathFactory pFactory;
     private final IdFactory idFactory;
 
+    private boolean expectingHintValue = false;
+
     private Name name;
-    private int propertyType;
     private int index = Path.INDEX_DEFAULT;
 
+    // temp. property state
+    private int propertyType;
+    private boolean multiValuedProperty = false;
+    private List<QValue> propValues = new ArrayList<QValue>();
+
     private Stack<NodeInfo> nodeInfos = new Stack<NodeInfo>();
-    private PropertyInfoImpl mvPropInfo;
+
+    private Stack<List<PropertyInfoImpl>> propInfoLists = new Stack<List<PropertyInfoImpl>>();
 
     ItemInfoJsonHandler(NamePathResolver resolver, NodeInfo nInfo,
                         String rootURI,
@@ -81,6 +90,7 @@ class ItemInfoJsonHandler implements Jso
         itemInfos = new ArrayList<ItemInfo>();
         itemInfos.add(nInfo);
         nodeInfos.push(nInfo);
+        propInfoLists.push(new ArrayList<PropertyInfoImpl>(8));
     }
 
     public void object() throws IOException {
@@ -93,7 +103,7 @@ class ItemInfoJsonHandler implements Jso
                 Path p = pFactory.create(currentPath, relPath, true);
                 NodeInfo nInfo = new NodeInfoImpl(id, p);
                 nodeInfos.push(nInfo);
-                itemInfos.add(nInfo);
+                propInfoLists.push(new ArrayList<PropertyInfoImpl>(8));
             } catch (RepositoryException e) {
                 throw new IOException(e.getMessage());
             }
@@ -103,7 +113,9 @@ class ItemInfoJsonHandler implements Jso
     public void endObject() throws IOException {
         try {
             NodeInfoImpl nInfo = (NodeInfoImpl) nodeInfos.pop();
-            nInfo.resolveUUID(idFactory);
+            List<PropertyInfoImpl> props = propInfoLists.pop();
+            // all required information to create a node info should now be gathered
+            nInfo.setPropertyInfos(props.toArray(new PropertyInfoImpl[props.size()]), idFactory);
             NodeInfo parent = getCurrentNodeInfo();
             if (parent != null) {
                 if (nInfo.getPath().getAncestor(1).equals(parent.getPath())) {
@@ -113,62 +125,80 @@ class ItemInfoJsonHandler implements Jso
                     log.debug("NodeInfo '"+ nInfo.getPath() + "' out of hierarchy. Parent path = " + parent.getPath());
                 }
             }
-            if (!nInfo.isCompleted()) {
+            if (nInfo.isCompleted()) {
+                itemInfos.addAll(props);
+                itemInfos.add(nInfo);
+            } else {
                 log.debug("Incomplete NodeInfo '"+ nInfo.getPath() + "' -> Only present as ChildInfo with its parent.");
-                itemInfos.remove(nInfo);
             }
         } catch (RepositoryException e) {
             throw new IOException(e.getMessage());
+        } finally {
+            // reset all node-related handler state
+            name = null;
+            index = Path.INDEX_DEFAULT;
         }
     }
 
     public void array() throws IOException {
-        try {
-            mvPropInfo = createPropertyInfo(null, true);
-        } catch (RepositoryException e) {
-            throw new IOException(e.getMessage());
-        }
+        multiValuedProperty = true;
+        propValues.clear();
     }
 
     public void endArray() throws IOException {
         try {
-            // make sure that type is set for mv-properties with empty value array.
-            if (propertyType == PropertyType.UNDEFINED &&
-                    mvPropInfo.numberOfValues() == 0) {
-                int type = vFactory.retrieveType(getValueURI());
-                mvPropInfo.setType(type);
+            if (propertyType == PropertyType.UNDEFINED) {
+                if (propValues.isEmpty()) {
+                    // make sure that type is set for mv-properties with empty value array.
+                    propertyType = vFactory.retrieveType(getValueURI());
+                } else {
+                    propertyType = propValues.get(0).getType();
+                }
             }
-            mvPropInfo.checkCompleted();
-            getCurrentNodeInfo().addPropertyInfo(mvPropInfo, idFactory);
-            mvPropInfo = null;
+            // create multi-valued property info
+            NodeInfoImpl parent = getCurrentNodeInfo();
+            Path p = pFactory.create(parent.getPath(), name, true);
+            PropertyId id = idFactory.createPropertyId(parent.getId(), name);
+            PropertyInfoImpl propInfo = new PropertyInfoImpl(id, p, propertyType, propValues.toArray(new QValue[propValues.size()]));
+            propInfo.checkCompleted();
+            getCurrentPropInfos().add(propInfo);
         } catch (RepositoryException e) {
             throw new IOException(e.getMessage());
+        } finally {
+            // reset property-related handler state
+            propertyType = PropertyType.UNDEFINED;
+            multiValuedProperty = false;
+            propValues.clear();
+            name = null;
         }
     }
 
     public void key(String key) throws IOException {
+        expectingHintValue = false;
         try {
-            if (key.equals("::NodeIteratorSize")) {
-                propertyType = SPECIAL_JSON_PAIR;
-                // TODO: if additional JSON pairs are created -> set name
+            if (key.equals(LEAF_NODE_HINT)) {
+                expectingHintValue = true;
+                // TODO: remember name of hint if there will be additional types of hints
+                name = null;
             } else if (key.startsWith(":")) {
-                // binary property
-                name = resolver.getQName(key.substring(1));
-                propertyType = PropertyType.BINARY;
+                expectingHintValue = true;
+                // either
+                //   :<nameOfProperty> : "PropertyTypeName"
+                // or
+                //   :<nameOfBinaryProperty> : <lengthOfBinaryProperty>
+                //name = resolver.getQName(key.substring(1));
+                name = resolver.getQName(StringCache.fromCacheOrNew(key.substring(1)));
                 index = Path.INDEX_DEFAULT;
             } else if (key.endsWith("]")) {
                 // sns-node name
                 int pos = key.lastIndexOf('[');
-                name = resolver.getQName(key.substring(0, pos));
+                //name = resolver.getQName(key.substring(0, pos));
+                name = resolver.getQName(StringCache.fromCacheOrNew(key.substring(0, pos)));
                 propertyType = PropertyType.UNDEFINED;
                 index = Integer.parseInt(key.substring(pos + 1, key.length() - 1));
             } else {
                 // either node or property
-                Name previousName = name;
-                name = resolver.getQName(key);
-                propertyType = guessPropertyType(name, previousName);
-                // property type is defined through json value OR special property
-                // :propertyName = type.
+                name = resolver.getQName(StringCache.cache(key));
                 index = Path.INDEX_DEFAULT;
             }
         } catch (RepositoryException e) {
@@ -176,21 +206,27 @@ class ItemInfoJsonHandler implements Jso
         }
     }
 
+    /**
+     * there is currently one special string-value hint:
+     *
+     *   :<nameOfProperty> : "PropertyTypeName"
+     *
+     * @param value The value.
+     * @throws IOException
+     */
     public void value(String value) throws IOException {
+        if (expectingHintValue) {
+            // :<nameOfProperty> : "PropertyTypeName"
+            propertyType = PropertyType.valueFromName(value);
+            return;
+        }
         try {
             QValue v;
             switch (propertyType) {
-                case SPECIAL_JSON_PAIR:
-                    // currently no special boolean value pair -> ignore
-                    return;
-                case PropertyType.BINARY:
-                    // key started with ':' but value is String instead of
-                    // long. value therefore reflects the property type.
-                    // -> reset the property type.
-                    // -> omit creation of value AND call to value(QValue)
-                    propertyType = PropertyType.valueFromName(value);
-                    return;
                 case PropertyType.UNDEFINED:
+                    if (!NameConstants.JCR_UUID.equals(name)) {
+                        value = StringCache.cache(value);
+                    }
                     v = vFactory.create(value, PropertyType.STRING);
                     break;
                 case PropertyType.NAME:
@@ -209,8 +245,8 @@ class ItemInfoJsonHandler implements Jso
     }
 
     public void value(boolean value) throws IOException {
-        if (propertyType == SPECIAL_JSON_PAIR) {
-            // currently no special boolean value pair -> ignore
+        if (expectingHintValue) {
+            // there are currently no special boolean value hints:
             return;
         }
         try {
@@ -220,30 +256,46 @@ class ItemInfoJsonHandler implements Jso
         }
     }
 
+    /**
+     * there are currently 2 types of special long value hints:
+     *
+     * a) ::NodeIteratorSize : 0
+     *    ==> denotes the current node as leaf node
+     *
+     * b) :<nameOfBinaryProperty> : <lengthOfBinaryProperty>
+     *
+     * @param value The value.
+     * @throws IOException
+     */
     public void value(long value) throws IOException {
-        if (propertyType == SPECIAL_JSON_PAIR) {
-            NodeInfoImpl parent = getCurrentNodeInfo();
-            if (parent != null) {
-                parent.setNumberOfChildNodes(value);
+        if (expectingHintValue) {
+            if (name == null) {
+                // ::NodeIteratorSize : 0
+                NodeInfoImpl parent = getCurrentNodeInfo();
+                if (parent != null) {
+                    parent.markAsLeafNode();
+                }
+            } else {
+                // :<nameOfBinaryProperty> : <lengthOfBinaryProperty>
+                propertyType = PropertyType.BINARY;
+                try {
+                    int indx = (!multiValuedProperty) ? -1 : propValues.size();
+                    value(vFactory.create(value, getValueURI(), indx));
+                } catch (RepositoryException e) {
+                    throw new IOException(e.getMessage());
+                }
             }
             return;
         }
         try {
-            QValue v;
-            if (propertyType == PropertyType.BINARY) {
-                int indx = (mvPropInfo == null) ? -1 : mvPropInfo.numberOfValues();
-                v = vFactory.create(value, getValueURI(), indx);
-            } else {
-                v = vFactory.create(value);
-            }
-            value(v);
+            value(vFactory.create(value));
         } catch (RepositoryException e) {
             throw new IOException(e.getMessage());
         }
     }
 
     public void value(double value) throws IOException {
-        if (propertyType == SPECIAL_JSON_PAIR) {
+        if (expectingHintValue) {
             // currently no special double value pair -> ignore
             return;
         }
@@ -261,10 +313,31 @@ class ItemInfoJsonHandler implements Jso
      * @throws RepositoryException
      */
     private void value(QValue value) throws RepositoryException {
-        if (mvPropInfo == null) {
-            createPropertyInfo(value, false);
+        if (!multiValuedProperty) {
+            try {
+                if (propertyType == PropertyType.UNDEFINED) {
+                    propertyType = value.getType();
+                }
+                // create single-valued property info
+                NodeInfoImpl parent = getCurrentNodeInfo();
+                Path p = pFactory.create(parent.getPath(), name, true);
+                PropertyId id = idFactory.createPropertyId(parent.getId(), name);
+                PropertyInfoImpl propInfo = new PropertyInfoImpl(id, p, propertyType, value);
+                propInfo.checkCompleted();
+                // add property info to current list, will be processed on endObject() event
+                getCurrentPropInfos().add(propInfo);
+            } finally {
+                // reset property-related handler state
+                propertyType = PropertyType.UNDEFINED;
+                multiValuedProperty = false;
+                propValues.clear();
+                name = null;
+                expectingHintValue = false;
+            }
         } else {
-            mvPropInfo.addValue(value);
+            // multi-valued property
+            // add value to current list, will be processed on endArray() event
+            propValues.add(value);
         }
     }
 
@@ -273,48 +346,17 @@ class ItemInfoJsonHandler implements Jso
     }
 
     private NodeInfoImpl getCurrentNodeInfo() {
-        return  (nodeInfos.isEmpty()) ? null : (NodeInfoImpl) nodeInfos.peek();
+        return (nodeInfos.isEmpty()) ? null : (NodeInfoImpl) nodeInfos.peek();
     }
 
-    private PropertyInfoImpl createPropertyInfo(QValue value, boolean isMultiValued) throws RepositoryException {
-        NodeInfoImpl parent = getCurrentNodeInfo();
-        Path p = pFactory.create(parent.getPath(), name, true);
-        PropertyId id = idFactory.createPropertyId(parent.getId(), name);
-
-        PropertyInfoImpl pInfo;
-        if (isMultiValued) {
-            pInfo = new PropertyInfoImpl(id, p, propertyType);
-            // not added to parent but upon having read all values.
-        } else {
-            pInfo = new PropertyInfoImpl(id, p, propertyType, value);
-            parent.addPropertyInfo(pInfo, idFactory);
-        }
-        itemInfos.add(pInfo);
-        return pInfo;
+    private List<PropertyInfoImpl> getCurrentPropInfos() {
+        return (propInfoLists.isEmpty()) ? null : propInfoLists.peek();
     }
 
     private String getValueURI() throws RepositoryException {
-        Path propertyPath;
-        if (mvPropInfo == null) {
-            propertyPath = pFactory.create(getCurrentNodeInfo().getPath(), name, true);
-        } else {
-            propertyPath = mvPropInfo.getPath();
-        }
+        Path propertyPath = pFactory.create(getCurrentNodeInfo().getPath(), name, true);
         StringBuffer sb = new StringBuffer(rootURI);
         sb.append(Text.escapePath(resolver.getJCRPath(propertyPath)));
         return sb.toString();
     }
-
-    private int guessPropertyType(Name name, Name previousName) {
-        if (name.equals(previousName)) {
-            // property has been previously retrieved from :name : "typeName"
-            // entry in the JSON string. if by coincidence the previous key
-            // is equal but belongs to an JSON object (-> node) the prop type
-            // has been reset to UNDEFINED anyway.
-            return propertyType;
-        } else {
-            // default: determine type upon Property.getType() only.
-            return PropertyType.UNDEFINED;
-        }
-    }
 }

Modified: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/NodeInfoImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/NodeInfoImpl.java?rev=945164&r1=945163&r2=945164&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/NodeInfoImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/NodeInfoImpl.java Mon May 17 14:00:01 2010
@@ -19,9 +19,7 @@ package org.apache.jackrabbit.spi2davex;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 
 import javax.jcr.RepositoryException;
 
@@ -32,7 +30,6 @@ import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.NodeInfo;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PropertyId;
-import org.apache.jackrabbit.spi.PropertyInfo;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 
@@ -41,13 +38,13 @@ import org.apache.jackrabbit.spi.commons
  */
 public class NodeInfoImpl extends ItemInfoImpl implements NodeInfo {
 
+    // data deduced from property values
     private NodeId id;
-    private String uniqueID;
     private Name primaryNodeTypeName;
     private Name[] mixinNodeTypeNames = Name.EMPTY_ARRAY;
 
-    private final Set<PropertyInfo> propertyInfos = new LinkedHashSet<PropertyInfo>();
-    private Set<ChildInfo> childInfos = null;
+    private final List<PropertyId> propertyIds = new ArrayList<PropertyId>(8);
+    private List<ChildInfo> childInfos = null;
 
     /**
      * Creates a new <code>NodeInfo</code>.
@@ -83,11 +80,7 @@ public class NodeInfoImpl extends ItemIn
     }
 
     public Iterator<PropertyId> getPropertyIds() {
-        List<PropertyId> l = new ArrayList<PropertyId>();
-        for (PropertyInfo propertyInfo : propertyInfos) {
-            l.add(propertyInfo.getId());
-        }
-        return l.iterator();
+        return propertyIds.iterator();
     }
 
     public Iterator<ChildInfo> getChildInfos() {
@@ -95,58 +88,55 @@ public class NodeInfoImpl extends ItemIn
     }
 
     //--------------------------------------------------------------------------
-    void addPropertyInfo(PropertyInfoImpl propInfo, IdFactory idFactory) throws RepositoryException {
-        propertyInfos.add(propInfo);
-
-        Name pn = propInfo.getId().getName();
-        if (NameConstants.JCR_UUID.equals(pn)) {
-            uniqueID = propInfo.getValues()[0].getString();
-            id = idFactory.createNodeId(uniqueID);
-        } else if (NameConstants.JCR_PRIMARYTYPE.equals(pn)) {
-            primaryNodeTypeName = propInfo.getValues()[0].getName();
-        } else if (NameConstants.JCR_MIXINTYPES.equals(pn)) {
-            QValue[] vs = propInfo.getValues();
-            Name[] mixins = new Name[vs.length];
-            for (int i = 0; i < vs.length; i++) {
-                mixins[i] = vs[i].getName();
+    void setPropertyInfos(PropertyInfoImpl[] propInfos, IdFactory idFactory) throws RepositoryException {
+        boolean resolveUUID = false;
+        for (PropertyInfoImpl propInfo : propInfos) {
+            Name pn = propInfo.getId().getName();
+            if (NameConstants.JCR_UUID.equals(pn)) {
+                id = idFactory.createNodeId(propInfo.getValues()[0].getString());
+                resolveUUID = true;
+            } else if (NameConstants.JCR_PRIMARYTYPE.equals(pn)) {
+                primaryNodeTypeName = propInfo.getValues()[0].getName();
+            } else if (NameConstants.JCR_MIXINTYPES.equals(pn)) {
+                QValue[] vs = propInfo.getValues();
+                Name[] mixins = new Name[vs.length];
+                for (int i = 0; i < vs.length; i++) {
+                    mixins[i] = vs[i].getName();
+                }
+                mixinNodeTypeNames = mixins;
             }
-            mixinNodeTypeNames = mixins;
         }
-    }
 
-    void resolveUUID(IdFactory idFactory) {
-        if (uniqueID != null) {
-            for (Object o : propertyInfos) {
-                PropertyInfoImpl propInfo = (PropertyInfoImpl) o;
+        propertyIds.clear();
+        for (PropertyInfoImpl propInfo : propInfos) {
+            if (resolveUUID) {
                 propInfo.setId(idFactory.createPropertyId(id, propInfo.getName()));
             }
+            propertyIds.add(propInfo.getId());
         }
+
     }
 
     void addChildInfo(ChildInfo childInfo) {
         if (childInfos == null) {
-            childInfos = new LinkedHashSet<ChildInfo>();
+            childInfos = new ArrayList<ChildInfo>();
         }
         childInfos.add(childInfo);
     }
 
-    void setNumberOfChildNodes(long numberOfChildNodes) {
-        if (numberOfChildNodes == 0) {
-            childInfos = Collections.<ChildInfo>emptySet();
-        } // else: wait for calls to #addChildInfo
+    void markAsLeafNode() {
+        childInfos = Collections.emptyList();
     }
 
     boolean isCompleted() {
-        return !(id == null || primaryNodeTypeName == null || propertyInfos.isEmpty());
-    }
-
-    void checkCompleted() throws RepositoryException {
-        if (!isCompleted()) {
-            throw new RepositoryException("Incomplete NodeInfo");
-        }
+        return (id != null && primaryNodeTypeName != null && !propertyIds.isEmpty());
     }
 
     String getUniqueID() {
-        return uniqueID;
+        if (id.getUniqueID() != null && id.getPath() == null) {
+            return id.getUniqueID();
+        } else {
+            return null;
+        }
     }
 }
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/PropertyInfoImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/PropertyInfoImpl.java?rev=945164&r1=945163&r2=945164&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/PropertyInfoImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/PropertyInfoImpl.java Mon May 17 14:00:01 2010
@@ -23,34 +23,33 @@ import org.apache.jackrabbit.spi.QValue;
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * <code>PropertyInfoImpl</code>...
  */
 public class PropertyInfoImpl extends ItemInfoImpl implements PropertyInfo {
 
-    private final boolean isMultiValued;
+    private final boolean multiValued;
 
     private PropertyId id;
     private int propertyType;
-    private List<QValue> values = new ArrayList<QValue>();
+    private QValue[] values = QValue.EMPTY_ARRAY;
 
     public PropertyInfoImpl(PropertyId id, Path path, int propertyType,
-                            QValue singleValue) throws RepositoryException {
+                            QValue value) throws RepositoryException {
         super(path, false);
         this.id = id;
         this.propertyType = propertyType;
-        isMultiValued = false;
-        values.add(singleValue);
+        multiValued = false;
+        values = new QValue[]{value};
     }
 
-    public PropertyInfoImpl(PropertyId id, Path path, int propertyType) throws RepositoryException {
+    public PropertyInfoImpl(PropertyId id, Path path, int propertyType, QValue[] values) throws RepositoryException {
         super(path, false);
         this.id = id;
         this.propertyType = propertyType;
-        isMultiValued = true;
+        this.values = values;
+        multiValued = true;
     }
 
     //-------------------------------------------------------< PropertyInfo >---
@@ -68,41 +67,24 @@ public class PropertyInfoImpl extends It
     }
 
     public boolean isMultiValued() {
-        return isMultiValued;
+        return multiValued;
     }
 
     public QValue[] getValues() {
-        return values.toArray(new QValue[values.size()]);
+        return values;
     }
 
     //--------------------------------------------------------------------------
-    int numberOfValues() {
-        return values.size();
-    }
-
     void setId(PropertyId id) {
         this.id = id;
     }
 
-    void addValue(QValue value) throws RepositoryException {
-        if (values == null) {
-            values = new ArrayList<QValue>();
-        } else if (!isMultiValued && !values.isEmpty()) {
-            throw new RepositoryException("Attempt to add multiple values to a single valued PropertyInfo");
-        }
-        values.add(value);
-    }
-
-    void setType(int propertyType) {
-        this.propertyType = propertyType;
-    }
-
     void checkCompleted() throws RepositoryException {
         if (id == null) {
             throw new RepositoryException("Incomplete PropertyInfo: id missing.");
         }
-        if (values.size() == 0 && propertyType == PropertyType.UNDEFINED) {
-            throw new RepositoryException("Incomplete PropertyInfo: missing type of multivalued property.");
+        if (propertyType == PropertyType.UNDEFINED) {
+            throw new RepositoryException("Incomplete PropertyInfo: missing type of property.");
         }
     }
 }

Modified: jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java?rev=945164&r1=945163&r2=945164&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java (original)
+++ jackrabbit/trunk/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java Mon May 17 14:00:01 2010
@@ -94,7 +94,7 @@ class ValueLoader {
 
     //--------------------------------------------------------------------------
     /**
-     * Internal inteface
+     * Internal interface
      */
     interface Target {
         /**