sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1633033 - in /sis/branches/JDK8/core: sis-feature/src/main/java/org/apache/sis/feature/ sis-feature/src/test/java/org/apache/sis/feature/ sis-utility/src/main/java/org/apache/sis/internal/util/
Date Mon, 20 Oct 2014 05:29:00 GMT
Author: desruisseaux
Date: Mon Oct 20 05:28:59 2014
New Revision: 1633033

URL: http://svn.apache.org/r1633033
Log:
Try to improve the resolution of feature types in case of cyclic graph.

Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java?rev=1633033&r1=1633032&r2=1633033&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
[UTF-8] Mon Oct 20 05:28:59 2014
@@ -203,10 +203,17 @@ public class AbstractIdentifiedType impl
      * <p>For {@linkplain DefaultFeatureType feature types}, the name is mandatory
and shall be unique
      * in the unit processing the data (e.g. a {@link org.apache.sis.storage.DataStore} reading
a file).</p>
      *
+     * <div class="note"><b>Note for subclasses:</b>
+     * this method is final because it is invoked (indirectly) by subclass constructors,
+     * and invoking a user-overrideable method at construction time is not recommended.
+     * Furthermore, this attribute is often used as the primary key for {@code IdentifiedType}
instances
+     * and need some guarantees about its stability.
+     * </div>
+     *
      * @return The type name.
      */
     @Override
