BUG-4803: introduce unordered offset maps 26/31726/7
authorRobert Varga <rovarga@cisco.com>
Mon, 21 Dec 2015 18:42:25 +0000 (19:42 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Mon, 4 Jan 2016 10:49:28 +0000 (10:49 +0000)
This patch introduces the static factories and methods which allow users
to select the appropriate implementation, either to retain or to ignore
iteration order.

Change-Id: I07dcf77927660461cbd266439463e8d64a1c89db
Signed-off-by: Robert Varga <rovarga@cisco.com>
common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java
common/util/src/main/java/org/opendaylight/yangtools/util/MutableOffsetMap.java
common/util/src/main/java/org/opendaylight/yangtools/util/OffsetMapCache.java
common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMap.java
common/util/src/test/java/org/opendaylight/yangtools/util/OffsetMapTest.java
common/util/src/test/java/org/opendaylight/yangtools/util/SharedSingletonMapTest.java
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java

index dc184b25ad361a99085539ee45f2d9d4294e91d7..7e5dd349ff0b268886c966792a3134e4446feef8 100644 (file)
@@ -46,20 +46,42 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
 
         @Override
         public MutableOffsetMap<K, V> toModifiableMap() {
-            return MutableOffsetMap.copyOf(this);
+            return MutableOffsetMap.orderedCopyOf(this);
         }
 
         @Override
-        Map<K, Integer> resolveKeys(final List<K> keys) {
-            return OffsetMapCache.orderedOffsets(keys);
+        void setFields(final List<K> keys, final V[] values) throws IOException {
+            setField(this, OFFSETS_FIELD, OffsetMapCache.orderedOffsets(keys));
+            setField(this, ARRAY_FIELD, values);
+        }
+    }
+
+    static final class Unordered<K, V> extends ImmutableOffsetMap<K, V> {
+        private static final long serialVersionUID = 1L;
+
+        Unordered(final Map<K, Integer> offsets, final V[] objects) {
+            super(offsets, objects);
+        }
+
+        @Override
+        public MutableOffsetMap<K, V> toModifiableMap() {
+            return MutableOffsetMap.unorderedCopyOf(this);
+        }
+
+        @Override
+        void setFields(final List<K> keys, final V[] values) throws IOException {
+            final Map<K, Integer> offsets = OffsetMapCache.unorderedOffsets(keys);
+
+            setField(this, OFFSETS_FIELD, offsets);
+            setField(this, ARRAY_FIELD, OffsetMapCache.adjustedArray(offsets, keys, values));
         }
     }
 
     private static final long serialVersionUID = 1L;
 
-    private final Map<K, Integer> offsets;
-    private final V[] objects;
-    private int hashCode;
+    private transient final Map<K, Integer> offsets;
+    private transient final V[] objects;
+    private transient int hashCode;
 
     /**
      * Construct a new instance backed by specified key-to-offset map and array of objects.
@@ -77,7 +99,7 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
     @Override
     public abstract MutableOffsetMap<K, V> toModifiableMap();
 
-    abstract Map<K, Integer> resolveKeys(List<K> keys);
+    abstract void setFields(List<K> keys, V[] values) throws IOException;
 
     /**
      * Create an {@link ImmutableOffsetMap} as a copy of an existing map. This is actually not completely true,
@@ -89,8 +111,25 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
      *
      * @param m Input map, may not be null.
      * @return An isolated, immutable copy of the input map
+     * @deprecated Use {@link #orderedCopyOf(Map)} or {@link #unorderedCopyOf(Map)} instead.
      */
+    @Deprecated
     @Nonnull public static <K, V> Map<K, V> copyOf(@Nonnull final Map<K, V> m) {
+        return orderedCopyOf(m);
+    }
+
+    /**
+     * Create an {@link ImmutableOffsetMap} as a copy of an existing map. This is actually not completely true,
+     * as this method returns an {@link ImmutableMap} for empty and singleton inputs, as those are more memory-efficient.
+     * This method also recognizes {@link ImmutableOffsetMap} on input, and returns it back without doing anything else.
+     * It also recognizes {@link MutableOffsetMap} (as returned by {@link #toModifiableMap()}) and makes an efficient
+     * copy of its contents. All other maps are converted to an {@link ImmutableOffsetMap} with the same iteration
+     * order as input.
+     *
+     * @param m Input map, may not be null.
+     * @return An isolated, immutable copy of the input map
+     */
+    @Nonnull public static <K, V> Map<K, V> orderedCopyOf(@Nonnull final Map<K, V> m) {
         // Prevent a copy. Note that ImmutableMap is not listed here because of its potentially larger keySet overhead.
         if (m instanceof ImmutableOffsetMap || m instanceof SharedSingletonMap) {
             return m;
@@ -109,7 +148,7 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
         if (size == 1) {
             // Efficient single-entry implementation
             final Entry<K, V> e = m.entrySet().iterator().next();
-            return SharedSingletonMap.of(e.getKey(), e.getValue());
+            return SharedSingletonMap.orderedOf(e.getKey(), e.getValue());
         }
 
         final Map<K, Integer> offsets = OffsetMapCache.orderedOffsets(m.keySet());
@@ -122,6 +161,49 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
         return new Ordered<>(offsets, array);
     }
 
+    /**
+     * Create an {@link ImmutableOffsetMap} as a copy of an existing map. This is actually not completely true,
+     * as this method returns an {@link ImmutableMap} for empty and singleton inputs, as those are more memory-efficient.
+     * This method also recognizes {@link ImmutableOffsetMap} on input, and returns it back without doing anything else.
+     * It also recognizes {@link MutableOffsetMap} (as returned by {@link #toModifiableMap()}) and makes an efficient
+     * copy of its contents. All other maps are converted to an {@link ImmutableOffsetMap}. Iterator order is not
+     * guaranteed to be retained.
+     *
+     * @param m Input map, may not be null.
+     * @return An isolated, immutable copy of the input map
+     */
+    @Nonnull public static <K, V> Map<K, V> unorderedCopyOf(@Nonnull final Map<K, V> m) {
+        // Prevent a copy. Note that ImmutableMap is not listed here because of its potentially larger keySet overhead.
+        if (m instanceof ImmutableOffsetMap || m instanceof SharedSingletonMap) {
+            return m;
+        }
+
+        // Familiar and efficient to copy
+        if (m instanceof MutableOffsetMap) {
+            return ((MutableOffsetMap<K, V>) m).toUnmodifiableMap();
+        }
+
+        final int size = m.size();
+        if (size == 0) {
+            // Shares a single object
+            return ImmutableMap.of();
+        }
+        if (size == 1) {
+            // Efficient single-entry implementation
+            final Entry<K, V> e = m.entrySet().iterator().next();
+            return SharedSingletonMap.unorderedOf(e.getKey(), e.getValue());
+        }
+
+        final Map<K, Integer> offsets = OffsetMapCache.unorderedOffsets(m.keySet());
+        @SuppressWarnings("unchecked")
+        final V[] array = (V[]) new Object[offsets.size()];
+        for (Entry<K, V> e : m.entrySet()) {
+            array[offsets.get(e.getKey())] = e.getValue();
+        }
+
+        return new Unordered<>(offsets, array);
+    }
+
     @Override
     public final int size() {
         return offsets.size();
@@ -320,9 +402,9 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
         return f;
     }
 
-    private void setField(final Field field, final Object value) throws IOException {
+    private static void setField(final ImmutableOffsetMap<?, ?> map, final Field field, final Object value) throws IOException {
         try {
-            field.set(this, value);
+            field.set(map, value);
         } catch (IllegalArgumentException | IllegalAccessException e) {
             throw new IOException("Failed to set field " + field, e);
         }
@@ -340,7 +422,6 @@ public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K
             values[i] = (V)in.readObject();
         }
 
-        setField(OFFSETS_FIELD, resolveKeys(keys));
-        setField(ARRAY_FIELD, values);
+        setFields(keys, values);
     }
 }
index b49b51d493802f437b3cf7fd19140a2e737097ed..2c3ea479a247f4f932aedcc71c6ead4e00676bc8 100644 (file)
@@ -69,6 +69,46 @@ public abstract class MutableOffsetMap<K, V> extends AbstractMap<K, V> implement
         UnmodifiableMapPhase<K, V> unmodifiedMap(final Map<K, Integer> offsets, final V[] objects) {
             return new ImmutableOffsetMap.Ordered<>(offsets, objects);
         }
+
+        @Override
+        SharedSingletonMap<K, V> singletonMap() {
+            return SharedSingletonMap.orderedCopyOf(this);
+        }
+    }
+
+    static final class Unordered<K, V> extends MutableOffsetMap<K, V> {
+        Unordered() {
+            super(new HashMap<K, V>());
+        }
+
+        Unordered(final Map<K, V> source) {
+            super(OffsetMapCache.unorderedOffsets(source.keySet()), source, new HashMap<K, V>());
+        }
+
+        Unordered(final Map<K, Integer> offsets, final V[] objects) {
+            super(offsets, objects, new HashMap<K, V>());
+        }
+
+        @Override
+        Object removedObject() {
+            return null;
+        }
+
+        @Override
+        UnmodifiableMapPhase<K, V> modifiedMap(final List<K> keys, final V[] objects) {
+            final Map<K, Integer> offsets = OffsetMapCache.unorderedOffsets(keys);
+            return new ImmutableOffsetMap.Unordered<>(offsets, OffsetMapCache.adjustedArray(offsets, keys, objects));
+        }
+
+        @Override
+        UnmodifiableMapPhase<K, V> unmodifiedMap(final Map<K, Integer> offsets, final V[] objects) {
+            return new ImmutableOffsetMap.Unordered<>(offsets, objects);
+        }
+
+        @Override
+        SharedSingletonMap<K, V> singletonMap() {
+            return SharedSingletonMap.unorderedCopyOf(this);
+        }
     }
 
     private static final Object[] EMPTY_ARRAY = new Object[0];
