You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2009/08/10 10:07:12 UTC

svn commit: r802684 - in /sling/trunk/bundles/jcr/resource/src: main/java/org/apache/sling/jcr/resource/ test/java/org/apache/sling/jcr/resource/

Author: cziegeler
Date: Mon Aug 10 08:07:12 2009
New Revision: 802684

URL: http://svn.apache.org/viewvc?rev=802684&view=rev
Log:
SLING-1077 : Try to encode map keys for value map implementations
Use the ISO9075 encoding for keys and add new tests.

Added:
    sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java   (with props)
Modified:
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java
    sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java?rev=802684&r1=802683&r2=802684&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java Mon Aug 10 08:07:12 2009
@@ -26,6 +26,7 @@
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 
+import org.apache.jackrabbit.util.ISO9075;
 import org.apache.sling.api.resource.PersistableValueMap;
 import org.apache.sling.api.resource.PersistenceException;
 
@@ -50,12 +51,12 @@
      * @see java.util.Map#get(java.lang.Object)
      */
     public Object get(Object key) {
-        Object value = cache.get(key);
-        if (value == null &&  !this.fullyRead ) {
-            value = read((String) key);
+        CacheEntry entry = cache.get(key);
+        if (entry == null && !this.fullyRead ) {
+            entry = read((String) key);
         }
 
-        return value;
+        return entry == null ? null : entry.defaultValue;
     }
 
     /**
@@ -69,6 +70,7 @@
         }
         this.changedProperties.addAll(this.cache.keySet());
         this.cache.clear();
+        this.valueCache.clear();
     }
 
     /**
@@ -77,11 +79,16 @@
     public Object put(String key, Object value) {
         readFully();
         final Object oldValue = this.get(key);
+        try {
+            this.cache.put(key, new CacheEntry(value, getNode()));
+        } catch (RepositoryException re) {
+            throw new IllegalArgumentException("Value can't be put into node: " + value, re);
+        }
+        this.valueCache.put(key, value);
         if ( this.changedProperties == null ) {
             this.changedProperties = new HashSet<String>();
         }
         this.changedProperties.add(key);
-        this.cache.put(key, value);
         return oldValue;
     }
 
@@ -106,6 +113,7 @@
     public Object remove(Object key) {
         readFully();
         final Object oldValue = this.cache.remove(key);
+        this.valueCache.remove(key);
         if ( this.changedProperties == null ) {
             this.changedProperties = new HashSet<String>();
         }
@@ -121,6 +129,7 @@
             this.changedProperties = null;
         }
         this.cache.clear();
+        this.valueCache.clear();
         this.fullyRead = false;
     }
 
@@ -135,10 +144,16 @@
         try {
             final Node node = getNode();
             for(final String key : this.changedProperties) {
+                final String name = ISO9075.encode(key);
                 if ( cache.containsKey(key) ) {
-                    JcrResourceUtil.setProperty(node, key, this.cache.get(key));
+                    final CacheEntry entry = cache.get(key);
+                    if ( entry.isMulti ) {
+                        node.setProperty(name, entry.values);
+                    } else {
+                        node.setProperty(name, entry.values[0]);
+                    }
                 } else {
-                    node.setProperty(key, (String)null);
+                    node.setProperty(name, (String)null);
                 }
             }
             node.save();

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java?rev=802684&r1=802683&r2=802684&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java Mon Aug 10 08:07:12 2009
@@ -36,53 +36,85 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFormatException;
 
+import org.apache.jackrabbit.util.ISO9075;
 import org.apache.sling.api.resource.ValueMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * An implementation of the value map based on a JCR node.
+ * @see JcrModifiablePropertyMap
+ */
 public class JcrPropertyMap implements ValueMap {
 
-    /** default log */
-    private Logger logger = LoggerFactory.getLogger(JcrPropertyMap.class);
+    /** default logger */
+    private static Logger LOGGER = LoggerFactory.getLogger(JcrPropertyMap.class);
 
+    /** The underlying node. */
     private final Node node;
 
-    protected final Map<String, Object> cache;
+    /** A cache for the properties. */
+    protected final Map<String, CacheEntry> cache;
 
+    /** A cache for the values. */
+    protected final Map<String, Object> valueCache;
+
+    /** Has the node been read completly? */
     protected boolean fullyRead;
 
-    public JcrPropertyMap(Node node) {
+    /**
+     * Constructor
+     * @param node The underlying node.
+     */
+    public JcrPropertyMap(final Node node) {
         this.node = node;
-        this.cache = new LinkedHashMap<String, Object>();
+        this.cache = new LinkedHashMap<String, CacheEntry>();
+        this.valueCache = new LinkedHashMap<String, Object>();
         this.fullyRead = false;
     }
 
+    /**
+     * Get the node.
+     */
     protected Node getNode() {
         return node;
     }
 
     // ---------- ValueMap
 
+    /**
+     * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Class)
+     */
     @SuppressWarnings("unchecked")
-    public <T> T get(String name, Class<T> type) {
+    public <T> T get(final String key, final Class<T> type) {
         if (type == null) {
-            return (T) get(name);
+            return (T) get(key);
         }
 
-        return convertToType(name, type);
+        CacheEntry entry = cache.get(key);
+        if (entry == null) {
+            entry = read(key);
+        }
+        if ( entry == null ) {
+            return null;
+        }
+        return convertToType(entry, type);
     }
 
+    /**
+     * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Object)
+     */
     @SuppressWarnings("unchecked")
-    public <T> T get(String name, T defaultValue) {
+    public <T> T get(final String key,final T defaultValue) {
         if (defaultValue == null) {
-            return (T) get(name);
+            return (T) get(key);
         }
 
         // special handling in case the default value implements one
         // of the interface types supported by the convertToType method
         Class<T> type = (Class<T>) normalizeClass(defaultValue.getClass());
 
-        T value = get(name, type);
+        T value = get(key, type);
         if (value == null) {
             value = defaultValue;
         }
@@ -92,12 +124,18 @@
 
     // ---------- Map
 
-    public Object get(Object key) {
-        Object value = cache.get(key);
-        if (value == null) {
-            value = read((String) key);
+    /**
+     * @see java.util.Map#get(java.lang.Object)
+     */
+    public Object get(final Object key) {
+        if ( key == null ) {
+            return null;
         }
-
+        CacheEntry entry = cache.get(key);
+        if (entry == null) {
+            entry = read((String)key);
+        }
+        final Object value = (entry == null ? null : entry.defaultValue);
         return value;
     }
 
@@ -113,7 +151,7 @@
      */
     public boolean containsValue(Object value) {
         readFully();
-        return cache.containsValue(value);
+        return valueCache.containsValue(value);
     }
 
     /**
@@ -136,7 +174,7 @@
      */
     public Set<java.util.Map.Entry<String, Object>> entrySet() {
         readFully();
-        return cache.entrySet();
+        return valueCache.entrySet();
     }
 
     /**
@@ -152,12 +190,12 @@
      */
     public Collection<Object> values() {
         readFully();
-        return cache.values();
+        return valueCache.values();
     }
 
     /**
      * Return the path of the current node.
-     * 
+     *
      * @throws IllegalStateException If a repository exception occurs
      */
     public String getPath() {
@@ -170,7 +208,7 @@
 
     // ---------- Helpers to access the node's property ------------------------
 
-    protected Object read(String key) {
+    protected CacheEntry read(final String key) {
 
         // if the node has been completely read, we need not check
         // again, as we certainly will not find the key
@@ -178,12 +216,14 @@
             return null;
         }
 
+        final String name = ISO9075.encode(key);
         try {
-            if (node.hasProperty(key)) {
-                Property prop = node.getProperty(key);
-                Object value = JcrResourceUtil.toJavaObject(prop);
-                cache.put(key, value);
-                return value;
+            if (node.hasProperty(name)) {
+                final Property prop = node.getProperty(name);
+                final CacheEntry entry = new CacheEntry(prop);
+                cache.put(key, entry);
+                valueCache.put(key, entry.defaultValue);
+                return entry;
             }
         } catch (RepositoryException re) {
             // TODO: log !!
@@ -199,9 +239,12 @@
                 PropertyIterator pi = node.getProperties();
                 while (pi.hasNext()) {
                     Property prop = pi.nextProperty();
-                    String key = prop.getName();
+                    final String name = prop.getName();
+                    final String key = ISO9075.encode(name);
                     if (!cache.containsKey(key)) {
-                        cache.put(key, JcrResourceUtil.toJavaObject(prop));
+                        final CacheEntry entry = new CacheEntry(prop);
+                        cache.put(key, entry);
+                        valueCache.put(key, entry.defaultValue);
                     }
                 }
                 fullyRead = true;
@@ -232,62 +275,55 @@
     // ---------- Implementation helper
 
     @SuppressWarnings("unchecked")
-    private <T> T convertToType(String name, Class<T> type) {
+    private <T> T convertToType(final CacheEntry entry, Class<T> type) {
         T result = null;
 
         try {
-            if (node.hasProperty(name)) {
-                Property prop = node.getProperty(name);
+            final boolean array = type.isArray();
 
-                boolean multiValue = prop.getDefinition().isMultiple();
-                boolean array = type.isArray();
+            if (entry.isMulti) {
 
-                if (multiValue) {
+                if (array) {
 
-                    Value[] values = prop.getValues();
-                    if (array) {
+                    result = (T) convertToArray(entry,
+                        type.getComponentType());
 
-                        result = (T) convertToArray(prop, values,
-                            type.getComponentType());
+                } else if (entry.values.length > 0) {
 
-                    } else if (values.length > 0) {
+                    result = convertToType(entry, -1, entry.values[0], type);
 
-                        result = convertToType(prop, -1, values[0], type);
-
-                    }
+                }
 
-                } else {
+            } else {
 
-                    Value value = prop.getValue();
-                    if (array) {
+                if (array) {
 
-                        result = (T) convertToArray(prop,
-                            new Value[] { value }, type.getComponentType());
+                    result = (T) convertToArray(entry,
+                            type.getComponentType());
 
-                    } else {
+                } else {
 
-                        result = convertToType(prop, -1, value, type);
+                    result = convertToType(entry, -1, entry.values[0], type);
 
-                    }
                 }
             }
 
         } catch (ValueFormatException vfe) {
-            logger.info("converToType: Cannot convert value of " + name
+            LOGGER.info("converToType: Cannot convert value of " + entry.defaultValue
                 + " to " + type, vfe);
         } catch (RepositoryException re) {
-            logger.info("converToType: Cannot get value of " + name, re);
+            LOGGER.info("converToType: Cannot get value of " + entry.defaultValue, re);
         }
 
         // fall back to nothing
         return result;
     }
 
-    private <T> T[] convertToArray(Property p, Value[] jcrValues, Class<T> type)
-            throws ValueFormatException, RepositoryException {
+    private <T> T[] convertToArray(final CacheEntry entry, Class<T> type)
+    throws ValueFormatException, RepositoryException {
         List<T> values = new ArrayList<T>();
-        for (int i = 0; i < jcrValues.length; i++) {
-            T value = convertToType(p, i, jcrValues[i], type);
+        for (int i = 0; i < entry.values.length; i++) {
+            T value = convertToType(entry, i, entry.values[i], type);
             if (value != null) {
                 values.add(value);
             }
@@ -300,7 +336,7 @@
     }
 
     @SuppressWarnings("unchecked")
-    private <T> T convertToType(Property p, int index, Value jcrValue,
+    private <T> T convertToType(final CacheEntry entry, int index, Value jcrValue,
             Class<T> type) throws ValueFormatException, RepositoryException {
 
         if (String.class == type) {
@@ -318,9 +354,9 @@
         } else if (Long.class == type) {
             if (jcrValue.getType() == PropertyType.BINARY) {
                 if (index == -1) {
-                    return (T) Long.valueOf(p.getLength());
+                    return (T) Long.valueOf(entry.property.getLength());
                 }
-                return (T) Long.valueOf(p.getLengths()[index]);
+                return (T) Long.valueOf(entry.property.getLengths()[index]);
             }
             return (T) Long.valueOf(jcrValue.getLong());
 
@@ -343,7 +379,7 @@
             return (T) jcrValue;
 
         } else if (Property.class == type) {
-            return (T) p;
+            return (T) entry.property;
         }
 
         // fallback in case of unsupported type
@@ -362,4 +398,42 @@
         }
         return type;
     }
+
+    protected static final class CacheEntry {
+        public final Property property;
+        public final boolean isMulti;
+        public final Value[] values;
+
+        public final Object defaultValue;
+
+        public CacheEntry(final Property prop)
+        throws RepositoryException {
+            this.property = prop;
+            if ( prop.getDefinition().isMultiple() ) {
+                isMulti = true;
+                values = prop.getValues();
+            } else {
+                isMulti = false;
+                values = new Value[] {prop.getValue()};
+            }
+            this.defaultValue = JcrResourceUtil.toJavaObject(prop);
+        }
+
+        public CacheEntry(final Object value, final Node node)
+        throws RepositoryException {
+            this.property = null;
+            this.defaultValue = value;
+            if ( value.getClass().isArray() ) {
+                this.isMulti = true;
+                final Object[] values = (Object[])value;
+                this.values = new Value[values.length];
+                for(int i=0; i<values.length; i++) {
+                    this.values[i] = JcrResourceUtil.createValue(values[i], node.getSession());
+                }
+            } else {
+                this.isMulti = false;
+                this.values = new Value[] {JcrResourceUtil.createValue(value, node.getSession())};
+            }
+        }
+    }
 }

Added: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java?rev=802684&view=auto
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java (added)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java Mon Aug 10 08:07:12 2009
@@ -0,0 +1,108 @@
+package org.apache.sling.jcr.resource;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+
+import org.apache.sling.api.resource.PersistableValueMap;
+import org.apache.sling.api.resource.ValueMap;
+
+public class JcrModifiablePropertyMapTest extends JcrPropertyMapTest {
+
+    private String rootPath;
+
+    private Node rootNode;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        rootPath = "/test_" + System.currentTimeMillis();
+        rootNode = getSession().getRootNode().addNode(rootPath.substring(1),
+            "nt:unstructured");
+
+        final Map<String, Object> values = this.initialSet();
+        for(Map.Entry<String, Object> entry : values.entrySet()) {
+            JcrResourceUtil.setProperty(rootNode, entry.getKey().toString(), entry.getValue());
+        }
+        session.save();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (rootNode != null) {
+            rootNode.remove();
+            session.save();
+        }
+
+        super.tearDown();
+    }
+
+    private Map<String, Object> initialSet() {
+        final Map<String, Object> values = new HashMap<String, Object>();
+        values.put("string", "test");
+        values.put("long", 1L);
+        values.put("bool", Boolean.TRUE);
+        return values;
+    }
+
+    public void testPut()
+    throws IOException {
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm, initialSet());
+        assertNull(pvm.get("something"));
+
+        // now put two values and check set again
+        pvm.put("something", "Another value");
+        pvm.put("string", "overwrite");
+
+        final Map<String, Object> currentlyStored = this.initialSet();
+        currentlyStored.put("something", "Another value");
+        currentlyStored.put("string", "overwrite");
+        assertContains(pvm, currentlyStored);
+
+        pvm.save();
+        assertContains(pvm, currentlyStored);
+
+        final PersistableValueMap pvm2 = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm2, currentlyStored);
+    }
+
+    public void testReset()
+    throws IOException {
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm, initialSet());
+        assertNull(pvm.get("something"));
+
+        // now put two values and check set again
+        pvm.put("something", "Another value");
+        pvm.put("string", "overwrite");
+
+        final Map<String, Object> currentlyStored = this.initialSet();
+        currentlyStored.put("something", "Another value");
+        currentlyStored.put("string", "overwrite");
+        assertContains(pvm, currentlyStored);
+
+        pvm.reset();
+        assertContains(pvm, initialSet());
+
+        final PersistableValueMap pvm2 = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm2, initialSet());
+    }
+
+    protected JcrPropertyMap createPropertyMap(final Node node) {
+        return new JcrModifiablePropertyMap(node);
+    }
+
+    /**
+     * Check that the value map contains all supplied values
+     */
+    private void assertContains(ValueMap map, Map<String, Object> values) {
+        for(Map.Entry<String, Object> entry : values.entrySet()) {
+            final Object stored = map.get(entry.getKey());
+            assertEquals(values.get(entry.getKey()), stored);
+        }
+    }
+}

Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMapTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java?rev=802684&r1=802683&r2=802684&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/JcrPropertyMapTest.java Mon Aug 10 08:07:12 2009
@@ -26,6 +26,8 @@
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
 
 public class JcrPropertyMapTest extends RepositoryTestBase {
@@ -68,68 +70,68 @@
 
     public void testTypeByClass() throws Exception {
         testValue(rootNode, "A String Value", String.class);
-        
+
         testValue(rootNode, 1l, Byte.class);
         testValue(rootNode, 1l, Short.class);
         testValue(rootNode, 1l, Integer.class);
         testValue(rootNode, 1l, Long.class);
-        
+
         testValue(rootNode, 1.0d, Float.class);
         testValue(rootNode, 1.0d, Double.class);
-        
+
         Calendar cal = Calendar.getInstance();
         testValue(rootNode, cal, Calendar.class);
         testValue(rootNode, cal, Date.class);
         testValue(rootNode, cal, Long.class);
     }
-    
+
     public void testTypeByDefaultValue() throws Exception {
         testValue(rootNode, "A String Value", "default");
-        
+
         testValue(rootNode, 1l, (byte) 10);
         testValue(rootNode, 1l, (short) 10);
         testValue(rootNode, 1l, 10);
         testValue(rootNode, 1l, 10l);
-        
+
         testValue(rootNode, 1.0d, 10.0f);
         testValue(rootNode, 1.0d, 10.0d);
-        
+
         long refTime = 1000l;
         Date refDate = new Date(refTime);
         Calendar refCal = Calendar.getInstance();
         refCal.setTimeInMillis(refTime);
-        
+
         Calendar cal = Calendar.getInstance();
         testValue(rootNode, cal, refCal);
         testValue(rootNode, cal, refDate);
         testValue(rootNode, cal, refTime);
     }
-    
+
     public void testDefaultValue() throws Exception {
         testDefaultValue(rootNode, "default");
-        
+
         testDefaultValue(rootNode, (byte) 10);
         testDefaultValue(rootNode, (short) 10);
         testDefaultValue(rootNode, 10);
         testDefaultValue(rootNode, 10l);
-        
+
         testDefaultValue(rootNode, 10.0f);
         testDefaultValue(rootNode, 10.0d);
-        
+
         long refTime = 1000l;
         Date refDate = new Date(refTime);
         Calendar refCal = Calendar.getInstance();
         refCal.setTimeInMillis(refTime);
-        
+
         testDefaultValue(rootNode, refCal);
         testDefaultValue(rootNode, refDate);
         testDefaultValue(rootNode, refTime);
     }
 
     public void testProperty() throws Exception {
-        JcrPropertyMap map = createProperty(rootNode, "Sample Value For Prop");
+        ValueMap map = createProperty(rootNode, "Sample Value For Prop");
         Property prop = rootNode.getProperty(PROP_NAME);
-        
+
         // explicite type
         Property result = map.get(PROP_NAME, Property.class);
         assertTrue(prop.isSame(result));
@@ -138,26 +140,26 @@
         Property defaultValue = rootNode.getProperty("jcr:primaryType");
         result = map.get(PROP_NAME, defaultValue);
         assertTrue(prop.isSame(result));
-        
+
         // default value
         result = map.get(PROP_NAME_NIL, defaultValue);
         assertSame(defaultValue, result);
     }
-    
+
     // ---------- internal
 
     private void testValue(Node node, Object value, Object defaultValue) throws RepositoryException {
-        JcrPropertyMap map = createProperty(rootNode, value);
+        ValueMap map = createProperty(rootNode, value);
         assertValueType(value, map.get(PROP_NAME, defaultValue), defaultValue.getClass());
     }
-    
+
     private void testDefaultValue(Node node, Object defaultValue) {
-        JcrPropertyMap map = new JcrPropertyMap(rootNode);
+        JcrPropertyMap map = createPropertyMap(rootNode);
         assertSame(defaultValue, map.get(PROP_NAME_NIL, defaultValue));
     }
-    
+
     private void testValue(Node node, Object value, Class<?> type) throws RepositoryException {
-        JcrPropertyMap map = createProperty(rootNode, value);
+        ValueMap map = createProperty(rootNode, value);
         assertValueType(value, map.get(PROP_NAME, type), type);
     }
 
@@ -166,10 +168,10 @@
 
         if (value instanceof Long && result instanceof Number) {
             assertEquals(((Number) value).longValue(), ((Number) result).longValue());
-            
+
         } else if (value instanceof Double && result instanceof Number) {
             assertEquals(((Number) value).doubleValue(), ((Number) result).doubleValue());
-            
+
         } else if (value instanceof Calendar) {
             long resultTime;
             if (result instanceof Date) {
@@ -183,18 +185,22 @@
                 return;
             }
             assertEquals(((Calendar) value).getTimeInMillis(), resultTime);
-            
+
         } else {
             assertEquals(value, result);
         }
     }
-    
+
+    protected JcrPropertyMap createPropertyMap(final Node node) {
+        return new JcrPropertyMap(node);
+    }
+
     private void testValue(Node node, Object value) throws RepositoryException {
-        JcrPropertyMap map = createProperty(node, value);
+        ValueMap map = createProperty(node, value);
         assertEquals(value, map.get(PROP_NAME));
     }
 
-    private JcrPropertyMap createProperty(Node node, Object value)
+    private ValueMap createProperty(Node node, Object value)
             throws RepositoryException {
         if (node.hasProperty(PROP_NAME)) {
             node.getProperty(PROP_NAME).remove();
@@ -224,6 +230,12 @@
         node.setProperty(PROP_NAME, jcrValue);
         node.save();
 
-        return new JcrPropertyMap(node);
+        return createPropertyMap(node);
+    }
+
+    public void testNames() throws Exception {
+        this.rootNode.setProperty(ISO9075.encode("a/a"), "value");
+        final ValueMap vm = this.createPropertyMap(this.rootNode);
+        assertEquals("value", vm.get("a/a"));
     }
 }