sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1640278 - in /sis/branches/JDK8/core: sis-feature/src/main/java/org/apache/sis/feature/ sis-feature/src/test/java/org/apache/sis/feature/ sis-referencing/src/main/java/org/apache/sis/referencing/ sis-utility/src/main/java/org/apache/sis/in...
Date Tue, 18 Nov 2014 07:03:27 GMT
Author: desruisseaux
Date: Tue Nov 18 07:03:27 2014
New Revision: 1640278

URL: http://svn.apache.org/r1640278
Log:
More tests.

Added:
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakEntry.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -17,8 +17,6 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
 import org.opengis.util.GenericName;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
@@ -187,7 +185,7 @@ final class CharacteristicMap extends Ab
      * @param index Index of the expected attribute type.
      * @param type  The actual attribute type.
      */
-    private void verifyAttributeType(final int index, final AttributeType<?> type) {
+    final void verifyAttributeType(final int index, final AttributeType<?> type) {
         final AttributeType<?> expected = types.characterizedBy[index];
         if (!expected.equals(type)) {
             final GenericName en = expected.getName();
@@ -270,51 +268,45 @@ final class CharacteristicMap extends Ab
      * Returns an iterator over the entries.
      */
     @Override
-    protected Iterator<Map.Entry<String, Attribute<?>>> entryIterator() {
+    protected EntryIterator<String, Attribute<?>> entryIterator() {
         if (characterizedBy == null) {
             return null;
         }
-        return new Iterator<Map.Entry<String, Attribute<?>>>() {
-            /** Index of the next element to return in the iteration. */
-            private int index;
+        return new EntryIterator<String, Attribute<?>>() {
+            /** Index of the current element to return in the iteration. */
+            private int index = -1;
 
-            /** The next element to return, or {@code null} if we reached the end of iteration. */
-            private Attribute<?> next;
-
-            /** Index of the element returned by the last call to {@link #next()}, or -1 if none. */
-            private int previous = -1;
+            /** The element to return, or {@code null} if we reached the end of iteration. */
+            private Attribute<?> value;
 
             /** Returns {@code true} if there is more entries in the iteration. */
-            @Override
-            public boolean hasNext() {
-                while (index < characterizedBy.length) {
-                    next = characterizedBy[index];
-                    if (next != null) return true;
-                    index++;
+            @Override protected boolean next() {
+                while (++index < characterizedBy.length) {
+                    value = characterizedBy[index];
+                    if (value != null) return true;
                 }
-                next = null;
+                value = null;
                 return false;
             }
 
+            /** Returns the name of the attribute characteristic. */
+            @Override protected String getKey() {
+                return value.getType().getName().toString();
+            }
+
+            /** Returns the attribute characteristic (never {@code null}). */
+            @Override protected Attribute<?> getValue() {
+                return value;
+            }
+
             /** Creates and return the next entry. */
-            @Override
-            public Map.Entry<String, Attribute<?>> next() {
-                if (hasNext()) {
-                    return new Entry(previous = index++, next);
-                } else {
-                    throw new NoSuchElementException();
-                }
+            @Override protected Map.Entry<String, Attribute<?>> getEntry() {
+                return new Entry(index, value);
             }
 
             /** Removes the last element returned by {@link #next()}. */
-            @Override
-            public void remove() {
-                if (previous >= 0) {
-                    characterizedBy[previous] = null;
-                    previous = -1;
-                } else {
-                    throw new IllegalStateException();
-                }
+            @Override protected void remove() {
+                characterizedBy[index] = null;
             }
         };
     }
@@ -338,20 +330,17 @@ final class CharacteristicMap extends Ab
         }
 
         /** Returns the name of the attribute characteristic. */
-        @Override
-        public String getKey() {
+        @Override public String getKey() {
             return value.getType().getName().toString();
         }
 
         /** Returns the attribute characteristic (never {@code null}). */
-        @Override
-        public Attribute<?> getValue() {
+        @Override public Attribute<?> getValue() {
             return value;
         }
 
         /** Sets the attribute characteristic. */
-        @Override
-        public Attribute<?> setValue(final Attribute<?> value) {
+        @Override public Attribute<?> setValue(final Attribute<?> value) {
             ArgumentChecks.ensureNonNull("value", value);
             verifyAttributeType(index, value.getType());
             final Attribute<?> previous = this.value;

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -18,10 +18,7 @@ package org.apache.sis.feature;
 
 import java.util.Map;
 import java.util.HashMap;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
 import org.apache.sis.internal.util.AbstractMap;
-import org.apache.sis.internal.util.AbstractMapEntry;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.collection.WeakValueHashMap;
@@ -169,51 +166,41 @@ final class CharacteristicTypeMap extend
      * This is not the iterator returned by public API like {@code Map.entrySet().iterator()}.
      */
     @Override
-    protected Iterator<Entry<String, AttributeType<?>>> entryIterator() {
-        return new Iter();
-    }
-
-    /**
-     * Iterator over the {@link CharacteristicTypeMap} entries. The entries returned by this method are always
-     * {@code this} (in order to avoid temporary objects creation), which is sufficient for {@link AbstractMap}
-     * needs. This is not the iterator returned by public API like {@code Map.entrySet().iterator()}.
-     */
-    private final class Iter extends AbstractMapEntry<String, AttributeType<?>>
-            implements Iterator<Entry<String, AttributeType<?>>>
-    {
-        /** Index of the next element to return in the iteration. */
-        private int index;
-
-        /** Value of current entry. */
-        private AttributeType<?> value;
-
-        /** Creates a new iterator. */
-        Iter() {
-        }
-
-        /** Returns {@code true} if there is more entries in the iteration. */
-        @Override public boolean hasNext() {
-            return index < characterizedBy.length;
-        }
-
-        /** Creates and return the next entry. */
-        @Override public Entry<String, AttributeType<?>> next() {
-            if (hasNext()) {
-                value = characterizedBy[index++];
-                return this;
-            } else {
-                throw new NoSuchElementException();
+    protected EntryIterator<String, AttributeType<?>> entryIterator() {
+        return new EntryIterator<String, AttributeType<?>>() {
+            /** Index of the next element to return in the iteration. */
+            private int index;
+
+            /** Value of current entry. */
+            private AttributeType<?> value;
+
+            /**
+             * Returns {@code true} if there is more entries in the iteration.
+             */
+            @Override
+            protected boolean next() {
+                if (index < characterizedBy.length) {
+                    value = characterizedBy[index++];
+                    return true;
+                }
+                return false;
             }
-        }
 
-        /** Returns the attribute characteristic name. */
-        @Override public String getKey() {
-            return value.getName().toString();
-        }
+            /**
+             * Returns the attribute characteristic name.
+             */
+            @Override
+            protected String getKey() {
+                return value.getName().toString();
+            }
 
-        /** Returns the attribute characteristic contained in this entry. */
-        @Override public AttributeType<?> getValue() {
-            return value;
-        }
+            /**
+             * Returns the attribute characteristic contained in this entry.
+             */
+            @Override
+            protected AttributeType<?> getValue() {
+                return value;
+            }
+        };
     }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -284,15 +284,49 @@ public final strictfp class Characterist
     }
 
     /**
+     * Sets the accuracy characteristic in the given attribute.
+     *
+     * @param temperature The attribute where to set the accuracy.
+     * @param isFirstTime {@code true} if the accuracy value is set for the first time.
+     * @param value       The new accuracy value.
+     */
+    private static void setAccuracy(final AbstractAttribute<Float> temperature, final boolean isFirstTime, final float value) {
+        assertEquals("keySet.add", isFirstTime, temperature.characteristics().keySet().add("accuracy"));
+        final Attribute<Float> accuracy = Features.cast(temperature.characteristics().get("accuracy"), Float.class);
+        accuracy.setValue(value);
+    }
+
+    /**
+     * Tests {@link CharacteristicMap#equals(Object)} and (opportunistically) {@link CharacteristicMap#clone()}
+     */
+    @Test
+    @DependsOnMethod("testAddKey")
+    public void testEquals() {
+        final AbstractAttribute<Float> t1 = temperature();
+        final AbstractAttribute<Float> t2 = temperature();
+        assertEquals("equals",   t1, t2);
+        assertEquals("hashCode", t1.hashCode(), t2.hashCode());
+        setAccuracy(t1, true, 0.2f);
+        assertFalse("equals",   t1.equals(t2));
+        assertFalse("equals",   t2.equals(t1));
+        assertFalse("hashCode", t1.hashCode() == t2.hashCode());
+        setAccuracy(t2, true, 0.3f);
+        assertFalse("equals",   t1.equals(t2));
+        assertFalse("equals",   t2.equals(t1));
+        assertFalse("hashCode", t1.hashCode() == t2.hashCode());
+        setAccuracy(t1, false, 0.3f);
+        assertEquals("equals",   t1, t2);
+        assertEquals("hashCode", t1.hashCode(), t2.hashCode());
+    }
+
+    /**
      * Tests the reconstruction of {@link CharacteristicTypeMap} after serialization.
      */
     @Test
-    @DependsOnMethod("testAddValue") // Implementation of readObject use values().addAll(...).
+    @DependsOnMethod({"testEquals", "testAddValue"}) // Implementation of readObject use values().addAll(...).
     public void testSerialization() {
         final AbstractAttribute<Float> temperature = temperature();
-        assertTrue(temperature.characteristics().keySet().add("accuracy"));
-        final Attribute<Float> accuracy = Features.cast(temperature.characteristics().get("accuracy"), Float.class);
-        accuracy.setValue(0.2f);
+        setAccuracy(temperature, true, 0.2f);
 
         final AbstractAttribute<Float> unserialized = assertSerializedEquals(temperature);
         assertNotSame(temperature, unserialized);

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/Properties.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -17,13 +17,8 @@
 package org.apache.sis.referencing;
 
 import java.util.Map;
-import java.util.Set;
 import java.util.HashMap;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
 import java.util.Collection;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
 import java.io.Serializable;
 import org.opengis.util.GenericName;
 import org.opengis.metadata.Identifier;
@@ -32,6 +27,7 @@ import org.opengis.referencing.Reference
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.metadata.quality.PositionalAccuracy;
+import org.apache.sis.internal.util.AbstractMap;
 
 
 /**
@@ -220,14 +216,6 @@ final class Properties extends AbstractM
     }
 
     /**
-     * Returns true if this map contains a mapping for the specified key.
-     */
-    @Override
-    public boolean containsKey(final Object key) {
-        return get(key) != null;
-    }
-
-    /**
      * Returns the value to which this map maps the specified key.
      * Returns {@code null} if the map contains no mapping for this key.
      */
@@ -238,90 +226,50 @@ final class Properties extends AbstractM
     }
 
     /**
-     * Returns a set view of the mappings contained in this map.
+     * Iterates over the {@link #KEYS}, returning only the entry having a non-null value.
      */
     @Override
-    public Set<Entry<String,Object>> entrySet() {
-        return new EntrySet();
-    }
-
-    /**
-     * The view returned by {@link #entrySet()}.
-     */
-    private final class EntrySet extends AbstractSet<Entry<String,Object>> {
-        /** Creates a new instance. */
-        EntrySet() {
-        }
-
-        /** Delegates to the enclosing map. */
-        @Override
-        public boolean isEmpty() {
-            return Properties.this.isEmpty();
-        }
-
-        /** Delegates to the enclosing map. */
-        @Override
-        public int size() {
-            return Properties.this.size();
-        }
-
-        /** Iterates over the {@link #KEYS}, returning only the entry having a non-null value. */
-        @Override
-        public Iterator<Entry<String, Object>> iterator() {
-            return new Iter();
-        }
-    }
-
-    /**
-     * The iterator returned by {@link EntrySet#iterator()}.
-     */
-    private final class Iter implements Iterator<Entry<String,Object>> {
-        /**
-         * Index of the next element to return.
-         */
-        private int nextIndex;
-
-        /**
-         * Index of the value to be returned by {@link #next()}, or {@code null} if not yet computed.
-         */
-        private Object value;
-
-        /**
-         * Creates a new iterator.
-         */
-        Iter() {
-        }
-
-        /**
-         * Returns {@code true} if there is a value to return.
-         */
-        @Override
-        public boolean hasNext() {
-            while (value == null) {
-                if (nextIndex == KEYS.length) {
-                    return false;
+    protected EntryIterator<String,Object> entryIterator() {
+        return new EntryIterator<String,Object>() {
+            /**
+             * Index of the next element to inspect.
+             */
+            private int nextIndex;
+
+            /**
+             * Index of the value to be returned by {@link #next()}, or {@code null} if not yet computed.
+             */
+            private Object value;
+
+            /**
+             * Returns {@code true} if there is a value to return.
+             */
+            @Override
+            protected boolean next() {
+                while (nextIndex < KEYS.length) {
+                    value = getAt(nextIndex++);
+                    if (value != null) {
+                        return true;
+                    }
                 }
-                value = getAt(nextIndex++);
+                return false;
             }
-            return true;
-        }
 
-        /**
-         * Returns the next element.
-         */
-        @Override
-        public Entry<String, Object> next() {
-            if (hasNext()) {
-                final Entry<String, Object> entry = new SimpleImmutableEntry<>(KEYS[nextIndex-1], value);
-                value = null; // For forcing the next call to 'hasNext()' to increment 'nextIndex'.
-                return entry;
+            /**
+             * Returns the key at the current position.
+             */
+            @Override
+            protected String getKey() {
+                return KEYS[nextIndex - 1];
             }
-            throw new NoSuchElementException();
-        }
 
-        /*
-         * remove() is an unsupported operation since this map is read-only.
-         * So we inherit the default implementation from Iterator.
-         */
+            /**
+             * Returns the value at the current position.
+             */
+            @Override
+            protected Object getValue() {
+                return value;
+            }
+        };
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/AbstractMap.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -20,12 +20,16 @@ import java.util.Map;
 import java.util.Set;
 import java.util.Iterator;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.AbstractSet;
 import java.util.AbstractCollection;
-import java.util.Collections;
+import java.util.NoSuchElementException;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.util.resources.Errors;
 
+// Branch-dependent imports
+import java.util.Objects;
+
 
 /**
  * An alternative to {@link java.util.AbstractMap java.util.AbstractMap} using different implementation strategies.
@@ -70,6 +74,65 @@ import org.apache.sis.util.resources.Err
  */
 public abstract class AbstractMap<K,V> implements Map<K,V> {
     /**
+     * An iterator over the entries in the enclosing map. This iterator has two main differences compared
+     * to the standard {@code Map.entrySet().iterator()}:
+     * <ul>
+     *   <li>The {@link #next()} method checks if there is more element and moves to the next one in a single step.
+     *       This is exactly the same approach than {@link java.sql.ResultSet#next()}.</li>
+     *   <li>Entry elements are returned by the {@link #getKey()} and {@link #getValue()} methods
+     *       instead than creating new {@code Map.Element} on each iterator.</li>
+     * </ul>
+     *
+     * @param <K> The type of keys maintained by the map.
+     * @param <V> The type of mapped values.
+     *
+     * @see AbstractMap#entryIterator()
+     */
+    protected static abstract class EntryIterator<K,V> {
+        /**
+         * Moves the iterator to the next position, and returns {@code true} if there is at least one remaining element.
+         *
+         * @return {@code false} if this method reached iteration end.
+         */
+        protected abstract boolean next();
+
+        /**
+         * Returns the key at the current iterator position.
+         * This method is invoked only after {@link #next()}.
+         *
+         * @return The key at the current iterator position.
+         */
+        protected abstract K getKey();
+
+        /**
+         * Returns the value at the current iterator position.
+         * This method is invoked only after {@link #next()}.
+         *
+         * @return The value at the current iterator position.
+         */
+        protected abstract V getValue();
+
+        /**
+         * Returns the entry at the current iterator position.
+         * This method is invoked only after {@link #next()}.
+         * The default implementation creates an immutable entry with {@link #getKey()} and {@link #getValue()}.
+         *
+         * @return The entry at the current iterator position.
+         */
+        protected Entry<K,V> getEntry() {
+            return new java.util.AbstractMap.SimpleImmutableEntry<>(getKey(), getValue());
+        }
+
+        /**
+         * Removes the entry at the current iterator position (optional operation).
+         * The default implementation throws {@code UnsupportedOperationException}.
+         */
+        protected void remove() throws UnsupportedOperationException {
+            throw new UnsupportedOperationException(message(false));
+        }
+    }
+
+    /**
      * For subclass constructors.
      */
     protected AbstractMap() {
@@ -106,9 +169,9 @@ public abstract class AbstractMap<K,V> i
      */
     @Override
     public boolean containsValue(final Object value) {
-        final Iterator<Entry<K,V>> it = entryIterator();
-        if (it != null) while (it.hasNext()) {
-            if (it.next().getValue().equals(value)) {
+        final EntryIterator<K,V> it = entryIterator();
+        if (it != null) while (it.next()) {
+            if (it.getValue().equals(value)) {
                 return true;
             }
         }
@@ -116,11 +179,25 @@ public abstract class AbstractMap<K,V> i
     }
 
     /**
+     * Returns the value for the given key, or {@code defaultValue} if none.
+     * The default implementation assumes that the map can not contain {@code null} values.
+     *
+     * @param  key The key for which to get the value.
+     * @param  defaultValue The value to return if this map does not have an entry for the given key.
+     * @return The value for the given key, or {@code defaultValue} if none.
+     */
+    @Override
+    public V getOrDefault(final Object key, final V defaultValue) {
+        final V value = get(key);
+        return (value != null) ? value : defaultValue;
+    }
+
+    /**
      * The message to gives to the exception to be thrown in case of unsupported operation.
      *
      * @param add {@code true} if this method is invoked from {@link #addKey(Object)} or {@link #addValue(Object)}.
      */
-    private static String message(final boolean add) {
+    static String message(final boolean add) {
         return Errors.format(add ? Errors.Keys.UnsupportedOperation_1 : Errors.Keys.UnmodifiableObject_1,
                              add ? "add" : Map.class);
     }
@@ -130,25 +207,11 @@ public abstract class AbstractMap<K,V> i
      * The default operation throws {@link UnsupportedOperationException}.
      */
     @Override
-    public void clear() {
+    public void clear() throws UnsupportedOperationException {
         throw new UnsupportedOperationException(message(false));
     }
 
     /**
-     * Returns the value for the given key, or {@code defaultValue} if none.
-     * The default implementation assumes that the map can not contain {@code null} values.
-     *
-     * @param  key The key for which to get the value.
-     * @param  defaultValue The value to return if this map does not have an entry for the given key.
-     * @return The value for the given key, or {@code defaultValue} if none.
-     */
-    @Override
-    public V getOrDefault(final Object key, final V defaultValue) {
-        final V value = get(key);
-        return (value != null) ? value : defaultValue;
-    }
-
-    /**
      * Removes the entry for the given key in this map.
      * The default operation throws {@link UnsupportedOperationException}.
      *
@@ -156,7 +219,7 @@ public abstract class AbstractMap<K,V> i
      * @return The previous value, or {@code null} if none.
      */
     @Override
-    public V remove(Object key) {
+    public V remove(Object key) throws UnsupportedOperationException {
         throw new UnsupportedOperationException(message(false));
     }
 
@@ -169,7 +232,7 @@ public abstract class AbstractMap<K,V> i
      * @return The previous value, or {@code null} if none.
      */
     @Override
-    public V put(K key, V value) {
+    public V put(K key, V value) throws UnsupportedOperationException {
         throw new UnsupportedOperationException(message(false));
     }
 
@@ -179,7 +242,7 @@ public abstract class AbstractMap<K,V> i
      * @param map The other map from which to copy the entries.
      */
     @Override
-    public void putAll(final Map<? extends K, ? extends V> map) {
+    public void putAll(final Map<? extends K, ? extends V> map) throws UnsupportedOperationException {
         for (final Entry<? extends K, ? extends V> entry : map.entrySet()) {
             put(entry.getKey(), entry.getValue());
         }
@@ -192,7 +255,7 @@ public abstract class AbstractMap<K,V> i
      * @param  key The key to add.
      * @return {@code true} if this map changed as a result of this operation.
      */
-    protected boolean addKey(final K key) {
+    protected boolean addKey(final K key) throws UnsupportedOperationException {
         throw new UnsupportedOperationException(message(true));
     }
 
@@ -203,7 +266,7 @@ public abstract class AbstractMap<K,V> i
      * @param  value The value to add.
      * @return {@code true} if this map changed as a result of this operation.
      */
-    protected boolean addValue(final V value) {
+    protected boolean addValue(final V value) throws UnsupportedOperationException {
         throw new UnsupportedOperationException(message(true));
     }
 
@@ -227,7 +290,7 @@ public abstract class AbstractMap<K,V> i
             @Override public boolean     remove(Object e)   {return AbstractMap.this.remove(e) != null;}
             @Override public boolean     add(K e)           {return AbstractMap.this.addKey(e);}
             @Override public Iterator<K> iterator() {
-                final Iterator<Entry<K,V>> it = entryIterator();
+                final EntryIterator<K,V> it = entryIterator();
                 return (it != null) ? new Keys<>(it) : Collections.emptyIterator();
             }
         };
@@ -251,7 +314,7 @@ public abstract class AbstractMap<K,V> i
             @Override public boolean     contains(Object e) {return AbstractMap.this.containsValue(e);}
             @Override public boolean     add(V e)           {return AbstractMap.this.addValue(e);}
             @Override public Iterator<V> iterator() {
-                final Iterator<Entry<K,V>> it = entryIterator();
+                final EntryIterator<K,V> it = entryIterator();
                 return (it != null) ? new Values<>(it) : Collections.emptyIterator();
             }
         };
@@ -286,66 +349,106 @@ public abstract class AbstractMap<K,V> i
 
             /** Returns an iterator compliant to the Map contract. */
             @Override public Iterator<Entry<K,V>> iterator() {
-                final Iterator<Entry<K,V>> it = entryIterator();
-                if (it == null) {
-                    return Collections.emptyIterator();
-                } else if (it instanceof Entry<?,?>) {
-                    return new Entries<>(it);
-                } else {
-                    return it;
-                }
+                final EntryIterator<K,V> it = entryIterator();
+                return (it != null) ? new Entries<>(it) : Collections.emptyIterator();
             }
         };
     }
 
     /**
      * Returns an iterator over the entries in this map.
-     * The returned iterator is not necessarily compliant to the {@code Map} contract:
-     * <ul>
-     *   <li>It is okay (but not required) to return {@code null} if the map is empty.</li>
-     *   <li>The {@code next()} method can return the same {@code Map.Entry} instance on every call, in order to
-     *       reduce the amount of objects created during iteration. However if the iterator implements such recycling,
-     *       then it shall implement the {@code Entry} interface in order to notify {@code AbstractMap} about this fact.
-     *       We use {@code Entry} as a marker interface because the {@code next()} method of an iterator doing such
-     *       recycling will typically returns {@code this}.</li>
-     * </ul>
+     * It is okay (but not required) to return {@code null} if the map is empty.
      *
      * @return An iterator over the entries in this map, or {@code null}.
      */
-    protected abstract Iterator<Entry<K,V>> entryIterator();
+    protected abstract EntryIterator<K,V> entryIterator();
+
+    /**
+     * Base class of iterators overs keys, values or entries.
+     * Those iterators wrap an {@link EntryIterator} instance.
+     */
+    private static abstract class Iter<K,V> {
+        /** The wrapped entry iterator. */
+        private final EntryIterator<K,V> iterator;
+
+        /** {@link #TRUE}, {@link #FALSE} or {@link #AFTER_NEXT}, or 0 if not yet determined. */
+        private byte hasNext;
+
+        /** Possible values for {@link #hasNext}. */
+        private static final byte TRUE=1, FALSE=2, AFTER_NEXT=3;
+
+        /**
+         * Creates a new standard iterator wrapping the given entry iterator.
+         *
+         * @param iterator {@link AbstractMap#entryIterator()}.
+         */
+        Iter(final EntryIterator<K,V> iterator) {
+            this.iterator = iterator;
+        }
+
+        /**
+         * Returns {@code true} if there is at least one more element to return.
+         */
+        public final boolean hasNext() {
+            switch (hasNext) {
+                case TRUE:  return true;
+                case FALSE: return false;
+                default: {
+                    final boolean c = iterator.next();
+                    hasNext = c ? TRUE : FALSE;
+                    return c;
+                }
+            }
+        }
+
+        /**
+         * Ensures that the entry iterator is positioned on a valid entry, and returns it.
+         * This method shall be invoked by implementations of {@link Iterator#next()}.
+         */
+        final EntryIterator<K,V> entry() {
+            if (hasNext()) {
+                hasNext = AFTER_NEXT;
+                return iterator;
+            }
+            throw new NoSuchElementException();
+        }
+
+        /**
+         * Removes the current entry.
+         */
+        public final void remove() {
+            if (hasNext == AFTER_NEXT) {
+                hasNext = 0;
+                iterator.remove();
+            } else {
+                throw new IllegalStateException();
+            }
+        }
+    }
 
     /**
      * Iterator over the keys.
      */
-    private static final class Keys<K,V> implements Iterator<K> {
-        private final Iterator<Entry<K,V>> it;
-        Keys(Iterator<Entry<K,V>> it)      {this.it = it;}
-        @Override public boolean hasNext() {return it.hasNext();}
-        @Override public K       next()    {return it.next().getKey();}
-        @Override public void    remove()  {it.remove();}
+    private static final class Keys<K,V> extends Iter<K,V> implements Iterator<K> {
+        Keys(final EntryIterator<K,V> it) {super(it);}
+        @Override public K next() {return entry().getKey();}
     }
 
     /**
      * Iterator over the values.
      */
-    private static final class Values<K,V> implements Iterator<V> {
-        private final Iterator<Entry<K,V>> it;
-        Values(Iterator<Entry<K,V>> it)    {this.it = it;}
-        @Override public boolean hasNext() {return it.hasNext();}
-        @Override public V       next()    {return it.next().getValue();}
-        @Override public void    remove()  {it.remove();}
+    private static final class Values<K,V> extends Iter<K,V> implements Iterator<V> {
+        Values(EntryIterator<K,V> it) {super(it);}
+        @Override public V next() {return entry().getValue();}
     }
 
     /**
      * Iterator over the entries, used only when {@link #entryIterator()} perform recycling.
      * This iterator copies each entry in an {@code SimpleImmutableEntry} instance.
      */
-    private static final class Entries<K,V> implements Iterator<Entry<K,V>> {
-        private final Iterator<Entry<K,V>> it;
-        Entries(Iterator<Entry<K,V>> it)   {this.it = it;}
-        @Override public boolean hasNext() {return it.hasNext();}
-        @Override public Entry<K,V> next() {return new java.util.AbstractMap.SimpleImmutableEntry<>(it.next());}
-        @Override public void    remove()  {it.remove();}
+    private static final class Entries<K,V> extends Iter<K,V> implements Iterator<Entry<K,V>> {
+        Entries(EntryIterator<K,V> it) {super(it);}
+        @Override public Entry<K,V> next() {return entry().getEntry();}
     }
 
     /**
@@ -361,7 +464,7 @@ public abstract class AbstractMap<K,V> i
         }
         if (object instanceof Map) {
             final Map<?,?> map = (Map<?,?>) object;
-            final Iterator<Entry<K,V>> it = entryIterator();
+            final EntryIterator<K,V> it = entryIterator();
             if (it == null) {
                 return map.isEmpty();
             }
@@ -371,9 +474,8 @@ public abstract class AbstractMap<K,V> i
              * are not equal, we will find a mismatched entry soon anyway.
              */
             int size = 0;
-            while (it.hasNext()) {
-                final Entry<K,V> entry = it.next();
-                if (!entry.getValue().equals(map.get(entry.getKey()))) {
+            while (it.next()) {
+                if (!it.getValue().equals(map.get(it.getKey()))) {
                     return false;
                 }
                 size++;
@@ -391,9 +493,9 @@ public abstract class AbstractMap<K,V> i
     @Override
     public int hashCode() {
         int code = 0;
-        final Iterator<Entry<K,V>> it = entryIterator();
-        if (it != null) while (it.hasNext()) {
-            code += it.next().hashCode();
+        final EntryIterator<K,V> it = entryIterator();
+        if (it != null) while (it.next()) {
+            code += (Objects.hashCode(it.getKey()) ^ Objects.hashCode(it.getValue()));
         }
         return code;
     }
@@ -409,12 +511,11 @@ public abstract class AbstractMap<K,V> i
     public String toString() {
         final TableAppender buffer = new TableAppender(" = ");
         buffer.setMultiLinesCells(true);
-        final Iterator<Entry<K,V>> it = entryIterator();
-        if (it != null) while (it.hasNext()) {
-            final Entry<K,V> entry = it.next();
-            buffer.append(String.valueOf(entry.getKey()));
+        final EntryIterator<K,V> it = entryIterator();
+        if (it != null) while (it.next()) {
+            buffer.append(String.valueOf(it.getKey()));
             buffer.nextColumn();
-            buffer.append(AbstractMapEntry.firstLine(entry.getValue()));
+            buffer.append(AbstractMapEntry.firstLine(it.getValue()));
             buffer.nextLine();
         }
         return buffer.toString();

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakEntry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakEntry.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakEntry.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakEntry.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -95,9 +95,10 @@ abstract class WeakEntry<E> extends Weak
      */
     static <E> int count(final WeakEntry<E>[] table) {
         int n = 0;
-        for (int i=0; i<table.length; i++) {
-            for (WeakEntry<E> e=table[i]; e!=null; e=e.next) {
+        for (WeakEntry<E> e : table) {
+            while (e != null) {
                 n++;
+                e = e.next;
             }
         }
         return n;
@@ -159,8 +160,8 @@ abstract class WeakEntry<E> extends Weak
         final Class<?> entryType = oldTable.getClass().getComponentType();
         @SuppressWarnings("unchecked")
         final WeakEntry<E>[] table = (WeakEntry<E>[]) Array.newInstance(entryType, capacity);
-        for (int i=0; i<oldTable.length; i++) {
-            for (WeakEntry<E> next=oldTable[i]; next!=null;) {
+        for (WeakEntry<E> next : oldTable) {
+            while (next != null) {
                 final WeakEntry<E> e = next;
                 next = next.next; // We keep 'next' right now because its value will change.
                 final int index = e.hash % table.length;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -293,22 +293,17 @@ public class WeakValueHashMap<K,V> exten
 
     /**
      * Checks if this {@code WeakValueHashMap} is valid. This method counts the number of elements
-     * and compares it to {@link #count}. If the check fails, the number of elements is corrected
-     * (if we didn't, an {@link AssertionError} would be thrown for every operations after the first
-     * error, which make debugging more difficult). The set is otherwise unchanged, which should
-     * help to get similar behavior as if assertions hasn't been turned on.
+     * and compares it to {@link #count}. This method is invoked in assertions only.
      */
     @Debug
     final boolean isValid() {
-        assert Thread.holdsLock(this);
-        assert count <= upperCapacityThreshold(table.length);
-        final int n = count(table);
-        if (n != count) {
-            count = n;
-            return false;
-        } else {
-            return true;
+        if (!Thread.holdsLock(this)) {
+            throw new AssertionError();
         }
+        if (count > upperCapacityThreshold(table.length)) {
+            throw new AssertionError(count);
+        }
+        return count(table) == count;
     }
 
     /**
@@ -520,12 +515,13 @@ public class WeakValueHashMap<K,V> exten
                 final Map.Entry<K,V>[] elements = new Map.Entry[size()];
                 int index = 0;
                 final Entry[] table = WeakValueHashMap.this.table;
-                for (int i=0; i<table.length; i++) {
-                    for (Entry el=table[i]; el!=null; el=(Entry) el.next) {
+                for (Entry el : table) {
+                    while (el != null) {
                         final Map.Entry<K,V> entry = new SimpleEntry<>(el);
                         if (entry.getValue() != null) {
                             elements[index++] = entry;
                         }
+                        el= (Entry) el.next;
                     }
                 }
                 return ArraysExt.resize(elements, index);

Added: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java?rev=1640278&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -0,0 +1,190 @@
+/*
+ * 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.sis.internal.util;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.NoSuchElementException;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link AbstractMap} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+public final strictfp class AbstractMapTest extends TestCase {
+    /**
+     * A dummy implementation of {@link AbstractMap} which will contain the English words
+     * for numbers 1 to 4 inclusive. This implementation does not check argument validity
+     * or consistency.
+     */
+    private static final class Count extends AbstractMap<Integer,String> {
+        private final List<String> values = new ArrayList<>(Arrays.asList("one", "two", "three"));
+
+        @Override public    void    clear   ()                    {       values.clear();}
+        @Override public    int     size    ()                    {return values.size();}
+        @Override public    String  get     (Object  k)           {return values.get(((Integer) k) - 1);}
+        @Override public    String  put     (Integer k, String v) {return values.set(k - 1, v);}
+        @Override protected boolean addKey  (Integer k)           {return values.add(k == 4 ? "four" : "other");}
+        @Override protected boolean addValue(String  v)           {return values.add(v);}
+        @Override protected EntryIterator<Integer,String> entryIterator() {
+            return new EntryIterator<Integer,String>() {
+                private int key;
+                @Override protected boolean next()     {return ++key <= size();}
+                @Override protected Integer getKey()   {return key;}
+                @Override protected String  getValue() {return get(key);}
+            };
+        }
+    }
+
+    /**
+     * Tests {@link AbstractMap#keySet()}, {@link AbstractMap#values()} and {@link AbstractMap#entrySet()}.
+     * This method will also opportunistically tests basic methods like {@link AbstractMap#isEmpty()} and
+     * {@link AbstractMap#containsValue(Object)}. This test does not write in the map.
+     */
+    @Test
+    public void testReadOnly() {
+        final Count map = new Count();
+        assertFalse("isEmpty",       map.isEmpty());
+        assertTrue ("containsKey",   map.containsKey(3));
+        assertTrue ("containsValue", map.containsValue("three"));
+        assertFalse("containsValue", map.containsValue("six"));
+
+        final Collection<Integer> keys = map.keySet();
+        assertTrue("contains", keys.contains(3));
+        assertArrayEquals("keySet", new Integer[] {1, 2, 3}, keys.toArray());
+
+        final Collection<String> values = map.values();
+        assertTrue ("contains", values.contains("three"));
+        assertFalse("contains", values.contains("six"));
+        assertArrayEquals("values", new String[] {"one", "two", "three"}, values.toArray());
+
+        final Collection<Map.Entry<Integer,String>> entries = map.entrySet();
+        assertTrue ("contains", entries.contains(new SimpleEntry<>(2, "two")));
+        assertFalse("contains", entries.contains(new SimpleEntry<>(2, "deux")));
+        assertArrayEquals("entrySet", new SimpleEntry<?,?>[] {
+                    new SimpleEntry<>(1, "one"),
+                    new SimpleEntry<>(2, "two"),
+                    new SimpleEntry<>(3, "three")
+                }, entries.toArray());
+
+        map.clear();
+        assertFalse("containsValue", map.containsValue("three"));
+        assertTrue ("isEmpty", map.isEmpty());
+        assertTrue ("isEmpty", keys.isEmpty());
+        assertTrue ("isEmpty", values.isEmpty());
+        assertTrue ("isEmpty", entries.isEmpty());
+    }
+
+    /**
+     * Tests adding an element in {@link AbstractMap#keySet()}.
+     * This is a non-standard feature of our {@link AbstractMap}.
+     */
+    @Test
+    @DependsOnMethod("testReadOnly")
+    public void testAddKey() {
+        final Count map = new Count();
+        assertTrue(map.keySet().add(4));
+        assertArrayEquals("entrySet", new SimpleEntry<?,?>[] {
+                    new SimpleEntry<>(1, "one"),
+                    new SimpleEntry<>(2, "two"),
+                    new SimpleEntry<>(3, "three"),
+                    new SimpleEntry<>(4, "four")
+                }, map.entrySet().toArray());
+    }
+
+    /**
+     * Tests adding an element in {@link AbstractMap#values()}.
+     * This is a non-standard feature of our {@link AbstractMap}.
+     */
+    @Test
+    @DependsOnMethod("testReadOnly")
+    public void testAddValue() {
+        final Count map = new Count();
+        assertTrue(map.values().add("quatre"));
+        assertArrayEquals("entrySet", new SimpleEntry<?,?>[] {
+                    new SimpleEntry<>(1, "one"),
+                    new SimpleEntry<>(2, "two"),
+                    new SimpleEntry<>(3, "three"),
+                    new SimpleEntry<>(4, "quatre")
+                }, map.entrySet().toArray());
+    }
+
+    /**
+     * Tests {@link AbstractMap#equals(Object)} and {@link AbstractMap#hashCode()}.
+     * We use {@link HashMap} as the reference implementation for checking hash code value.
+     */
+    @Test
+    public void testEquals() {
+        final Count map = new Count();
+        final Map<Integer,String> copy = new HashMap<>(map);
+        assertTrue  ("equals",   copy.equals(map));
+        assertTrue  ("equals",   map.equals(copy));
+        assertEquals("hashCode", copy.hashCode(), map.hashCode());
+
+        // Make a change and test again.
+        assertEquals("put", "two", map.put(2, "deux"));
+        assertFalse("equals",   copy.equals(map));
+        assertFalse("equals",   map.equals(copy));
+        assertFalse("hashCode", copy.hashCode() == map.hashCode());
+    }
+
+    /**
+     * Tests exceptions during iteration.
+     */
+    @Test
+    public void testIterationException() {
+        final Iterator<Integer> it = new Count().keySet().iterator();
+        try {
+            it.remove();
+            fail("Should not be allowed to invoke Iterator.remove() before next().");
+        } catch (IllegalStateException e) {
+            // This is the expected exception.
+        }
+        // Iterating without invoking Iterator.hasNext() should work anyway.
+        for (int i=1; i<=3; i++) {
+            assertEquals(Integer.valueOf(i), it.next());
+            try {
+                it.remove();
+                fail();
+            } catch (UnsupportedOperationException e) {
+                // This is the expected exception.
+            }
+        }
+        try {
+            it.next();
+            fail("Expected end of iteration.");
+        } catch (NoSuchElementException e) {
+            // This is the expected exception.
+        }
+        assertFalse("Expected end of iteration.", it.hasNext());
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/AbstractMapTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1640278&r1=1640277&r2=1640278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Tue Nov 18 07:03:27 2014
@@ -75,6 +75,7 @@ import org.junit.BeforeClass;
     org.apache.sis.util.collection.TreeTablesTest.class,
     org.apache.sis.util.collection.CodeListSetTest.class,
     org.apache.sis.internal.util.CollectionsExtTest.class,
+    org.apache.sis.internal.util.AbstractMapTest.class,
 
     // GeoAPI most basic types.
     org.apache.sis.internal.util.DefinitionURITest.class,



Mime
View raw message