@@ -103,25 +143,58 @@ public abstract class MutableOffsetMap<K, V> extends AbstractMap<K, V> implement
         this.needClone = false;
     }
 
+    /**
+     * @deprecated Use {@link #orderedCopyOf(Map)} or {@link #unorderedCopyOf(Map)} instead.
+     */
+    @Deprecated
     public static <K, V> MutableOffsetMap<K, V> copyOf(final Map<K, V> m) {
-        if (m instanceof MutableOffsetMap) {
-            return ((MutableOffsetMap<K, V>) m).clone();
+        return orderedCopyOf(m);
+    }
+
+    public static <K, V> MutableOffsetMap<K, V> orderedCopyOf(final Map<K, V> m) {
+        if (m instanceof Ordered) {
+            return ((Ordered<K, V>) m).clone();
         }
         if (m instanceof ImmutableOffsetMap) {
             final ImmutableOffsetMap<K, V> om = (ImmutableOffsetMap<K, V>) m;
-            return new MutableOffsetMap.Ordered<>(om.offsets(), om.objects());
+            return new Ordered<>(om.offsets(), om.objects());
         }
 
-        return new MutableOffsetMap.Ordered<>(m);
+        return new Ordered<>(m);
     }
 
+    public static <K, V> MutableOffsetMap<K, V> unorderedCopyOf(final Map<K, V> m) {
+        if (m instanceof Unordered) {
+            return ((Unordered<K, V>) m).clone();
+        }
+        if (m instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<K, V> om = (ImmutableOffsetMap<K, V>) m;
+            return new Unordered<>(om.offsets(), om.objects());
+        }
+
+        return new Unordered<>(m);
+    }
+
+    /**
+     * @deprecated Use {@link #ordered()} or {@link #unordered()} instead.
+     */
+    @Deprecated
     public static <K, V> MutableOffsetMap<K, V> of() {
+        return ordered();
+    }
+
+    public static <K, V> MutableOffsetMap<K, V> ordered() {
         return new MutableOffsetMap.Ordered<>();
     }
 
+    public static <K, V> MutableOffsetMap<K, V> unordered() {
+        return new MutableOffsetMap.Unordered<>();
+    }
+
     abstract Object removedObject();
     abstract UnmodifiableMapPhase<K, V> modifiedMap(List<K> keys, V[] objects);
     abstract UnmodifiableMapPhase<K, V> unmodifiedMap(Map<K, Integer> offsets, V[] objects);
+    abstract SharedSingletonMap<K, V> singletonMap();
 
     @Override
     public final int size() {
@@ -280,7 +353,7 @@ public abstract class MutableOffsetMap<K, V> extends AbstractMap<K, V> implement
             return ImmutableMap.of();
         }
         if (s == 1) {
-            return SharedSingletonMap.copyOf(this);
+            return singletonMap();
         }
 
         // Construct the set of keys
index b9776e3421bab45dffdcaa6f0066d73e5a9be261..b7ba220379146dd6be14ca1d6eb30762fd9960b1 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.util;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Verify;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
@@ -15,7 +16,10 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSet;
+import java.lang.reflect.Array;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 final class OffsetMapCache {
@@ -63,4 +67,35 @@ final class OffsetMapCache {
 
         return offsets(ImmutableSet.copyOf(args));
     }
+
+    private static <K, V> V[] adjustArray(final Map<K, Integer> offsets, final List<K> keys, final V[] array) {
+        @SuppressWarnings("unchecked")
+        final V[] ret = (V[]) Array.newInstance(array.getClass().getComponentType(), array.length);
+
+        int i = 0;
+        for (final K k : keys) {
+            final Integer o = Verify.verifyNotNull(offsets.get(k), "Key %s not present in offsets %s", k, offsets);
+            ret[o] = array[i++];
+        }
+
+        return ret;
+    }
+
+    static <K, V> V[] adjustedArray(final Map<K, Integer> offsets, final List<K> keys, final V[] array) {
+        Verify.verify(offsets.size() == keys.size(), "Offsets %s do not match keys %s", offsets, keys);
+
+        // This relies on the fact that offsets has an ascending iterator
+        final Iterator<K> oi = offsets.keySet().iterator();
+        final Iterator<K> ki = keys.iterator();
+
+        while (oi.hasNext()) {
+            final K o = oi.next();
+            final K k = ki.next();
+            if (!k.equals(o)) {
+                return adjustArray(offsets, keys, array);
+            }
+        }
+
+        return array;
+    }
 }
index 9e1776adfb2c7b69a23e19e816d5df5dd89e9c15..ef420b4b578cd80f526ba4f5ea22d6fbd5225ee6 100644 (file)
@@ -34,7 +34,20 @@ public abstract class SharedSingletonMap<K, V> implements Serializable, Unmodifi
 
         @Override
         public ModifiableMapPhase<K, V> toModifiableMap() {
-            return MutableOffsetMap.copyOf(this);
+            return MutableOffsetMap.orderedCopyOf(this);
+        }
+    }
+
+    private static final class Unordered<K, V> extends SharedSingletonMap<K, V> {
+        private static final long serialVersionUID = 1L;
+
+        Unordered(final K key, final V value) {
+            super(key, value);
+        }
+
+        @Override
+        public ModifiableMapPhase<K, V> toModifiableMap() {
+            return MutableOffsetMap.unorderedCopyOf(this);
         }
     }
 
