Introduce SharedSingletonMap
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / MutableOffsetMap.java
index a020c89d28be326ac77be0f9ba64c157848ca740..4be9ed99cc41f849d8f8e83515bb094c7d763f50 100644 (file)
@@ -51,7 +51,7 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         this(Collections.<K>emptySet());
     }
 
-    public MutableOffsetMap(final Collection<K> keySet) {
+    protected MutableOffsetMap(final Collection<K> keySet) {
         if (!keySet.isEmpty()) {
             removed = keySet.size();
             offsets = OffsetMapCache.offsetsFor(keySet);
@@ -66,9 +66,11 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
     }
 
     protected MutableOffsetMap(final ImmutableOffsetMap<K, V> m) {
-        this.offsets = m.offsets();
-        this.objects = m.objects();
-        this.newKeys = new LinkedHashMap<>();
+        this(m.offsets(), m.objects());
+    }
+
+    protected MutableOffsetMap(final Map<K, V> m) {
+        this(OffsetMapCache.offsetsFor(m.keySet()), m.values().toArray());
     }
 
     protected MutableOffsetMap(final MutableOffsetMap<K, V> m) {
@@ -78,6 +80,34 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         this.removed = m.removed;
     }
 
+    private MutableOffsetMap(final Map<K, Integer> offsets, final Object[] objects) {
+        this.offsets = Preconditions.checkNotNull(offsets);
+        this.objects = Preconditions.checkNotNull(objects);
+        this.newKeys = new LinkedHashMap<>();
+    }
+
+    public static <K, V> MutableOffsetMap<K, V> copyOf(final Map<K, V> m) {
+        if (m instanceof MutableOffsetMap) {
+            return ((MutableOffsetMap<K, V>) m).clone();
+        }
+        if (m instanceof ImmutableOffsetMap) {
+            return ((ImmutableOffsetMap<K, V>) m).toModifiableMap();
+        }
+
+        return new MutableOffsetMap<>(m);
+    }
+
+    public static <K, V> MutableOffsetMap<K, V> forOffsets(final Map<K, Integer> offsets) {
+        final Object[] objects = new Object[offsets.size()];
+        Arrays.fill(objects, NO_VALUE);
+
+        return new MutableOffsetMap<>(offsets, objects);
+    }
+
+    public static <K, V> MutableOffsetMap<K, V> forKeySet(final Collection<K> keySet) {
+        return forOffsets(OffsetMapCache.offsetsFor(keySet));
+    }
+
     @Override
     public final int size() {
         return offsets.size() + newKeys.size() - removed;
@@ -127,7 +157,7 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
     @Override
     public final V put(final K key, final V value) {
         Preconditions.checkNotNull(value);
-        final Integer offset = offsets.get(key);
+        final Integer offset = offsets.get(Preconditions.checkNotNull(key));
         if (offset == null) {
             final V ret = newKeys.put(key, value);
             if (ret == null) {
@@ -249,10 +279,86 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
     }
 
     @Override
-    public MutableOffsetMap<K, V> clone() throws CloneNotSupportedException {
+    public MutableOffsetMap<K, V> clone() {
         return new MutableOffsetMap<K, V>(this);
     }
 
+    @Override
+    public int hashCode() {
+        int result = 0;
+
+        for (Entry<K, Integer> e : offsets.entrySet()) {
+            final Object v = objects[e.getValue()];
+            if (!NO_VALUE.equals(v)) {
+                result += e.getKey().hashCode() ^ objectToValue(e.getKey(), v).hashCode();
+            }
+        }
+
+        return result + newKeys.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+
+        if (o instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<?, ?> om = (ImmutableOffsetMap<?, ?>) o;
+            if (newKeys.isEmpty() && offsets == om.offsets() && Arrays.deepEquals(objects, om.objects())) {
+                return true;
+            }
+        } else if (o instanceof MutableOffsetMap) {
+            final MutableOffsetMap<?, ?> om = (MutableOffsetMap<?, ?>) o;
+            if (offsets == om.offsets && Arrays.deepEquals(objects, om.objects) && newKeys.equals(om.newKeys)) {
+                return true;
+            }
+        } else if (o instanceof Map) {
+            final Map<?, ?> om = (Map<?, ?>)o;
+
+            // Size and key sets have to match
+            if (size() != om.size() || !keySet().equals(om.keySet())) {
+                return false;
+            }
+
+            try {
+                // Ensure all newKeys are present. Note newKeys is guaranteed to
+                // not contain null value.
+                for (Entry<K, V> e : newKeys.entrySet()) {
+                    if (!e.getValue().equals(om.get(e.getKey()))) {
+                        return false;
+                    }
+                }
+
+                // Ensure all objects are present
+                for (Entry<K, Integer> e : offsets.entrySet()) {
+                    final Object obj = objects[e.getValue()];
+                    if (!NO_VALUE.equals(obj)) {
+                        final V v = objectToValue(e.getKey(), obj);
+                        if (!v.equals(om.get(e.getKey()))) {
+                            return false;
+                        }
+                    }
+                }
+            } catch (ClassCastException e) {
+                // Can be thrown by om.get() and indicate we have incompatible key types
+                return false;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public final Set<K> keySet() {
+        return new KeySet();
+    }
+
     @VisibleForTesting
     boolean needClone() {
         return needClone;
@@ -271,7 +377,13 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
     private final class EntrySet extends AbstractSet<Entry<K, V>> {
         @Override
         public Iterator<Entry<K, V>> iterator() {
-            return new EntrySetIterator();
+            return new AbstractSetIterator<Entry<K, V>>() {
+                @Override
+                public Entry<K, V> next() {
+                    final K key = nextKey();
+                    return new SimpleEntry<>(key, get(key));
+                }
+            };
         }
 
         @Override
@@ -327,17 +439,34 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         }
     }
 
-    private final class EntrySetIterator implements Iterator<Entry<K, V>> {
+    private final class KeySet extends AbstractSet<K> {
+        @Override
+        public Iterator<K> iterator() {
+            return new AbstractSetIterator<K>() {
+                @Override
+                public K next() {
+                    return nextKey();
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return MutableOffsetMap.this.size();
+        }
+    }
+
+    private abstract class AbstractSetIterator<E> implements Iterator<E> {
         private final Iterator<Entry<K, Integer>> oldIterator = offsets.entrySet().iterator();
-        private final Iterator<Entry<K, V>> newIterator = newKeys.entrySet().iterator();
+        private final Iterator<K> newIterator = newKeys.keySet().iterator();
         private int expectedModCount = modCount;
         private K currentKey, nextKey;
 
-        EntrySetIterator() {
-            calculateNextKey();
+        AbstractSetIterator() {
+            updateNextKey();
         }
 
-        private void calculateNextKey() {
+        private void updateNextKey() {
             while (oldIterator.hasNext()) {
                 final Entry<K, Integer> e = oldIterator.next();
                 if (!NO_VALUE.equals(objects[e.getValue()])) {
@@ -346,7 +475,7 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
                 }
             }
 
-            nextKey = newIterator.hasNext() ? newIterator.next().getKey() : null;
+            nextKey = newIterator.hasNext() ? newIterator.next() : null;
         }
 
         private void checkModCount() {
@@ -356,26 +485,13 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         }
 
         @Override
-        public boolean hasNext() {
+        public final boolean hasNext() {
             checkModCount();
             return nextKey != null;
         }
 
         @Override
-        public Entry<K, V> next() {
-            if (nextKey == null) {
-                throw new NoSuchElementException();
-            }
-
-            checkModCount();
-            currentKey = nextKey;
-            calculateNextKey();
-
-            return new SimpleEntry<>(currentKey, get(currentKey));
-        }
-
-        @Override
-        public void remove() {
+        public final void remove() {
             Preconditions.checkState(currentKey != null);
 
             checkModCount();
@@ -391,5 +507,17 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
             expectedModCount = ++modCount;
             currentKey = null;
         }
+
+        protected final K nextKey() {
+            if (nextKey == null) {
+                throw new NoSuchElementException();
+            }
+
+            checkModCount();
+            currentKey = nextKey;
+            updateNextKey();
+
+            return currentKey;
+        }
     }
 }