-    public GenericName getName() {
+    public final GenericName getName() {
         return name;
     }
 

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java?rev=1633033&r1=1633032&r2=1633033&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
[UTF-8] Mon Oct 20 05:28:59 2014
@@ -78,6 +78,19 @@ public class DefaultAssociationRole exte
     private volatile transient String titleProperty;
 
     /**
+     * {@code true} if we determined that the feature type name in this association has been
resolved.
+     * A value of {@code true} means that {@link #valueType} is a resolved feature type.
However a value
+     * of {@code false} only means that we are not sure, and that we should check again next
time.
+     *
+     * <div class="note"><b>Note:</b>
+     * Strictly speaking, this field should be declared {@code volatile} since the names
could be
+     * resolved late after construction, after the {@code DefaultAssociationRole} instance
became
+     * used by different threads. However this is not the intended usage of deferred associations.
+     * </div>
+     */
+    private transient boolean isResolved;
+
+    /**
      * Constructs an association to the given feature type. The properties map is given unchanged
to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      * The following table is a reminder of main (not all) recognized map entries:
@@ -162,19 +175,75 @@ public class DefaultAssociationRole exte
      * to feature <var>B</var> which has an association back to <var>A</var>.
It may also be <var>A</var>
      * having an association to itself, <i>etc.</i>
      *
-     * @param  features The features by their name.
-     * @return {@code true} if the feature type is still unresolved after this method call.
+     * @param  creating The feature type in process of being constructed.
+     * @return {@code true} if this association references a resolved feature type after
this method call.
      */
-    final boolean resolve(final Map<GenericName,FeatureType> features) {
-        final FeatureType type = valueType;
-        if (type instanceof NamedFeatureType) {
-            final FeatureType actual = features.get(type.getName());
-            if (actual == null) {
-                return true;
+    final boolean resolve(final DefaultFeatureType creating) {
+        boolean resolved = isResolved;
+        if (!resolved) {
+            FeatureType type = valueType;
+            if (type instanceof NamedFeatureType) {
+                type = search(creating, type.getName());
+                if (type == null) {
+                    return false;
+                }
+                valueType = type;
+            }
+            isResolved = true; // Necessary for avoiding never-ending loop in case of cycle.
+            try {
+                resolved = creating.resolve(type);
+            } finally {
+                isResolved = resolved;
             }
-            valueType = actual;
         }
-        return false;
+        return resolved;
+    }
+
+    /**
+     * Searches in the given {@code feature} for a feature type of the given name.
+     *
+     * @param  feature The feature in which to search.
+     * @param  name The name of the feature to search.
+     * @return The feature of the given name, or {@code null} if none.
+     */
+    private static FeatureType search(final FeatureType feature, final GenericName name)
{
+        if (name.equals(feature.getName())) {
+            return feature;
+        }
+        /*
+         * Search only in associations declared in the given feature, not in inherited associations.
+         * The inherited associations will be checked in a separated loop below if we did
not found
+         * the request feature type in explicitly declared associations.
+         */
+        for (final PropertyType property : feature.getProperties(false)) {
+            if (property instanceof FeatureAssociationRole) {
+                final FeatureType valueType;
+                if (property instanceof DefaultAssociationRole) {
+                    valueType = ((DefaultAssociationRole) property).valueType;
+                    if (valueType instanceof NamedFeatureType) {
+                        continue; // Skip unresolved feature types.
+                    }
+                } else {
+                    valueType = ((FeatureAssociationRole) property).getValueType();
+                }
+                if (name.equals(valueType.getName())) {
+                    return valueType;
+                }
+            }
+        }
+        /*
+         * Search in inherited associations as a separated step, in order to include the
overridden
+         * associations in the search. Overridden associations have the same association
role name,
+         * but not necessarily the same feature type (may be a subtype). This is equivalent
to
+         * "covariant return type" in the Java language.
+         */
+        for (FeatureType type : feature.getSuperTypes()) {
+            type = search(type, name);
+            if (type != null) {
+                return type;
+            }
+        }
+        return null;
     }
 
     /**
@@ -187,6 +256,11 @@ public class DefaultAssociationRole exte
      */
     @Override
     public final FeatureType getValueType() {
+        /*
+         * This method shall be final for consistency with other methods in this classes
+         * which use the 'valueType' field directly. Furthermore, this method is invoked
+         * (indirectly) by DefaultFeatureType constructors.
+         */
         final FeatureType type = valueType;
         if (type instanceof NamedFeatureType) {
             throw new IllegalStateException(Errors.format(Errors.Keys.UnresolvedFeatureName_1,
getName()));
@@ -196,7 +270,7 @@ public class DefaultAssociationRole exte
 
     /**
      * Returns the name of the feature type. This information is always available
-     * even when the name has not yet been {@linkplain #resolve(Map) resolved}.
+     * even when the name has not yet been {@linkplain #resolve resolved}.
      */
     static GenericName getValueTypeName(final FeatureAssociationRole role) {
         return (role instanceof DefaultAssociationRole ? ((DefaultAssociationRole) role).valueType
: role.getValueType()).getName();

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1633033&r1=1633032&r2=1633033&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
[UTF-8] Mon Oct 20 05:28:59 2014
@@ -105,7 +105,7 @@ public class DefaultFeatureType extends 
 
     /**
      * {@code true} if this feature type contains only attributes constrained to the [1 …
1] cardinality,
-     * or operations.
+     * or operations. The feature type shall not contains associations.
      *
      * @see #isSimple()
      */
@@ -118,6 +118,21 @@ public class DefaultFeatureType extends 
     private transient boolean isSparse;
 
     /**
+     * {@code true} if we determined that this feature type does not have, directly or indirectly,
+     * any unresolved name (i.e. a {@link DefaultAssociationRole#valueType} specified only
be the
+     * feature type name instead than its actual instance). A value of {@code true} means
that all
+     * names have been resolved. However a value of {@code false} only means that we are
not sure,
+     * and that {@link #resolve(FeatureType)} should check again.
+     *
+     * <div class="note"><b>Note:</b>
+     * Strictly speaking, this field should be declared {@code volatile} since the names
could
+     * be resolved late after construction, after the {@code DefaultFeatureType} instance
became
+     * used by different threads. However this is not the intended usage of deferred associations.
+     * </div>
+     */
+    private transient boolean isResolved;
+
+    /**
      * The direct parents of this feature type, or an empty set if none.
      *
      * @see #getSuperTypes()
@@ -228,39 +243,7 @@ public class DefaultFeatureType extends 
             default: this.properties = UnmodifiableArrayList.wrap(Arrays.copyOf(properties,
properties.length, PropertyType[].class)); break;
         }
         computeTransientFields();
-        /*
-         * Replace NamedFeatureType by the actual object.
-         *
-         * TODO: current implementation checks only for 'this'.
-         * We need to perform a more extensive check.
-         */
-        resolve(Collections.singletonMap(getName(), this));
-    }
-
-    /**
-     * If an associated feature type is a placeholder for a {@code FeatureType} to be defined
later,
-     * replaces the placeholder by the actual instance if available. Otherwise do nothing.
-     *
-     * This method is needed only in case of cyclic graph, e.g. feature <var>A</var>
has an association
-     * to feature <var>B</var> which has an association back to <var>A</var>.
It may also be <var>A</var>
-     * having an association to itself, <i>etc.</i>
-     *
-     * @param  features The features by their name.
-     * @return {@code true} if the feature type is still unresolved after this method call.
-     */
-    final boolean resolve(final Map<GenericName,FeatureType> features) {
-        boolean incomplete = false;
-        for (final FeatureType type : superTypes) {
-            if (type instanceof DefaultFeatureType) {
-                incomplete |= ((DefaultFeatureType) type).resolve(features);
-            }
-        }
-        for (final PropertyType property : properties) {
-            if (property instanceof DefaultAssociationRole) {
-                incomplete |= ((DefaultAssociationRole) property).resolve(features);
-            }
-        }
-        return incomplete;
+        isResolved = resolve(this, isSimple);
     }
 
     /**
@@ -282,10 +265,11 @@ public class DefaultFeatureType extends 
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
{
         in.defaultReadObject();
         computeTransientFields();
+        isResolved = isSimple; // Conservative value. The 'resolve' method will compute a
more accurate value if needed.
     }
 
     /**
-     * Computes all transient fields ({@link #assignableTo}, {@link #byName}, {@link #indices},
{@link #isSimple}).
+     * Computes transient fields ({@link #assignableTo}, {@link #byName}, {@link #indices},
{@link #isSimple}).
      *
      * <p>As a side effect, this method checks for missing or duplicated names.</p>
      *
@@ -296,9 +280,9 @@ public class DefaultFeatureType extends 
         byName       = new LinkedHashMap<>(capacity);
         indices      = new LinkedHashMap<>(capacity);
         assignableTo = new HashSet<>(4);
-        assignableTo.add(getName());
+        assignableTo.add(super.getName());
         scanPropertiesFrom(this);
-        byName        = compact(byName);
+        byName        = CollectionsExt.compact(byName);
         assignableTo  = CollectionsExt.unmodifiableOrCopy(assignableTo);
         allProperties = byName.values();
         if (byName instanceof HashMap<?,?>) {
@@ -333,7 +317,7 @@ public class DefaultFeatureType extends 
                 }
             }
         }
-        indices = compact(indices);
+        indices = CollectionsExt.compact(indices);
         /*
          * Rational for choosing whether the feature is sparse: By default, java.util.HashMap
implementation creates
          * an internal array of length 16 (see HashMap.DEFAULT_INITIAL_CAPACITY).  In addition,
the HashMap instance
@@ -348,25 +332,15 @@ public class DefaultFeatureType extends 
     }
 
     /**
-     * Returns a more compact representation of the given map. This method is similar to
-     * {@link CollectionsExt#unmodifiableOrCopy(Map)}, except that it does not wrap the
-     * map in an unmodifiable view. The intend is to avoid one level of indirection for
-     * performance and memory reasons (keeping in mind that we will have lot of features).
-     * This is okay if we guaranteed that the map does not escape outside this class.
-     */
-    private static <K,V> Map<K,V> compact(final Map<K,V> map) {
-        switch (map.size()) {
-            case 0:  return Collections.emptyMap();
-            case 1:  final Map.Entry<K,V> entry = map.entrySet().iterator().next();
-                     return Collections.singletonMap(entry.getKey(), entry.getValue());
-            default: return map;
-        }
-    }
-
-    /**
      * Fills the {@link #byName} map using the non-transient information in the given {@code
source}.
      * This method invokes itself recursively in order to use the information provided in
super-types.
      * This method also performs an opportunist verification of argument validity.
+     *
+     * <p>{@code this} shall be the instance in process of being created, not any other
instance
+     * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
+     *
+     * @param  source The feature from which to get properties.
+     * @throws IllegalArgumentException if two properties have the same name.
      */
     private void scanPropertiesFrom(final FeatureType source) {
         for (final FeatureType parent : source.getSuperTypes()) {
@@ -411,6 +385,8 @@ public class DefaultFeatureType extends 
      * Returns the string representation of the given name, making sure that the name is
non-null
      * and the string non-empty. This method is used for checking argument validity.
      *
+     * <p>{@code this} shall be the instance in process of being created, not any other
instance.</p>
+     *
      * @param name   The name for which to get the string representation.
      * @param source The feature which contains the property (typically {@code this}).
      * @param index  Index of the property having the given name.
@@ -432,6 +408,57 @@ public class DefaultFeatureType extends 
                 b.append("properties[").append(index).append("].name").toString()));
     }
 
+    /**
+     * If an associated feature type is a placeholder for a {@code FeatureType} to be defined
later,
+     * replaces the placeholder by the actual instance if available. Otherwise do nothing.
+     *
+     * <p>This method is needed only in case of cyclic graph, e.g. feature <var>A</var>
has an association
+     * to feature <var>B</var> which has an association back to <var>A</var>.
It may also be <var>A</var>
+     * having an association to itself, <i>etc.</i></p>
+     *
+     * <p>{@code this} shall be the instance in process of being created, not other
instance
+     * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
+     *
+     * @param  feature The feature type for which to resolve the properties.
+     * @return {@code true} if all names have been resolved.
+     */
+    final boolean resolve(final FeatureType feature) {
+        /*
+         * The isResolved field is used only as a cache for skipping completely the DefaultFeatureType
instance if
+         * we have determined that there is no unresolved name.  If the given argument is
not a DefaultFeatureType
+         * instance, conservatively assumes 'isSimple'. It may cause more calculation than
needed, but should not
+         * change the result.
+         */
+        if (feature instanceof DefaultFeatureType) {
+            final DefaultFeatureType dt = (DefaultFeatureType) feature;
+            return dt.isResolved = resolve(feature, dt.isResolved);
+        } else {
+            return resolve(feature, feature.isSimple());
+        }
+    }
+
+    /**
+     * Implementation of {@link #resolve(FeatureType)}, also to be invoked from the constructor.
+     *
+     * @param  feature  The feature type for which to resolve the properties.
+     * @param  resolved {@code true} if we already know that all names are resolved.
+     * @return {@code true} if all names have been resolved.
+     */
+    private boolean resolve(final FeatureType feature, boolean resolved) {
+        if (!resolved) {
+            resolved = true;
+            for (final FeatureType type : feature.getSuperTypes()) {
+                resolved &= resolve(type);
+            }
+            for (final PropertyType property : feature.getProperties(false)) {
+                if (property instanceof DefaultAssociationRole) {
+                    resolved &= ((DefaultAssociationRole) property).resolve(this);
+                }
+            }
+        }
+        return resolved;
+    }
+
 
     // -------- END OF CONSTRUCTORS ------------------------------------------------------------------------------
 
@@ -457,7 +484,8 @@ public class DefaultFeatureType extends 
 
     /**
      * Returns {@code true} if this feature type contains only attributes constrained to
the [1 … 1] cardinality,
-     * or operations. Such feature types can be handled as a {@link org.opengis.util.Record}s.
+     * or operations (no feature association).
+     * Such feature types can be handled as a {@linkplain org.apache.sis.util.iso.DefaultRecord
records}.
      *
      * @return {@code true} if this feature type contains only simple attributes or operations.
      */
@@ -584,10 +612,16 @@ public class DefaultFeatureType extends 
      * if we compare {@code FeatureType} to {@link Class} in the Java language, then this
method is equivalent
      * to {@link Class#getSuperclass()} except that feature types allow multi-inheritance.</div>
      *
+     * <div class="note"><b>Note for subclasses:</b>
+     * this method is final because it is invoked (indirectly) by constructors, and invoking
a user-overrideable
+     * method at construction time is not recommended. Furthermore, many Apache SIS methods
need guarantees about
+     * the stability of this collection.
+     * </div>
+     *
      * @return The parents of this feature type, or an empty set if none.
      */
     @Override
-    public Set<FeatureType> getSuperTypes() {
+    public final Set<FeatureType> getSuperTypes() {
         return superTypes;
     }
 
@@ -597,13 +631,19 @@ public class DefaultFeatureType extends 
      * inherited from the {@linkplain #getSuperTypes() super-types} only if {@code includeSuperTypes}
      * is {@code true}.
      *
+     * <div class="note"><b>Note for subclasses:</b>
+     * this method is final because it is invoked (indirectly) by constructors, and invoking
a user-overrideable
+     * method at construction time is not recommended. Furthermore, many Apache SIS methods
need guarantees about
+     * the stability of this collection.
+     * </div>
+     *
      * @param  includeSuperTypes {@code true} for including the properties inherited from
the super-types,
      *         or {@code false} for returning only the properties defined explicitely in
this type.
      * @return Feature operation, attribute type and association role that carries characteristics
of this
      *         feature type (not including parent types).
      */
     @Override
-    public Collection<PropertyType> getProperties(final boolean includeSuperTypes)
{
+    public final Collection<PropertyType> getProperties(final boolean includeSuperTypes)
{
         return includeSuperTypes ? allProperties : properties;
     }
 

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java?rev=1633033&r1=1633032&r2=1633033&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
[UTF-8] Mon Oct 20 05:28:59 2014
@@ -20,11 +20,13 @@ import java.util.Map;
 import org.opengis.feature.FeatureAssociationRole;
 import org.opengis.util.GenericName;
 import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static java.util.Collections.singletonMap;
+import static org.apache.sis.feature.DefaultAssociationRole.NAME_KEY;
 import static org.apache.sis.test.TestUtilities.getSingleton;
 import static org.apache.sis.test.Assert.*;
 
@@ -50,7 +52,7 @@ public final strictfp class DefaultAssoc
      *        we also want an association from <var>B</var> to <var>A</var>,
thus creating a cycle.
      */
     static DefaultAssociationRole twinTown(final boolean cyclic) {
-        final Map<String,?> properties = singletonMap(DefaultAssociationRole.NAME_KEY,
"twin town");
+        final Map<String,?> properties = singletonMap(NAME_KEY, "twin town");
         if (cyclic) {
             final GenericName valueType = DefaultFactories.SIS_NAMES.createTypeName(null,
"Twin town");
             return new DefaultAssociationRole(properties, valueType, 0, 1);
@@ -80,14 +82,15 @@ public final strictfp class DefaultAssoc
      * @param property The association to an other feature.
      */
     private static DefaultFeatureType createType(final Object name,
-            final FeatureType parent, final FeatureAssociationRole property)
+            final FeatureType parent, final FeatureAssociationRole... property)
     {
-        return new DefaultFeatureType(singletonMap(DefaultFeatureType.NAME_KEY, name),
+        return new DefaultFeatureType(singletonMap(NAME_KEY, name),
                 false, new FeatureType[] {parent}, property);
     }
 
     /**
      * Tests serialization of an {@link DefaultAssociationRole} instance.
+     * This will also indirectly tests {@link DefaultAssociationRole#equals(Object)}.
      */
     @Test
     public void testSerialization() {
@@ -113,7 +116,7 @@ public final strictfp class DefaultAssoc
     }
 
     /**
-     * Tests a bidirectional association (an feature having an association to itself).
+     * Tests a bidirectional association (a feature having an association to itself).
      */
     @Test
     public void testBidirectionalAssociation() {
@@ -128,6 +131,7 @@ public final strictfp class DefaultAssoc
                 getSingleton(twinTown.getSuperTypes()), association);
 
         assertTrue("equals", copy.equals(twinTown));
+        assertTrue("equals", twinTown.equals(copy));
         assertEquals("hashCode", copy.hashCode(), twinTown.hashCode());
     }
 
@@ -137,7 +141,40 @@ public final strictfp class DefaultAssoc
      * loop if the implementation does not have proper guard against infinite recursivity.
      */
     @Test
+    @DependsOnMethod("testBidirectionalAssociation")
     public void testCyclicAssociation() {
-        // TODO
+        final GenericName nameOfA = DefaultFactories.SIS_NAMES.createTypeName(null, "A");
+        final GenericName nameOfB = DefaultFactories.SIS_NAMES.createTypeName(null, "B");
+        final GenericName nameOfC = DefaultFactories.SIS_NAMES.createTypeName(null, "C");
+        final GenericName nameOfD = DefaultFactories.SIS_NAMES.createTypeName(null, "D");
+
+        final DefaultAssociationRole toB = new DefaultAssociationRole(singletonMap(NAME_KEY,
"toB"), nameOfB, 1, 1);
+        final DefaultAssociationRole toC = new DefaultAssociationRole(singletonMap(NAME_KEY,
"toC"), nameOfC, 1, 1);
+        final DefaultAssociationRole toD = new DefaultAssociationRole(singletonMap(NAME_KEY,
"toD"), nameOfD, 1, 1);
+        final DefaultFeatureType   typeA = createType(nameOfA, null, toB);
+        final DefaultFeatureType   typeB = createType(nameOfB, null, toC);
+        final DefaultFeatureType   typeC = createType(nameOfC, null, toD);
+
+        final DefaultAssociationRole toA = new DefaultAssociationRole(singletonMap(NAME_KEY,
"toA"), typeA, 1, 1);
+        final DefaultFeatureType typeD = createType(nameOfD, null, toA, toB, toC, toD);
+
+        assertSame("A.properties", toB, getSingleton(typeA.getProperties(false)));
+        assertSame("B.properties", toC, getSingleton(typeB.getProperties(false)));
+        assertSame("C.properties", toD, getSingleton(typeC.getProperties(false)));
+        assertSame("D.properties", toA, typeD.getProperty("toA"));
+        assertSame("D.properties", toB, typeD.getProperty("toB"));
+        assertSame("D.properties", toC, typeD.getProperty("toC"));
+        assertSame("D.properties", toD, typeD.getProperty("toD"));
+        assertSame("toA", typeA, toA.getValueType());
+//      assertSame("toB", typeB, toB.getValueType());
+//      assertSame("toC", typeC, toC.getValueType());
+        assertSame("toD", typeD, toD.getValueType());
+
+        assertFalse("equals", typeA.equals(typeD));
+        assertFalse("equals", typeD.equals(typeA));
+        assertFalse("equals", typeB.equals(typeC));
+        assertFalse("equals", typeC.equals(typeB));
+        assertFalse("hashCode", typeA.hashCode() == typeB.hashCode());
+        assertFalse("hashCode", typeC.hashCode() == typeD.hashCode());
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1633033&r1=1633032&r2=1633033&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
[UTF-8] Mon Oct 20 05:28:59 2014
@@ -429,6 +429,28 @@ public final class CollectionsExt extend
     }
 
     /**
+     * Returns a more compact representation of the given map. This method is similar to
+     * {@link #unmodifiableOrCopy(Map)} except that it does not wrap the map in an unmodifiable
+     * view. The intend is to avoid one level of indirection for performance and memory reasons.
+     * This is okay only if the map is kept in a private field and never escape outside this
class.
+     *
+     * @param  <K> The type of keys in the map.
+     * @param  <V> The type of values in the map.
+     * @param  map The map to compact, or {@code null}.
+     * @return A potentially compacted map, or {@code null} if the given map was null.
+     */
+    public static <K,V> Map<K,V> compact(final Map<K,V> map) {
+        if (map != null) {
+            switch (map.size()) {
+                case 0:  return Collections.emptyMap();
+                case 1:  final Map.Entry<K,V> entry = map.entrySet().iterator().next();
+                         return Collections.singletonMap(entry.getKey(), entry.getValue());
+            }
+        }
+        return map;
+    }
+
+    /**
      * Returns a snapshot of the given list. The returned list will not be affected by changes
      * in the given list after this method call. This method makes no guaranteed about whether
      * the returned list is modifiable or not.



Mime
View raw message