@@ -56,17 +69,44 @@ public abstract class SharedSingletonMap<K, V> implements Serializable, Unmodifi
         this.value = Preconditions.checkNotNull(value);
     }
 
+    /**
+     * @deprecated Use {@link #orderedOf(Object, Object)} or {@link #unorderedOf(Object, Object)} instead.
+     */
+    @Deprecated
     public static <K, V> SharedSingletonMap<K, V> of(final K key, final V value) {
         return new Ordered<>(key, value);
     }
 
+    public static <K, V> SharedSingletonMap<K, V> orderedOf(final K key, final V value) {
+        return new Ordered<>(key, value);
+    }
+
+    public static <K, V> SharedSingletonMap<K, V> unorderedOf(final K key, final V value) {
+        return new Unordered<>(key, value);
+    }
+
+    /**
+     * @deprecated Use {@link #orderedCopyOf(Map)} or {@link #unorderedCopyOf(Map)} instead.
+     */
+    @Deprecated
     public static <K, V> SharedSingletonMap<K, V> copyOf(final Map<K, V> m) {
+        return orderedCopyOf(m);
+    }
+
+    public static <K, V> SharedSingletonMap<K, V> orderedCopyOf(final Map<K, V> m) {
         Preconditions.checkArgument(m.size() == 1);
 
         final Entry<K, V> e = m.entrySet().iterator().next();
         return new Ordered<>(e.getKey(), e.getValue());
     }
 
