You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2012/11/01 13:32:20 UTC
svn commit: r1404561 - in /sis/branches/JDK7/sis-utility/src:
main/java/org/apache/sis/util/collection/
test/java/org/apache/sis/util/collection/
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