sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1404561 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/util/collection/ test/java/org/apache/sis/util/collection/
Date Thu, 01 Nov 2012 12:32:20 GMT
Author: desruisseaux
Date: Thu Nov  1 12:32:19 2012
New Revision: 1404561

URL: http://svn.apache.org/viewvc?rev=1404561&view=rev
Log:
Refactored DerivedMap and DerivedSet in order to use ObjectConverter,
and remove those derived collections from public API. We will add static
factory methods in Collections later.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedIterator.java
      - copied, changed from r1404232, sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java   (with props)
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedMap.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java

Copied: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedIterator.java (from r1404232, sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedIterator.java?p2=sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedIterator.java&p1=sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java&r1=1404232&r2=1404561&rev=1404561&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedIterator.java Thu Nov  1 12:32:19 2012
@@ -16,41 +16,15 @@
  */
 package org.apache.sis.util.collection;
 
-import java.util.Set;
 import java.util.Iterator;
-import java.util.AbstractSet;
-import java.io.Serializable;
 import org.apache.sis.util.Decorator;
-import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ObjectConverter;
 
 
 /**
- * A set whose values are derived <cite>on-the-fly</cite> from an other set.
- * Conversions are performed when needed by two methods:
- *
- * <ul>
- *   <li>The {@linkplain #iterator() iterator} obtain the derived values by calls to the
- *       {@link #baseToDerived(Object)} method.</li>
- *   <li>Queries ({@link #contains contains}) and write operations ({@link #add add},
- *       {@link #remove remove}) obtain the storage values by calls to the
- *       {@link #derivedToBase(Object)} method.</li>
- * </ul>
- *
- * {@section Constraints}
- * <ul>
- *   <li>This set does not support {@code null} values, since {@code null} is used as a
- *       sentinel value when no mapping from {@linkplain #base} to {@code this} exists.</li>
- *   <li>Instances of this class are serializable if their underlying {@linkplain #base} set
- *       is serializable.</li>
- *   <li>This class performs no synchronization by itself. Nevertheless instances of this class
- *       may be thread-safe (depending on the sub-class implementation) if the underlying
- *       {@linkplain #base} set (including its iterator) is thread-safe.</li>
- * </ul>
- *
- * {@section Performance considerations}
- * This class does not cache any value, since the {@linkplain #base} set is presumed modifiable.
- * If the base set is known to be immutable, then sub-classes may consider to cache some values,
- * especially the result of the {@link #size()} method.
+ * An iterator which performs conversions on the fly using the given converter.
+ * If a value is converted into a null value, then this iterator skips that value.
+ * Consequently this iterator can not returns null value.
  *
  * @param <B> The type of elements in the backing set.
  * @param <E> The type of elements in this set.
@@ -60,216 +34,67 @@ import org.apache.sis.util.ArgumentCheck
  * @version 0.3
  * @module
  */
-@Decorator(Set.class)
-public abstract class DerivedSet<B,E> extends AbstractSet<E> implements CheckedContainer<E>, Serializable {
+@Decorator(Iterator.class)
+final class DerivedIterator<B,E> implements Iterator<E> {
     /**
-     * Serial number for inter-operability with different versions.
+     * The original iterator to wrap.
      */
-    private static final long serialVersionUID = -4662336508586424581L;
+    private final Iterator<B> iterator;
 
     /**
-     * The base set whose values are derived from.
-     *
-     * @see #baseToDerived(Object)
-     * @see #derivedToBase(Object)
+     * The converter from the original values to the converted values.
      */
-    protected final Set<B> base;
+    private final ObjectConverter<B,E> converter;
 
     /**
-     * The derived type.
+     * The next element to be returned, or {@code null}.
      */
-    private final Class<E> derivedType;
+    private transient E next;
 
     /**
-     * Creates a new derived set from the specified base set.
-     *
-     * @param base The base set.
-     * @param derivedType The type of elements in this derived set.
+     * Creates a new iterator wrapping the given original iterator and converting the
+     * values using the given converter.
      */
-    public DerivedSet(final Set<B> base, final Class<E> derivedType) {
-        ArgumentChecks.ensureNonNull("base",        this.base        = base);
-        ArgumentChecks.ensureNonNull("derivedType", this.derivedType = derivedType);
+    DerivedIterator(final Iterator<B> iterator, ObjectConverter<B,E> converter) {
+        this.iterator  = iterator;
+        this.converter = converter;
     }
 
     /**
-     * Returns the derived element type.
+     * Returns {@code true} if the iteration has more elements.
      */
     @Override
-    public Class<E> getElementType() {
-        return derivedType;
-    }
-
-    /**
-     * Transforms a value in the {@linkplain #base} set to a value in this set.
-     * If there is no mapping in the derived set for the specified element,
-     * then this method returns {@code null}.
-     *
-     * @param  element A value in the {@linkplain #base} set.
-     * @return The value that this view should contains instead of {@code element}, or {@code null}.
-     */
-    protected abstract E baseToDerived(final B element);
-
-    /**
-     * Transforms a value in this set to a value in the {@linkplain #base} set.
-     *
-     * @param  element A value in this set.
-     * @return The value stored in the {@linkplain #base} set.
-     */
-    protected abstract B derivedToBase(final E element);
-
-    /**
-     * Returns an iterator over the elements contained in this set.
-     * The iterator will invokes the {@link #baseToDerived(Object)} method for each element.
-     *
-     * @return an iterator over the elements contained in this set.
-     */
-    @Override
-    public Iterator<E> iterator() {
-        return new Iter(base.iterator());
-    }
-
-    /**
-     * Returns the number of elements in this set. The default implementation counts
-     * the number of elements returned by the {@link #iterator() iterator}.
-     * Subclasses are encouraged to cache this value if they know that the
-     * {@linkplain #base} set is immutable.
-     *
-     * @return the number of elements in this set.
-     */
-    @Override
-    public int size() {
-        int count = 0;
-        for (final Iterator<E> it=iterator(); it.hasNext();) {
-            it.next();
-            count++;
+    public boolean hasNext() {
+        while (next == null) {
+            if (!iterator.hasNext()) {
+                return false;
+            }
+            next = converter.convert(iterator.next());
         }
-        return count;
-    }
-
-    /**
-     * Returns {@code true} if this set contains no elements.
-     *
-     * @return {@code true} if this set contains no elements.
-     */
-    @Override
-    public boolean isEmpty() {
-        return !base.isEmpty() || !iterator().hasNext();
+        return true;
     }
 
     /**
-     * Returns {@code true} if this set contains the specified element.
-     * This method first checks if the given element is an instance of {@link #derivedType},
-     * then delegates to the {@link #base} set like below:
-     *
-     * {@preformat java
-     *     return base.contains(derivedToBase(element));
-     * }
-     *
-     * @param  element object to be checked for containment in this set.
-     * @return {@code true} if this set contains the specified element.
+     * Returns the next element in the iteration.
      */
     @Override
-    public boolean contains(final Object element) {
-        return derivedType.isInstance(element) && base.contains(derivedToBase(derivedType.cast(element)));
-    }
-
-    /**
-     * Ensures that this set contains the specified element.
-     * This method first checks if the given element is non-null,
-     * then delegates to the {@link #base} set like below:
-     *
-     * {@preformat java
-     *     return base.add(derivedToBase(element));
-     * }
-     *
-     * @param  element element whose presence in this set is to be ensured.
-     * @return {@code true} if the set changed as a result of the call.
-     * @throws UnsupportedOperationException if the {@linkplain #base} set doesn't
-     *         supports the {@code add} operation.
-     */
-    @Override
-    public boolean add(final E element) throws UnsupportedOperationException {
-        ArgumentChecks.ensureNonNull("element", element);
-        return base.add(derivedToBase(element));
+    public E next() {
+        E value = next;
+        next = null;
+        while (value == null) {
+            value = converter.convert(iterator.next());
+        }
+        return value;
     }
 
     /**
-     * Removes a single instance of the specified element from this set.
-     * This method first checks if the given element is an instance of {@link #derivedType},
-     * then delegates to the {@link #base} set like below:
-     *
-     * {@preformat java
-     *     return base.remove(derivedToBase(element));
-     * }
+     * Removes from the underlying set the last element returned by the iterator.
      *
-     * @param  element element to be removed from this set, if present.
-     * @return {@code true} if the set contained the specified element.
-     * @throws UnsupportedOperationException if the {@linkplain #base} set doesn't
-     *         supports the {@code remove} operation.
+     * @throws UnsupportedOperationException if the underlying collection doesn't supports the
+     * {@code remove} operation.
      */
     @Override
-    public boolean remove(final Object element) throws UnsupportedOperationException {
-        return derivedType.isInstance(element) && base.remove(derivedToBase(derivedType.cast(element)));
-    }
-
-    /**
-     * Iterates through the elements in the set.
-     */
-    @Decorator(Iterator.class)
-    private final class Iter implements Iterator<E> {
-        /**
-         * The iterator from the {@linkplain DerivedSet#base} set.
-         */
-        private final Iterator<B> iterator;
-
-        /**
-         * The next element to be returned, or {@code null}.
-         */
-        private transient E next;
-
-        /**
-         * The iterator from the {@linkplain DerivedSet#base} set.
-         */
-        public Iter(final Iterator<B> iterator) {
-            this.iterator = iterator;
-        }
-
-        /**
-         * Returns {@code true} if the iteration has more elements.
-         */
-        @Override
-        public boolean hasNext() {
-            while (next == null) {
-                if (!iterator.hasNext()) {
-                    return false;
-                }
-                next = baseToDerived(iterator.next());
-            }
-            return true;
-        }
-
-        /**
-         * Returns the next element in the iteration.
-         */
-        @Override
-        public E next() {
-            E value = next;
-            next = null;
-            while (value == null) {
-                value = baseToDerived(iterator.next());
-            }
-            return value;
-        }
-
-        /**
-         * Removes from the underlying set the last element returned by the iterator.
-         *
-         * @throws UnsupportedOperationException if the {@linkplain #base} set doesn't
-         *         supports the {@code remove} operation.
-         */
-        @Override
-        public void remove() {
-            iterator.remove();
-        }
+    public void remove() {
+        iterator.remove();
     }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedMap.java?rev=1404561&r1=1404560&r2=1404561&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedMap.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedMap.java Thu Nov  1 12:32:19 2012
@@ -16,25 +16,29 @@
  */
 package org.apache.sis.util.collection;
 
-import java.io.Serializable;
-import java.util.AbstractMap;
-import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
+import java.util.EnumSet;
+import java.util.AbstractMap;
+import java.io.Serializable;
 import org.apache.sis.util.Decorator;
+import org.apache.sis.util.ObjectConverter;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.math.FunctionProperty;
 
 
 /**
  * A map whose keys are derived <cite>on-the-fly</cite> from an other map.
- * Conversions are performed when needed by two methods:
+ * Conversions are performed when needed by the following methods:
  *
  * <ul>
  *   <li>The iterators over the {@linkplain #keySet() key set} or {@linkplain #entrySet() entry set}
- *       obtain the derived values by calls to the {@link #baseToDerived(Object)} method.</li>
+ *       obtain the derived keys using the {@link #keyConverter}.</li>
+ *   <li>The iterators over the {@linkplain #values() values} or {@linkplain #entrySet() entry set}
+ *       obtain the derived values using the {@link #valueConverter}.</li>
  *   <li>Queries ({@link #get get}, {@link #containsKey containsKey}) and write operations
- *       ({@link #put put}, {@link #remove remove}) obtain the storage values by calls to the
- *       {@link #derivedToBase(Object)} method.</li>
+ *       ({@link #put put}, {@link #remove remove}) obtain the storage values using the
+ *       inverse of the above converters.</li>
  * </ul>
  *
  * {@section Constraints}
@@ -52,6 +56,7 @@ import org.apache.sis.util.ArgumentCheck
  * especially the result of the {@link #size()} method.
  *
  * @param <BK> The type of keys in the backing map.
+ * @param <BV> The type of values in the backing map.
  * @param <K>  The type of keys in this map.
  * @param <V>  The type of values in both this map and the underlying map.
  *
@@ -61,7 +66,9 @@ import org.apache.sis.util.ArgumentCheck
  * @module
  */
 @Decorator(Map.class)
-public abstract class DerivedMap<BK,K,V> extends AbstractMap<K,V> implements Serializable {
+class DerivedMap<BK,BV,K,V> extends AbstractMap<K,V> implements
+        ObjectConverter<Map.Entry<BK,BV>, Map.Entry<K,V>>, Serializable
+{
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -69,265 +76,290 @@ public abstract class DerivedMap<BK,K,V>
 
     /**
      * The base map whose keys are derived from.
-     *
-     * @see #baseToDerived(Object)
-     * @see #derivedToBase(Object)
-     */
-    protected final Map<BK,V> base;
-
-    /**
-     * Key set. Will be constructed only when first needed.
-     *
-     * @see #keySet()
-     */
-    private transient Set<K> keySet;
-
-    /**
-     * Entry set. Will be constructed only when first needed.
-     *
-     * @see #entrySet()
      */
-    private transient Set<? extends Map.Entry<K,V>> entrySet;
+    protected final Map<BK,BV> base;
 
     /**
-     * The derived key type.
+     * The converter from the base to the derived keys.
      */
-    private final Class<K> keyType;
+    protected final ObjectConverter<BK,K> keyConverter;
 
     /**
-     * Creates a new derived map from the specified base map.
-     *
-     * @param base The base map.
-     * @param keyType the type of keys in the derived map.
+     * The converter from the base to the derived values.
      */
-    public DerivedMap(final Map<BK,V> base, final Class<K> keyType) {
-        ArgumentChecks.ensureNonNull("base",    this.base    = base);
-        ArgumentChecks.ensureNonNull("keyType", this.keyType = keyType);
-    }
+    protected final ObjectConverter<BV,V> valueConverter;
 
     /**
-     * Transforms a key from the {@linkplain #base} map to a key in this map.
-     * If there is no key in the derived map for the specified base key,
-     * then this method returns {@code null}.
+     * Key set. Will be constructed only when first needed.
      *
-     * @param  key A ley from the {@linkplain #base} map.
-     * @return The key that this view should contains instead of {@code key}, or {@code null}.
+     * @see #keySet()
      */
-    protected abstract K baseToDerived(final BK key);
+    private transient Set<K> keySet;
 
     /**
-     * Transforms a key from this derived map to a key in the {@linkplain #base} map.
+     * Entry set. Will be constructed only when first needed.
      *
-     * @param  key A key in this map.
-     * @return The key stored in the {@linkplain #base} map.
+     * @see #entrySet()
      */
-    protected abstract BK derivedToBase(final K key);
+    private transient Set<Map.Entry<K,V>> entrySet;
 
     /**
-     * Returns {@code true} if this map contains no key-value mappings.
+     * Creates a new derived map from the specified base map.
      *
-     * @return {@code true} if this map contains no key-value mappings.
-     */
-    @Override
-    public boolean isEmpty() {
-        return base.isEmpty() || entrySet().isEmpty();
+     * @param base           The base map.
+     * @param keyConverter   The converter for the keys.
+     * @param valueConverter The converter for the values.
+     */
+    static <BK,BV,K,V> Map<K,V> create(final Map<BK,BV> base,
+                                       final ObjectConverter<BK,K> keyConverter,
+                                       final ObjectConverter<BV,V> valueConverter)
+    {
+        final Set<FunctionProperty> kp =   keyConverter.properties();
+        final Set<FunctionProperty> vp = valueConverter.properties();
+        if (kp.contains(FunctionProperty.INVERTIBLE)) {
+            if (vp.contains(FunctionProperty.INVERTIBLE)) {
+                return new Invertible<>(base, keyConverter, valueConverter);
+            }
+            return new InvertibleKey<>(base, keyConverter, valueConverter);
+        }
+        if (vp.contains(FunctionProperty.INVERTIBLE)) {
+            return new InvertibleValue<>(base, keyConverter, valueConverter);
+        }
+        return new DerivedMap<>(base, keyConverter, valueConverter);
     }
 
     /**
-     * Returns {@code true} if this map maps one or more keys to this value.
-     * The default implementation delegates directly to the {@linkplain #base} map.
+     * Creates a new derived map from the specified base map.
      *
-     * @return {@code true} if this map maps one or more keys to this value.
+     * @param base           The base map.
+     * @param keyConverter   The converter for the keys.
+     * @param valueConverter The converter for the values.
      */
-    @Override
-    public boolean containsValue(final Object value) {
-        return base.containsValue(value);
+    private DerivedMap(final Map<BK,BV> base,
+                       final ObjectConverter<BK,K> keyConverter,
+                       final ObjectConverter<BV,V> valueConverter)
+    {
+        this.base           = base;
+        this.keyConverter   = keyConverter;
+        this.valueConverter = valueConverter;
     }
 
     /**
-     * Returns {@code true} if this map contains a mapping for the specified key.
-     * This method first checks if the given element is an instance of {@link #keyType},
-     * then delegates to the {@link #base} map like below:
+     * Returns the number of entries in this map.
      *
-     * {@preformat java
-     *     return base.containsKey(derivedToBase(element));
-     * }
-     *
-     * @param  key key whose presence in this map is to be tested.
-     * @return {@code true} if this map contains a mapping for the specified key.
+     * @return The number of entries in this map.
      */
     @Override
-    public boolean containsKey(final Object key) {
-        return keyType.isInstance(key) && base.containsKey(derivedToBase(keyType.cast(key)));
+    public int size() {
+        return base.isEmpty() ? 0 : keySet().size();
     }
 
     /**
-     * Returns the value to which this map maps the specified key.
-     * This method first checks if the given element is an instance of {@link #keyType},
-     * then delegates to the {@link #base} map like below:
-     *
-     * {@preformat java
-     *     return base.get(derivedToBase(element));
-     * }
+     * Returns {@code true} if this map contains no key-value mappings.
      *
-     * @param  key key whose associated value is to be returned.
-     * @return the value to which this map maps the specified key.
+     * @return {@code true} if this map contains no key-value mappings.
      */
     @Override
-    public V get(final Object key) {
-        return keyType.isInstance(key) ? base.get(derivedToBase(keyType.cast(key))) : null;
+    public boolean isEmpty() {
+        return base.isEmpty() || keySet().isEmpty();
     }
 
     /**
      * Associates the specified value with the specified key in this map.
-     * This method first checks if the given element is non-null,
-     * then delegates to the {@link #base} map like below:
-     *
-     * {@preformat java
-     *     return base.put(derivedToBase(key), value);
-     * }
      *
      * @param  key key with which the specified value is to be associated.
      * @param  value value to be associated with the specified key.
      * @return previous value associated with specified key, or {@code null}
      *         if there was no mapping for key.
-     * @throws UnsupportedOperationException if the {@linkplain #base} map doesn't
-     *         supports the {@code put} operation.
+     * @throws UnsupportedOperationException if the converters are not invertible,
+     *         or the {@linkplain #base} map doesn't supports the {@code put} operation.
      */
     @Override
     public V put(final K key, final V value) throws UnsupportedOperationException {
-        return base.put(derivedToBase(key), value);
+        ArgumentChecks.ensureNonNull("key", key);
+        return valueConverter.convert(base.put(
+                 keyConverter.inverse().convert(key),
+               valueConverter.inverse().convert(value)));
     }
 
     /**
-     * Removes the mapping for this key from this map if present.
-     * This method first checks if the given element is an instance of {@link #keyType},
-     * then delegates to the {@link #base} map like below:
-     *
-     * {@preformat java
-     *     return base.remove(derivedToBase(element));
-     * }
-     *
-     * @param  key key whose mapping is to be removed from the map.
-     * @return previous value associated with specified key, or {@code null}
-     *         if there was no entry for key.
-     * @throws UnsupportedOperationException if the {@linkplain #base} map doesn't
-     *         supports the {@code remove} operation.
+     * A {@link DerivedMap} used when the {@link #keyConverter} is invertible.
+     * Availability of the inverse conversion allows us to delegate some operations
+     * to the {@linkplain #base} map instead than iterating over all entries.
      */
-    @Override
-    public V remove(final Object key) throws UnsupportedOperationException {
-        return keyType.isInstance(key) ? base.remove(derivedToBase(keyType.cast(key))) : null;
-    }
+    private static class InvertibleKey<BK,BV,K,V> extends DerivedMap<BK,BV,K,V> {
+        private static final long serialVersionUID = -7770446176017835821L;
 
-    /**
-     * Returns a set view of the keys contained in this map.
-     *
-     * @return a set view of the keys contained in this map.
-     */
-    @Override
-    public Set<K> keySet() {
-        if (keySet == null) {
-            keySet = new KeySet(base.keySet());
-        }
-        return keySet;
-    }
+        /** The inverse of {@link #keyConverter}. */
+        protected final ObjectConverter<K,BK> keyInverse;
 
-    /**
-     * Returns a collection view of the values contained in this map.
-     *
-     * @return a collection view of the values contained in this map.
-     */
-    @Override
-    public Collection<V> values() {
-        return base.values();
-    }
-
-    /**
-     * Returns a set view of the mappings contained in this map.
-     *
-     * @return a set view of the mappings contained in this map.
-     */
-    @Override
-    @SuppressWarnings("unchecked")
-    public Set<Map.Entry<K,V>> entrySet() {
-        if (entrySet == null) {
-            entrySet = new EntrySet(base.entrySet());
+        InvertibleKey(final Map<BK,BV> base,
+                      final ObjectConverter<BK,K> keyConverter,
+                      final ObjectConverter<BV,V> valueConverter)
+        {
+            super(base, keyConverter, valueConverter);
+            keyInverse = keyConverter.inverse();
         }
-        return (Set<Map.Entry<K,V>>) entrySet; // Safe because read-only.
-    }
-
-    /**
-     * The key set.
-     */
-    @Decorator(Set.class)
-    private final class KeySet extends DerivedSet<BK,K> {
-        private static final long serialVersionUID = -2931806200277420177L;
 
-        public KeySet(final Set<BK> base) {
-            super(base, keyType);
+        @Override
+        public final V get(final Object key) {
+            final Class<? extends K> type = keyConverter.getTargetClass();
+            return type.isInstance(key) ? valueConverter.convert(base.get(keyInverse.convert(type.cast(key)))) : null;
         }
 
         @Override
-        protected K baseToDerived(final BK element) {
-            return DerivedMap.this.baseToDerived(element);
+        public final V remove(final Object key) throws UnsupportedOperationException {
+            final Class<? extends K> type = keyConverter.getTargetClass();
+            return type.isInstance(key) ? valueConverter.convert(base.remove(keyInverse.convert(type.cast(key)))) : null;
         }
 
         @Override
-        protected BK derivedToBase(final K element) {
-            return DerivedMap.this.derivedToBase(element);
+        public final boolean containsKey(final Object key) {
+            final Class<? extends K> type = keyConverter.getTargetClass();
+            return type.isInstance(key) && base.containsKey(keyInverse.convert(type.cast(key)));
         }
     }
 
     /**
-     * The entry set.
+     * A {@link DerivedMap} used when the {@link #valueConverter} is invertible.
+     * Availability of the inverse conversion allows us to delegate some operations
+     * to the {@linkplain #base} map instead than iterating over all entries.
      */
-    @Decorator(Set.class)
-    private final class EntrySet extends DerivedSet<Map.Entry<BK,V>, Entry<BK,K,V>> {
-        private static final long serialVersionUID = -1328083271645313149L;
+    private static final class InvertibleValue<BK,BV,K,V> extends DerivedMap<BK,BV,K,V> {
+        private static final long serialVersionUID = 6249800498911409046L;
 
-        @SuppressWarnings({"unchecked","rawtypes"})
-        public EntrySet(final Set<Map.Entry<BK,V>> base) {
-            super(base, (Class) Entry.class);
-        }
+        /** The inverse of {@link #valueConverter}. */
+        private final ObjectConverter<V,BV> valueInverse;
 
-        @Override
-        protected Entry<BK,K,V> baseToDerived(final Map.Entry<BK,V> entry) {
-            final K derived = DerivedMap.this.baseToDerived(entry.getKey());
-            return (derived != null) ? new Entry<>(entry, derived) : null;
+        InvertibleValue(final Map<BK,BV> base,
+                        final ObjectConverter<BK,K> keyConverter,
+                        final ObjectConverter<BV,V> valueConverter)
+        {
+            super(base, keyConverter, valueConverter);
+            valueInverse = valueConverter.inverse();
         }
 
         @Override
-        protected Map.Entry<BK,V> derivedToBase(final Entry<BK,K,V> element) {
-            return element.entry;
+        public boolean containsValue(final Object value) {
+            final Class<? extends V> type = valueConverter.getTargetClass();
+            return type.isInstance(value) && base.containsValue(valueInverse.convert(type.cast(value)));
         }
     }
 
     /**
-     * The entry element.
+     * A {@link DerivedMap} used when both the {@link #keyConverter} and {@link #valueConverter}
+     * are invertible. Availability of the inverse conversion allows us to delegate some operations
+     * to the {@linkplain #base} map instead than iterating over all entries.
      */
-    @Decorator(Map.Entry.class)
-    private static final class Entry<BK,K,V> implements Map.Entry<K,V> {
-        public final Map.Entry<BK,V> entry;
-        private final K derived;
+    private static final class Invertible<BK,BV,K,V> extends InvertibleKey<BK,BV,K,V> {
+        private static final long serialVersionUID = 3830322680676020356L;
 
-        public Entry(final Map.Entry<BK,V> entry, final K derived) {
-            this.entry   = entry;
-            this.derived = derived;
+        /** The inverse of {@link #valueConverter}. */
+        private final ObjectConverter<V,BV> valueInverse;
+
+        /** The inverse of this entry converter. */
+        private transient ObjectConverter<Entry<K,V>, Entry<BK,BV>> inverse;
+
+        Invertible(final Map<BK,BV> base,
+                   final ObjectConverter<BK,K> keyConverter,
+                   final ObjectConverter<BV,V> valueConverter)
+        {
+            super(base, keyConverter, valueConverter);
+            valueInverse = valueConverter.inverse();
         }
 
         @Override
-        public K getKey() {
-            return derived;
+        public boolean containsValue(final Object value) {
+            final Class<? extends V> type = valueConverter.getTargetClass();
+            return type.isInstance(value) && base.containsValue(valueInverse.convert(type.cast(value)));
         }
 
         @Override
-        public V getValue() {
-            return entry.getValue();
+        public V put(final K key, final V value) {
+            ArgumentChecks.ensureNonNull("key", key);
+            return valueConverter.convert(base.put(keyInverse.convert(key), valueInverse.convert(value)));
         }
 
         @Override
-        public V setValue(V value) {
-            return entry.setValue(value);
+        public ObjectConverter<Entry<K,V>, Entry<BK,BV>> inverse() {
+            if (inverse == null) {
+                inverse = new DerivedMap<>(null, keyInverse, valueInverse);
+            }
+            return inverse;
         }
     }
+
+    /**
+     * Returns a set view of the keys contained in this map.
+     */
+    @Override
+    public final Set<K> keySet() {
+        if (keySet == null) {
+            keySet = DerivedSet.create(base.keySet(), keyConverter);
+        }
+        return keySet;
+    }
+
+    /**
+     * Returns a set view of the mappings contained in this map.
+     */
+    @Override
+    public final Set<Map.Entry<K,V>> entrySet() {
+        if (entrySet == null) {
+            entrySet = DerivedSet.create(base.entrySet(), this);
+        }
+        return entrySet;
+    }
+
+    /**
+     * Returns the properties of the entry converter, as the union of some properties
+     * of the key and value converters.
+     */
+    @Override
+    public final Set<FunctionProperty> properties() {
+        final EnumSet<FunctionProperty> properties = EnumSet.of(
+                FunctionProperty.INVERTIBLE,
+                FunctionProperty.INJECTIVE,
+                FunctionProperty.SURJECTIVE);
+        properties.retainAll(  keyConverter.properties());
+        properties.retainAll(valueConverter.properties());
+        return properties;
+    }
+
+    /**
+     * Returns the source class of the map entry converter.
+     * Defined because the interface requires so but not used.
+     */
+    @Override
+    public final Class<? super Entry<BK,BV>> getSourceClass() {
+        return Entry.class;
+    }
+
+    /**
+     * Returns the target class of the map entry converter.
+     * Defined because the interface requires so but not used.
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public final Class<? extends Entry<K,V>> getTargetClass() {
+        return (Class) Entry.class;
+    }
+
+    /**
+     * Converts the given entry.
+     */
+    @Override
+    public final Entry<K,V> convert(final Entry<BK,BV> entry) {
+        final K key   =   keyConverter.convert(entry.getKey());
+        final V value = valueConverter.convert(entry.getValue());
+        return (key != null) ? new SimpleEntry<>(key, value) : null;
+    }
+
+    /**
+     * To be defined in the {@link Invertible} sub-class only.
+     */
+    @Override
+    public ObjectConverter<Entry<K,V>, Entry<BK,BV>> inverse() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java?rev=1404561&r1=1404560&r2=1404561&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedSet.java Thu Nov  1 12:32:19 2012
@@ -21,19 +21,19 @@ import java.util.Iterator;
 import java.util.AbstractSet;
 import java.io.Serializable;
 import org.apache.sis.util.Decorator;
+import org.apache.sis.util.ObjectConverter;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.math.FunctionProperty;
 
 
 /**
  * A set whose values are derived <cite>on-the-fly</cite> from an other set.
- * Conversions are performed when needed by two methods:
+ * Conversions are performed when needed by two converters:
  *
  * <ul>
- *   <li>The {@linkplain #iterator() iterator} obtain the derived values by calls to the
- *       {@link #baseToDerived(Object)} method.</li>
- *   <li>Queries ({@link #contains contains}) and write operations ({@link #add add},
- *       {@link #remove remove}) obtain the storage values by calls to the
- *       {@link #derivedToBase(Object)} method.</li>
+ *   <li>The {@linkplain #iterator() iterator} obtain the derived values using the {@linkplain #converter}.</li>
+ *   <li>Queries ({@link #contains contains}) and write operations ({@link #add add}, {@link #remove remove})
+ *       obtain the storage values using the {@link Invertible#inverse} converter.</li>
  * </ul>
  *
  * {@section Constraints}
@@ -41,10 +41,11 @@ import org.apache.sis.util.ArgumentCheck
  *   <li>This set does not support {@code null} values, since {@code null} is used as a
  *       sentinel value when no mapping from {@linkplain #base} to {@code this} exists.</li>
  *   <li>Instances of this class are serializable if their underlying {@linkplain #base} set
- *       is serializable.</li>
+ *       and the {@linkplain #converter} are serializable.</li>
  *   <li>This class performs no synchronization by itself. Nevertheless instances of this class
  *       may be thread-safe (depending on the sub-class implementation) if the underlying
- *       {@linkplain #base} set (including its iterator) is thread-safe.</li>
+ *       {@linkplain #base} set (including its iterator) and the {@linkplain #converter}
+ *       are thread-safe.</li>
  * </ul>
  *
  * {@section Performance considerations}
@@ -61,7 +62,7 @@ import org.apache.sis.util.ArgumentCheck
  * @module
  */
 @Decorator(Set.class)
-public abstract class DerivedSet<B,E> extends AbstractSet<E> implements CheckedContainer<E>, Serializable {
+class DerivedSet<B,E> extends AbstractSet<E> implements CheckedContainer<E>, Serializable {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -69,53 +70,46 @@ public abstract class DerivedSet<B,E> ex
 
     /**
      * The base set whose values are derived from.
-     *
-     * @see #baseToDerived(Object)
-     * @see #derivedToBase(Object)
      */
     protected final Set<B> base;
 
     /**
-     * The derived type.
+     * The converter from the base to the derived type.
      */
-    private final Class<E> derivedType;
+    protected final ObjectConverter<B,E> converter;
 
     /**
      * Creates a new derived set from the specified base set.
      *
-     * @param base The base set.
-     * @param derivedType The type of elements in this derived set.
-     */
-    public DerivedSet(final Set<B> base, final Class<E> derivedType) {
-        ArgumentChecks.ensureNonNull("base",        this.base        = base);
-        ArgumentChecks.ensureNonNull("derivedType", this.derivedType = derivedType);
-    }
-
-    /**
-     * Returns the derived element type.
+     * @param base      The base set.
+     * @param converter The converter from the type in the base set to the type in the derived set.
      */
-    @Override
-    public Class<E> getElementType() {
-        return derivedType;
+    static <B,E> Set<E> create(final Set<B> base, final ObjectConverter<B,E> converter) {
+        final Set<FunctionProperty> properties = converter.properties();
+        if (properties.contains(FunctionProperty.INVERTIBLE)) {
+            return new Invertible<>(base, converter);
+        }
+        return new DerivedSet<>(base, converter);
     }
 
     /**
-     * Transforms a value in the {@linkplain #base} set to a value in this set.
-     * If there is no mapping in the derived set for the specified element,
-     * then this method returns {@code null}.
+     * Creates a new derived set from the specified base set.
      *
-     * @param  element A value in the {@linkplain #base} set.
-     * @return The value that this view should contains instead of {@code element}, or {@code null}.
+     * @param base The base set.
+     * @param converter The type of elements in this derived set.
      */
-    protected abstract E baseToDerived(final B element);
+    private DerivedSet(final Set<B> base, final ObjectConverter<B,E> converter) {
+        this.base      = base;
+        this.converter = converter;
+    }
 
     /**
-     * Transforms a value in this set to a value in the {@linkplain #base} set.
-     *
-     * @param  element A value in this set.
-     * @return The value stored in the {@linkplain #base} set.
+     * Returns the derived element type.
      */
-    protected abstract B derivedToBase(final E element);
+    @Override
+    public final Class<? extends E> getElementType() {
+        return converter.getTargetClass();
+    }
 
     /**
      * Returns an iterator over the elements contained in this set.
@@ -124,8 +118,8 @@ public abstract class DerivedSet<B,E> ex
      * @return an iterator over the elements contained in this set.
      */
     @Override
-    public Iterator<E> iterator() {
-        return new Iter(base.iterator());
+    public final Iterator<E> iterator() {
+        return new DerivedIterator<>(base.iterator(), converter);
     }
 
     /**
@@ -157,29 +151,12 @@ public abstract class DerivedSet<B,E> ex
     }
 
     /**
-     * Returns {@code true} if this set contains the specified element.
-     * This method first checks if the given element is an instance of {@link #derivedType},
-     * then delegates to the {@link #base} set like below:
-     *
-     * {@preformat java
-     *     return base.contains(derivedToBase(element));
-     * }
-     *
-     * @param  element object to be checked for containment in this set.
-     * @return {@code true} if this set contains the specified element.
-     */
-    @Override
-    public boolean contains(final Object element) {
-        return derivedType.isInstance(element) && base.contains(derivedToBase(derivedType.cast(element)));
-    }
-
-    /**
      * Ensures that this set contains the specified element.
      * This method first checks if the given element is non-null,
      * then delegates to the {@link #base} set like below:
      *
      * {@preformat java
-     *     return base.add(derivedToBase(element));
+     *     return base.add(inverse.convert(element));
      * }
      *
      * @param  element element whose presence in this set is to be ensured.
@@ -190,86 +167,84 @@ public abstract class DerivedSet<B,E> ex
     @Override
     public boolean add(final E element) throws UnsupportedOperationException {
         ArgumentChecks.ensureNonNull("element", element);
-        return base.add(derivedToBase(element));
+        return base.add(converter.inverse().convert(element));
     }
 
     /**
-     * Removes a single instance of the specified element from this set.
-     * This method first checks if the given element is an instance of {@link #derivedType},
-     * then delegates to the {@link #base} set like below:
-     *
-     * {@preformat java
-     *     return base.remove(derivedToBase(element));
-     * }
+     * A {@link DerivedSet} for invertible converters. Availability of the inverse conversion
+     * allows us to delegate the {@link #contains(Object)} and {@linkplain #remove(Object)}
+     * operations to the {@linkplain #base} set instead than iterating over all elements.
      *
-     * @param  element element to be removed from this set, if present.
-     * @return {@code true} if the set contained the specified element.
-     * @throws UnsupportedOperationException if the {@linkplain #base} set doesn't
-     *         supports the {@code remove} operation.
-     */
-    @Override
-    public boolean remove(final Object element) throws UnsupportedOperationException {
-        return derivedType.isInstance(element) && base.remove(derivedToBase(derivedType.cast(element)));
-    }
-
-    /**
-     * Iterates through the elements in the set.
+     * @param <B> The type of elements in the backing set.
+     * @param <E> The type of elements in this set.
      */
-    @Decorator(Iterator.class)
-    private final class Iter implements Iterator<E> {
+    private static final class Invertible<B,E> extends DerivedSet<B,E> {
         /**
-         * The iterator from the {@linkplain DerivedSet#base} set.
+         * For cross-version compatibility.
          */
-        private final Iterator<B> iterator;
+        private static final long serialVersionUID = 5957167307119709856L;
 
         /**
-         * The next element to be returned, or {@code null}.
+         * The converter from the derived to the base type.
          */
-        private transient E next;
+        private final ObjectConverter<E,B> inverse;
 
         /**
-         * The iterator from the {@linkplain DerivedSet#base} set.
+         * Creates a new derived set from the specified base set.
+         *
+         * @param base The base set.
+         * @param converter The type of elements in this derived set.
          */
-        public Iter(final Iterator<B> iterator) {
-            this.iterator = iterator;
+        Invertible(final Set<B> base, final ObjectConverter<B,E> converter) {
+            super(base, converter);
+            inverse = converter.inverse();
         }
 
         /**
-         * Returns {@code true} if the iteration has more elements.
+         * {@inheritDoc}
          */
         @Override
-        public boolean hasNext() {
-            while (next == null) {
-                if (!iterator.hasNext()) {
-                    return false;
-                }
-                next = baseToDerived(iterator.next());
-            }
-            return true;
+        public boolean add(final E element) throws UnsupportedOperationException {
+            ArgumentChecks.ensureNonNull("element", element);
+            return base.add(inverse.convert(element));
         }
 
         /**
-         * Returns the next element in the iteration.
+         * Returns {@code true} if this set contains the specified element.
+         * This method first checks if the given element is an instance of {@link #getElementType()},
+         * then delegates to the {@link #base} set like below:
+         *
+         * {@preformat java
+         *     return base.contains(inverse.convert(element));
+         * }
+         *
+         * @param  element object to be checked for containment in this set.
+         * @return {@code true} if this set contains the specified element.
          */
         @Override
-        public E next() {
-            E value = next;
-            next = null;
-            while (value == null) {
-                value = baseToDerived(iterator.next());
-            }
-            return value;
+        public boolean contains(final Object element) {
+            final Class<? extends E> type = getElementType();
+            return type.isInstance(element) && base.contains(inverse.convert(type.cast(element)));
         }
 
         /**
-         * Removes from the underlying set the last element returned by the iterator.
+         * Removes a single instance of the specified element from this set.
+         * This method first checks if the given element is an instance of {@link #getElementType},
+         * then delegates to the {@link #base} set like below:
+         *
+         * {@preformat java
+         *     return base.remove(inverse.convert(element));
+         * }
          *
+         * @param  element element to be removed from this set, if present.
+         * @return {@code true} if the set contained the specified element.
          * @throws UnsupportedOperationException if the {@linkplain #base} set doesn't
          *         supports the {@code remove} operation.
          */
         @Override
-        public void remove() {
-            iterator.remove();
+        public boolean remove(final Object element) throws UnsupportedOperationException {
+            final Class<? extends E> type = getElementType();
+            return type.isInstance(element) && base.remove(inverse.convert(type.cast(element)));
         }
     }
 }

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java?rev=1404561&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java Thu Nov  1 12:32:19 2012
@@ -0,0 +1,148 @@
+/*
+ * 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.util.collection;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.EnumSet;
+import org.apache.sis.math.FunctionProperty;
+import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link DerivedMap}. For the purpose of this test, this class implements an
+ * {@link ObjectConverter} for which input values are multiplied by 100, except value
+ * {@value #EXCLUDED} which is converted to {@code null} (meaning: excluded from the
+ * converted map).
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+@DependsOn(DerivedSetTest.class)
+public final strictfp class DerivedMapTest extends TestCase implements ObjectConverter<Integer,Integer> {
+    /**
+     * The value to replace by {@code null}.
+     */
+    private static final int EXCLUDED = 17;
+
+    /**
+     * Fills test values in the given maps.
+     */
+    private static void fill(final Map<Integer,Integer> source,
+                             final Map<Integer,Integer> target)
+    {
+        assertNull(source.put(4,   7 ));
+        assertNull(target.put(400, 70));
+        assertNull(source.put(3,   8 ));
+        assertNull(target.put(300, 80));
+        assertNull(source.put(9,   1 ));
+        assertNull(target.put(900, 10));
+        assertNull(source.put(2,   1 ));
+        assertNull(target.put(200, 10));
+    }
+
+    /**
+     * Tests {@link DerivedMap} without excluded value.
+     */
+    @Test
+    public void testNoExclusion() {
+        final Map<Integer,Integer> source = new HashMap<>();
+        final Map<Integer,Integer> target = new HashMap<>();
+        final Map<Integer,Integer> tested = DerivedMap.create(source, this, new DerivedSetTest());
+        fill(source, target);
+        assertEquals(target.size(),     tested.size());
+        assertEquals(target.keySet(),   tested.keySet());
+        assertEquals(target.entrySet(), tested.entrySet());
+        assertEquals(target,            tested);
+        assertTrue ("containsKey(400)", tested.containsKey(400));
+        assertFalse("containsKey(4)",   tested.containsKey(4));
+
+        assertEquals("before remove(300)", 8,  source.get        (3  ).intValue());
+        assertEquals("       remove(300)", 80, tested.remove     (300).intValue());
+        assertFalse ("after  remove(300)",     source.containsKey(3  ));
+        assertEquals("       remove(300)", 80, target.remove     (300).intValue()); // For comparison purpose.
+        assertEquals(target, tested);
+
+        assertEquals("before put(900)", 1,  source.get(9      ).intValue());
+        assertEquals("       put(900)", 10, tested.put(900, 30).intValue());
+        assertEquals("after  put(900)", 3,  source.get(9      ).intValue());
+    }
+
+    /**
+     * Tests {@link DerivedMap} with an excluded key.
+     */
+    @Test
+    public void testWithExclusion() {
+        final Map<Integer,Integer> source = new HashMap<>();
+        final Map<Integer,Integer> target = new HashMap<>();
+        final Map<Integer,Integer> tested = DerivedMap.create(source, this, new DerivedSetTest());
+        fill(source, target);
+        assertNull(source.put(EXCLUDED, 4));
+        assertEquals(target.size(),     tested.size());
+        assertEquals(target.keySet(),   tested.keySet());
+        assertEquals(target.entrySet(), tested.entrySet());
+        assertEquals(target,            tested);
+    }
+
+    /**
+     * Returns the converter properties, which is injective and preserve order.
+     */
+    @Override
+    public Set<FunctionProperty> properties() {
+        return EnumSet.of(FunctionProperty.INJECTIVE, FunctionProperty.ORDER_PRESERVING);
+    }
+    @Override public Class<Integer> getSourceClass() {return Integer.class;}
+    @Override public Class<Integer> getTargetClass() {return Integer.class;}
+
+    /**
+     * Multiply the given value by 10, except value {@value #EXCLUDED}.
+     *
+     * @param  value The value to multiply.
+     * @return The multiplied value, or {@code null}.
+     */
+    @Override
+    public Integer convert(final Integer value) {
+        if (value.intValue() == EXCLUDED) {
+            return null;
+        }
+        return value * 100;
+    }
+
+    /**
+     * Returns the inverse of this object converter.
+     */
+    @Override
+    public ObjectConverter<Integer,Integer> inverse() {
+        return new ObjectConverter<Integer,Integer>() {
+            @Override public ObjectConverter<Integer,Integer> inverse() {return DerivedMapTest.this;}
+            @Override public Class<Integer> getSourceClass()            {return Integer.class;}
+            @Override public Class<Integer> getTargetClass()            {return Integer.class;}
+            @Override public Integer        convert(Integer value)      {return value / 100;}
+            @Override public Set<FunctionProperty> properties() {
+                return EnumSet.of(FunctionProperty.SURJECTIVE, FunctionProperty.ORDER_PRESERVING);
+            }
+        };
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedMapTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java?rev=1404561&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java Thu Nov  1 12:32:19 2012
@@ -0,0 +1,126 @@
+/*
+ * 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.util.collection;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.EnumSet;
+import java.util.Arrays;
+import org.apache.sis.math.FunctionProperty;
+import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link DerivedSet}. For the purpose of this test, this class implements an
+ * {@link ObjectConverter} for which input values are multiplied by 10, except value
+ * {@value #EXCLUDED} which is converted to {@code null} (meaning: excluded from the
+ * converted set).
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+public final strictfp class DerivedSetTest extends TestCase implements ObjectConverter<Integer,Integer> {
+    /**
+     * The value to replace by {@code null}.
+     */
+    private static final int EXCLUDED = 19;
+
+    /**
+     * Tests {@link DerivedSet} without excluded value.
+     */
+    @Test
+    public void testNoExclusion() {
+        final Set<Integer> source = new HashSet<>(Arrays.asList(2,  7,  12,  17,  20 ));
+        final Set<Integer> target = new HashSet<>(Arrays.asList(20, 70, 120, 170, 200));
+        final Set<Integer> tested = DerivedSet.create(source, this);
+        assertEquals(target.size(), tested.size());
+        assertEquals(target, tested);
+
+        assertFalse("contains(2)",       tested.contains(2 )); // Original value
+        assertTrue ("contains(20)",      tested.contains(20)); // Derived value
+        assertTrue ("before remove(70)", source.contains(7 ));
+        assertTrue (       "remove(70)", tested.remove  (70));
+        assertFalse( "after remove(70)", source.contains(7 ));
+        assertTrue (       "remove(70)", target.remove(70)); // For comparison purpose.
+        assertEquals(target, tested);
+
+        assertFalse("before add(30)", source.contains(3 ));
+        assertTrue (       "add(30)", tested.add     (30));
+        assertTrue ( "after add(30)", source.contains(3 ));
+        assertTrue (       "add(30)", target.add     (30)); // For comparison purpose.
+        assertEquals(target, tested);
+    }
+
+    /**
+     * Tests {@link DerivedSet} with an excluded value.
+     */
+    @Test
+    public void testWithExclusion() {
+        final Set<Integer> source = new HashSet<>(Arrays.asList(2,  7,  12,  EXCLUDED, 20));
+        final Set<Integer> target = new HashSet<>(Arrays.asList(20, 70, 120, 200));
+        final Set<Integer> tested = DerivedSet.create(source, this);
+        assertEquals(target.size(), tested.size());
+        assertEquals(target, tested);
+        assertFalse(tested.contains(EXCLUDED * 10));
+    }
+
+    /**
+     * Returns the converter properties, which is injective and preserve order.
+     */
+    @Override
+    public Set<FunctionProperty> properties() {
+        return EnumSet.of(FunctionProperty.INJECTIVE, FunctionProperty.ORDER_PRESERVING);
+    }
+    @Override public Class<Integer> getSourceClass() {return Integer.class;}
+    @Override public Class<Integer> getTargetClass() {return Integer.class;}
+
+    /**
+     * Multiply the given value by 10, except value {@value #EXCLUDED}.
+     *
+     * @param  value The value to multiply.
+     * @return The multiplied value, or {@code null}.
+     */
+    @Override
+    public Integer convert(final Integer value) {
+        if (value.intValue() == EXCLUDED) {
+            return null;
+        }
+        return value * 10;
+    }
+
+    /**
+     * Returns the inverse of this object converter.
+     */
+    @Override
+    public ObjectConverter<Integer,Integer> inverse() {
+        return new ObjectConverter<Integer,Integer>() {
+            @Override public ObjectConverter<Integer,Integer> inverse() {return DerivedSetTest.this;}
+            @Override public Class<Integer> getSourceClass()            {return Integer.class;}
+            @Override public Class<Integer> getTargetClass()            {return Integer.class;}
+            @Override public Integer        convert(Integer value)      {return value / 10;}
+            @Override public Set<FunctionProperty> properties() {
+                return EnumSet.of(FunctionProperty.SURJECTIVE, FunctionProperty.ORDER_PRESERVING);
+            }
+        };
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/collection/DerivedSetTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message