+    public static <K, V> SharedSingletonMap<K, V> unorderedCopyOf(final Map<K, V> m) {
+        Preconditions.checkArgument(m.size() == 1);
+
+        final Entry<K, V> e = m.entrySet().iterator().next();
+        return new Unordered<>(e.getKey(), e.getValue());
+    }
+
     @Override
     public final SingletonSet<Entry<K, V>> entrySet() {
         return SingletonSet.<Entry<K, V>>of(new SimpleImmutableEntry<>(keySet.getElement(), value));
index 578b87bc140828837f040e89d100dea5ee4e1f2d..fad44069e593a6a3270288b304fd0676da852212 100644 (file)
@@ -38,7 +38,11 @@ public class OffsetMapTest {
     private final Map<String, String> threeEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3");
 
     private ImmutableOffsetMap<String, String> createMap() {
-        return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.copyOf(twoEntryMap);
+        return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.orderedCopyOf(twoEntryMap);
+    }
+
+    private ImmutableOffsetMap<String, String> unorderedMap() {
+        return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.unorderedCopyOf(twoEntryMap);
     }
 
     @Before
@@ -54,7 +58,7 @@ public class OffsetMapTest {
     @Test
     public void testCopyEmptyMap() {
         final Map<String, String> source = Collections.emptyMap();
-        final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
+        final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
 
         assertEquals(source, result);
         assertTrue(result instanceof ImmutableMap);
@@ -63,7 +67,7 @@ public class OffsetMapTest {
     @Test
     public void testCopySingletonMap() {
         final Map<String, String> source = Collections.singletonMap("a", "b");
-        final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
+        final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
 
         assertEquals(source, result);
         assertTrue(result instanceof SharedSingletonMap);
@@ -84,10 +88,10 @@ public class OffsetMapTest {
         assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
 
         // Should result in the same object
-        assertSame(map, ImmutableOffsetMap.copyOf(map));
+        assertSame(map, ImmutableOffsetMap.orderedCopyOf(map));
 
         final Map<String, String> mutable = map.toModifiableMap();
-        final Map<String, String> copy = ImmutableOffsetMap.copyOf(mutable);
+        final Map<String, String> copy = ImmutableOffsetMap.orderedCopyOf(mutable);
 
         assertEquals(mutable, copy);
         assertEquals(map, copy);
@@ -334,9 +338,28 @@ public class OffsetMapTest {
         assertFalse(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
     }
 
+    @Test
+    public void testReusedOffsetsUnordered() {
+        final ImmutableOffsetMap<String, String> source = unorderedMap();
+        final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k1", "v1");
+
+        final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
+        assertEquals(source, result);
+
+        // Only offsets should be shared
+        assertSame(source.offsets(), result.offsets());
+        assertNotSame(source.objects(), result.objects());
+
+        // Iterator order needs to be preserved
+        assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
+    }
+
     @Test
     public void testEmptyMutable() throws CloneNotSupportedException {
-        final MutableOffsetMap<String, String> map = MutableOffsetMap.of();
+        final MutableOffsetMap<String, String> map = MutableOffsetMap.ordered();
         assertTrue(map.isEmpty());
 
         final Map<String, String> other = map.clone();
@@ -412,6 +435,24 @@ public class OffsetMapTest {
         assertFalse(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
     }
 
+    @Test
+    public void testExpansionWithoutOrder() {
+        final MutableOffsetMap<String, String> mutable = unorderedMap().toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k3", "v3");
+        mutable.put("k1", "v1");
+
+        assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
+
+        final Map<String, String> result = mutable.toUnmodifiableMap();
+
+        assertTrue(result instanceof ImmutableOffsetMap);
+        assertEquals(threeEntryMap, result);
+        assertEquals(result, threeEntryMap);
+        assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
+    }
+
     @Test
     public void testReplacedValue() {
         final ImmutableOffsetMap<String, String> source = createMap();
@@ -462,6 +503,38 @@ public class OffsetMapTest {
         assertTrue(Iterables.elementsEqual(source.entrySet(), immutable.entrySet()));
     }
 
+    @Test
+    public void testCloneableFlippingUnordered() throws CloneNotSupportedException {
+        final MutableOffsetMap<String, String> source = unorderedMap().toModifiableMap();
+
+        // Must clone before mutation
+        assertTrue(source.needClone());
+
+        // Non-existent entry, should not clone
+        source.remove("non-existent");
+        assertTrue(source.needClone());
+
+        // Changes the array, should clone
+        source.remove("k1");
+        assertFalse(source.needClone());
+
+        // Create a clone of the map, which shares the array
+        final MutableOffsetMap<String, String> result = source.clone();
+        assertFalse(source.needClone());
+        assertTrue(result.needClone());
+        assertSame(source.array(), result.array());
+
+        // Changes the array, should clone
+        source.put("k1", "v2");
+        assertFalse(source.needClone());
+        assertTrue(result.needClone());
+
+        // Creates a immutable view, which shares the array
+        final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source.toUnmodifiableMap();
+        assertTrue(source.needClone());
+        assertSame(source.array(), immutable.objects());
+    }
+
     @Test
     public void testMutableEntrySet() {
         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
index 3e0bb753dfbead2d027e3808f964faa0695d1628..58064e87bc9369a2997040aa8a3f306ff194d4e1 100644 (file)
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import com.google.common.collect.ImmutableMap;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
@@ -18,7 +19,7 @@ import org.junit.Test;
 
 public class SharedSingletonMapTest {
     private static UnmodifiableMapPhase<String, String> create() {
-        return SharedSingletonMap.of("k1", "v1");
+        return SharedSingletonMap.orderedOf("k1", "v1");
     }
 
     @Test
@@ -59,12 +60,39 @@ public class SharedSingletonMapTest {
         assertFalse(m.equals(Collections.singletonMap("k1", null)));
         assertFalse(m.equals(Collections.singletonMap(null, "v1")));
         assertFalse(m.equals(Collections.singletonMap("k1", "v2")));
+        assertFalse(m.equals(ImmutableMap.of("k1", "v1", "k2", "v2")));
 
         final Set<String> set = m.keySet();
         assertTrue(set instanceof SingletonSet);
         assertTrue(set.contains("k1"));
     }
 
+    @Test
+    public void testOrderedCopyOf() {
+        final Map<String, String> t = Collections.singletonMap("k1", "v1");
+        final Map<String, String> m = SharedSingletonMap.orderedCopyOf(t);
+        assertTrue(t.equals(m));
+        assertTrue(m.equals(t));
+    }
+
+    @Test
+    public void testUnorderedCopyOf() {
+        final Map<String, String> t = Collections.singletonMap("k1", "v1");
+        final Map<String, String> m = SharedSingletonMap.unorderedCopyOf(t);
+        assertTrue(t.equals(m));
+        assertTrue(m.equals(t));
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testEmptyOrderedCopyOf() {
+        SharedSingletonMap.orderedCopyOf(ImmutableMap.of());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testEmptyUnorderedCopyOf() {
+        SharedSingletonMap.unorderedCopyOf(ImmutableMap.of());
+    }
+
     @Test(expected=UnsupportedOperationException.class)
     public void testClear() {
         create().clear();
index 717b4cadd770d703adeaf0e778c20645790c545c..9944d741e66cd64a99e61c82393646e8c9dae6a3 100644 (file)
@@ -509,11 +509,12 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
         public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
             super(node);
             // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
-            this.keyValues = ImmutableOffsetMap.copyOf(keyValues);
+            this.keyValues = ImmutableOffsetMap.unorderedCopyOf(keyValues);
         }
 
         public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
-            this(node, SharedSingletonMap.of(key, value));
+            super(node);
+            this.keyValues = SharedSingletonMap.unorderedOf(key, value);
         }
 
         public Map<QName, Object> getKeyValues() {
index a406b154f9f0c8bc335a70c322b675dc9d100ebc..ea6e954da79685bb471d5bde4ae39750d08ffe1f 100644 (file)
@@ -16,14 +16,16 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 
-public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> extends AbstractImmutableNormalizedNode<K, Collection<DataContainerChild<? extends PathArgument, ?>>> implements Immutable, DataContainerNode<K> {
+public abstract class AbstractImmutableDataContainerNode<K extends PathArgument>
+        extends AbstractImmutableNormalizedNode<K, Collection<DataContainerChild<? extends PathArgument, ?>>>
+        implements Immutable, DataContainerNode<K> {
     private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
 
     public AbstractImmutableDataContainerNode(
             final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
         super(nodeIdentifier);
 
-        this.children = ImmutableOffsetMap.copyOf(children);
+        this.children = ImmutableOffsetMap.unorderedCopyOf(children);
     }
 
     @Override