sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1640079 - in /sis/branches/JDK8/core/sis-feature/src: main/java/org/apache/sis/feature/ test/java/org/apache/sis/feature/
Date Mon, 17 Nov 2014 07:27:53 GMT
Author: desruisseaux
Date: Mon Nov 17 07:27:52 2014
New Revision: 1640079

URL: http://svn.apache.org/r1640079
Log:
Attribute.equals/hashCode should take characteristics in account.
Added more tests.

Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -34,6 +34,7 @@ import org.opengis.feature.FeatureAssoci
 
 /**
  * An instance of an {@linkplain DefaultAssociationRole feature association role} containing
the associated feature.
+ * {@code AbstractAssociation} can be instantiated by calls to {@link DefaultAssociationRole#newInstance()}.
  *
  * {@section Limitations}
  * <ul>
@@ -49,7 +50,7 @@ import org.opengis.feature.FeatureAssoci
  * @version 0.5
  * @module
  *
- * @see DefaultAssociationRole
+ * @see DefaultAssociationRole#newInstance()
  */
 public abstract class AbstractAssociation extends Field<Feature> implements FeatureAssociation,
Cloneable, Serializable {
     /**
@@ -78,6 +79,8 @@ public abstract class AbstractAssociatio
      *
      * @param  role Information about the association.
      * @return The new association.
+     *
+     * @see DefaultAssociationRole#newInstance()
      */
     public static AbstractAssociation create(final FeatureAssociationRole role) {
         ArgumentChecks.ensureNonNull("role", role);
@@ -231,7 +234,7 @@ public abstract class AbstractAssociatio
             @Override public Object next() {
                 return it.next().getPropertyValue(pt);
             }
-        });
+        }).toString();
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -17,9 +17,14 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.InvalidObjectException;
+import java.io.IOException;
 import org.opengis.util.GenericName;
 import org.opengis.metadata.quality.DataQuality;
 import org.opengis.metadata.maintenance.ScopeCode;
@@ -46,6 +51,8 @@ import org.opengis.feature.AttributeType
  *       Characteristics are often, but not necessarily, constant for all attributes of the
same type in a dataset.</li>
  * </ul>
  *
+ * {@code AbstractAttribute} can be instantiated by calls to {@link DefaultAttributeType#newInstance()}.
+ *
  * {@section Limitations}
  * <ul>
  *   <li><b>Multi-threading:</b> {@code AbstractAttribute} instances are
<strong>not</strong> thread-safe.
@@ -63,7 +70,7 @@ import org.opengis.feature.AttributeType
  * @version 0.5
  * @module
  *
- * @see DefaultAttributeType
+ * @see DefaultAttributeType#newInstance()
  */
 public abstract class AbstractAttribute<V> extends Field<V> implements Attribute<V>,
Cloneable, Serializable {
     /**
@@ -79,6 +86,14 @@ public abstract class AbstractAttribute<
     /**
      * Other attributes that describes this attribute, or {@code null} if not yet created.
      *
+     * <div class="note"><b>Design note:</b>
+     * We could question if it is a good idea to put this field here, given that this field
add a slight cost
+     * to all attribute implementations while only a small fraction of them will want attribute
characteristics.
+     * Since attributes may exist in a very large amount, that question may be significant.
+     * However {@link AbstractFeature} tries hard to not create {@code Attribute} instances
at all (it tries to
+     * store only their value instead), so we presume that peoples who ask for {@code Attribute}
instances are
+     * willing to accept their cost.</div>
+     *
      * @see #characteristics()
      */
     private transient Map<String,Attribute<?>> characteristics;
@@ -101,6 +116,8 @@ public abstract class AbstractAttribute<
      * @param  <V>  The type of attribute values.
      * @param  type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
      * @return The new attribute.
+     *
+     * @see DefaultAttributeType#newInstance()
      */
     public static <V> AbstractAttribute<V> create(final AttributeType<V>
type) {
         ArgumentChecks.ensureNonNull("type", type);
@@ -126,6 +143,43 @@ public abstract class AbstractAttribute<
     }
 
     /**
+     * Invoked on serialization for saving the {@link #characteristics} field.
+     *
+     * @param  out The output stream where to serialize this attribute.
+     * @throws IOException If an I/O error occurred while writing.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        final Attribute<?>[] characterizedBy;
+        if (characteristics instanceof CharacteristicMap) {
+            characterizedBy = characteristics.values().toArray(new Attribute<?>[characteristics.size()]);
+        } else {
+            characterizedBy = null;
+        }
+        out.writeObject(characterizedBy);
+    }
+
+    /**
+     * Invoked on deserialization for restoring the {@link #characteristics} field.
+     *
+     * @param  in The input stream from which to deserialize an attribute.
+     * @throws IOException If an I/O error occurred while reading or if the stream contains
invalid data.
+     * @throws ClassNotFoundException If the class serialized on the stream is not on the
classpath.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
{
+        in.defaultReadObject();
+        try {
+            final Attribute<?>[] characterizedBy = (Attribute<?>[]) in.readObject();
+            if (characterizedBy != null) {
+                characteristics = newCharacteristicsMap();
+                characteristics.values().addAll(Arrays.asList(characterizedBy));
+            }
+        } catch (RuntimeException e) { // At least ClassCastException, NullPointerException,
IllegalArgumentException and IllegalStateException.
+            throw (IOException) new InvalidObjectException(e.getMessage()).initCause(e);
+        }
+    }
+
+    /**
      * Returns the name of this attribute as defined by its {@linkplain #getType() type}.
      * This convenience method delegates to {@link AttributeType#getName()}.
      *
@@ -277,20 +331,37 @@ public abstract class AbstractAttribute<
      */
     public Map<String,Attribute<?>> characteristics() {
         if (characteristics == null) {
-            if (type instanceof DefaultAttributeType<?>) {
-                Map<String, AttributeType<?>> map = ((DefaultAttributeType<?>)
type).characteristics();
-                if (map.isEmpty()) {
-                    characteristics = Collections.emptyMap();
-                } else {
-                    if (!(map instanceof CharacteristicTypeMap)) {
-                        final Collection<AttributeType<?>> types = map.values();
-                        map = CharacteristicTypeMap.create(type, types.toArray(new AttributeType<?>[types.size()]));
-                    }
-                    characteristics = new CharacteristicMap(this, (CharacteristicTypeMap)
map);
+            characteristics = newCharacteristicsMap();
+        }
+        return characteristics;
+    }
+
+    /**
+     * Creates an initially empty map of characteristics for this attribute.
+     * This method does not store the new map in the {@link #characteristics} field;
+     * it is caller responsibility to do so if desired.
+     */
+    private Map<String,Attribute<?>> newCharacteristicsMap() {
+        if (type instanceof DefaultAttributeType<?>) {
+            Map<String, AttributeType<?>> map = ((DefaultAttributeType<?>)
type).characteristics();
+            if (!map.isEmpty()) {
+                if (!(map instanceof CharacteristicTypeMap)) {
+                    final Collection<AttributeType<?>> types = map.values();
+                    map = CharacteristicTypeMap.create(type, types.toArray(new AttributeType<?>[types.size()]));
                 }
+                return new CharacteristicMap(this, (CharacteristicTypeMap) map);
             }
         }
-        return characteristics;
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Returns the characteristics, or an empty map if the characteristics have not yet been
built.
+     * Contrarily to {@link #characteristics()}, this method does not create the map. This
method
+     * is suitable when then caller only wants to read the map and does not plan to write
anything.
+     */
+    final Map<String,Attribute<?>> characteristicsReadOnly() {
+        return (characteristics != null) ? characteristics : Collections.emptyMap();
     }
 
     /**
@@ -370,13 +441,29 @@ public abstract class AbstractAttribute<
     /**
      * Returns a string representation of this attribute.
      * The returned string is for debugging purpose and may change in any future SIS version.
+     * The current implementation is like below:
+     *
+     * {@preformat text
+     *     Attribute[“temperature” : Float] = {20.3, 17.8, 21.1}
+     *     └─ characteristics: units=°C, accuracy=0.1
+     * }
      *
      * @return A string representation of this attribute for debugging purpose.
      */
     @Debug
     @Override
     public String toString() {
-        return FieldType.toString("Attribute", type, Classes.getShortName(type.getValueClass()),
getValues().iterator());
+        final StringBuilder buffer = FieldType.toString("Attribute", type,
+                Classes.getShortName(type.getValueClass()), getValues().iterator());
+        if (characteristics != null && !characteristics.isEmpty()) {
+            buffer.append(System.lineSeparator());
+            String separator = "└─ characteristics: ";
+            for (final Map.Entry<String,Attribute<?>> entry : characteristics.entrySet())
{
+                buffer.append(separator).append(entry.getKey()).append('=').append(entry.getValue().getValue());
+                separator = ", ";
+            }
+        }
+        return buffer.toString();
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -189,7 +189,7 @@ final class CharacteristicMap extends Ab
      */
     private void verifyAttributeType(final int index, final AttributeType<?> type)
{
         final AttributeType<?> expected = types.characterizedBy[index];
-        if (expected != type) {
+        if (!expected.equals(type)) {
             final GenericName en = expected.getName();
             final GenericName an = type.getName();
             throw new IllegalArgumentException(String.valueOf(en).equals(String.valueOf(an))

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=1640079&r1=1640078&r2=1640079&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 Nov 17 07:27:52 2014
@@ -31,6 +31,7 @@ import static org.apache.sis.util.Argume
 import org.opengis.feature.PropertyType;
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
+import org.opengis.feature.FeatureAssociation;
 import org.opengis.feature.FeatureAssociationRole;
 
 
@@ -386,6 +387,17 @@ public class DefaultAssociationRole exte
     }
 
     /**
+     * Creates a new association instance of this role.
+     *
+     * @return A new association instance.
+     *
+     * @see AbstractAssociation#create(FeatureAssociationRole)
+     */
+    public FeatureAssociation newInstance() {
+        return AbstractAssociation.create(this);
+    }
+
+    /**
      * Returns a hash code value for this association role.
      *
      * @return {@inheritDoc}

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -32,6 +32,7 @@ import static org.apache.sis.util.Argume
 
 // Branch-dependent imports
 import java.util.Objects;
+import org.opengis.feature.Attribute;
 import org.opengis.feature.AttributeType;
 
 
@@ -299,6 +300,17 @@ public class DefaultAttributeType<V> ext
     }
 
     /**
+     * Creates a new attribute instance of this type initialized to the {@linkplain #getDefaultValue()
default value}.
+     *
+     * @return A new attribute instance.
+     *
+     * @see AbstractAttribute#create(AttributeType)
+     */
+    public Attribute<V> newInstance() {
+        return AbstractAttribute.create(this);
+    }
+
+    /**
      * Returns a hash code value for this attribute type.
      *
      * @return {@inheritDoc}

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -25,7 +25,7 @@ import org.opengis.feature.AttributeType
 
 
 /**
- * Static methods working on features.
+ * Static methods working on features or attributes.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -160,7 +160,7 @@ abstract class FieldType extends Abstrac
      * @param valueType The name of value class (attribute), or the feature type name (association).
      * @param values    The actual values.
      */
-    static String toString(final String className, final PropertyType type, final Object
valueType, final Iterator<?> values) {
+    static StringBuilder toString(final String className, final PropertyType type, final
Object valueType, final Iterator<?> values) {
         final StringBuilder buffer = toString(className, type, valueType);
         if (values.hasNext()) {
             final Object value = values.next();
@@ -178,6 +178,6 @@ abstract class FieldType extends Abstrac
                 buffer.append('}');
             }
         }
-        return buffer.toString();
+        return buffer;
     }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -171,7 +171,7 @@ final class MultiValuedAttribute<V> exte
      */
     @Override
     public int hashCode() {
-        return type.hashCode() + values.hashCode();
+        return type.hashCode() + values.hashCode() + characteristicsReadOnly().hashCode();
     }
 
     /**
@@ -186,7 +186,8 @@ final class MultiValuedAttribute<V> exte
         }
         if (obj instanceof MultiValuedAttribute<?>) {
             final MultiValuedAttribute<?> that = (MultiValuedAttribute<?>) obj;
-            return type.equals(that.type) && values.equals(that.values);
+            return type.equals(that.type) && values.equals(that.values) &&
+                   characteristicsReadOnly().equals(that.characteristicsReadOnly());
         }
         return false;
     }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -106,7 +106,7 @@ final class SingletonAttribute<V> extend
      */
     @Override
     public int hashCode() {
-        return type.hashCode() + Objects.hashCode(value);
+        return type.hashCode() + Objects.hashCode(value) + characteristicsReadOnly().hashCode();
     }
 
     /**
@@ -121,7 +121,8 @@ final class SingletonAttribute<V> extend
         }
         if (obj instanceof SingletonAttribute<?>) {
             final SingletonAttribute<?> that = (SingletonAttribute<?>) obj;
-            return type.equals(that.type) && Objects.equals(value, that.value);
+            return type.equals(that.type) && Objects.equals(value, that.value) &&
+                   characteristicsReadOnly().equals(that.characteristicsReadOnly());
         }
         return false;
     }

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java?rev=1640079&r1=1640078&r2=1640079&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/CharacteristicMapTest.java
[UTF-8] Mon Nov 17 07:27:52 2014
@@ -17,12 +17,14 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
+import java.util.Collection;
 import java.util.AbstractMap.SimpleEntry;
+import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 
 // Branch-dependent imports
 import org.opengis.feature.Attribute;
@@ -141,6 +143,138 @@ public final strictfp class Characterist
             assertTrue(message, message.contains("accuracy"));
             assertTrue(message, message.contains("temperature:units"));
         }
+        assertEntriesEqual(units, accuracy, characteristics);
+        assertSame(characteristics, temperature.characteristics());
+    }
+
+    /**
+     * Tests adding a characteristic indirectly with {@link CharacteristicMap#addValue(Attribute)}.
+     */
+    @Test
+    @DependsOnMethod("testPut")
+    public void testAddValue() {
+        final AbstractAttribute<?>     temperature     = temperature();
+        final AbstractAttribute<?>     units           = create(temperature, "units");
+        final AbstractAttribute<?>     accuracy        = create(temperature, "accuracy");
+        final Map<String,Attribute<?>> characteristics = temperature.characteristics();
+        final Collection<Attribute<?>> values          = characteristics.values();
+        /*
+         * Verify that the collection is initially empty.
+         */
+        assertTrue  ("isEmpty",  values.isEmpty());
+        assertEquals("size", 0,  values.size());
+        assertFalse ("contains", values.contains(units));
+        assertFalse ("contains", values.contains(accuracy));
+        assertFalse ("contains", values.contains(temperature));
+        /*
+         * Store "units" characteristic and verify.
+         */
+        assertTrue  ("add",      values.add(units));
+        assertFalse ("isEmpty",  values.isEmpty());
+        assertEquals("size", 1,  values.size());
+        assertTrue  ("contains", values.contains(units));
+        assertFalse ("contains", values.contains(accuracy));
+        assertFalse ("contains", values.contains(temperature));
+        /*
+         * Store "accuracy" characteristic and verify.
+         */
+        assertTrue  ("add",      values.add(accuracy));
+        assertFalse ("isEmpty",  values.isEmpty());
+        assertEquals("size", 2,  values.size());
+        assertTrue  ("contains", values.contains(units));
+        assertTrue  ("contains", values.contains(accuracy));
+        assertFalse ("contains", values.contains(temperature));
+        /*
+         * Overwrite values. Map shall stay unchanged.
+         */
+        assertFalse ("add",     values.add(accuracy));
+        assertFalse ("add",     values.add(units));
+        assertEquals("size", 2, values.size());
+        /*
+         * Try adding an attribute of the wrong type.
+         * Map shall stay unchanged.
+         */
+        try {
+            values.add(temperature);
+            fail("Operation shall not have been allowed.");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("temperature"));
+        }
+        assertEntriesEqual(units, accuracy, characteristics);
+        assertSame(characteristics, temperature.characteristics());
+    }
+
+    /**
+     * Tests adding a characteristic indirectly with {@link CharacteristicMap#addKey(String)}.
+     */
+    @Test
+    @DependsOnMethod("testPut")
+    public void testAddKey() {
+        final Attribute<?> units, accuracy;
+        final AbstractAttribute<?>     temperature     = temperature();
+        final Map<String,Attribute<?>> characteristics = temperature.characteristics();
+        final Collection<String>       keys            = characteristics.keySet();
+        /*
+         * Verify that the collection is initially empty.
+         */
+        assertTrue  ("isEmpty",  keys.isEmpty());
+        assertEquals("size", 0,  keys.size());
+        assertFalse ("contains", keys.contains("units"));
+        assertFalse ("contains", keys.contains("accuracy"));
+        assertFalse ("contains", keys.contains("temperature"));
+        /*
+         * Store "units" characteristic and verify.
+         */
+        assertTrue   ("add",      keys.add("units"));
+        assertFalse  ("isEmpty",  keys.isEmpty());
+        assertEquals ("size", 1,  keys.size());
+        assertTrue   ("contains", keys.contains("units"));
+        assertFalse  ("contains", keys.contains("accuracy"));
+        assertFalse  ("contains", keys.contains("temperature"));
+        assertNotNull("get",      units = characteristics.get("units"));
+        /*
+         * Store "accuracy" characteristic and verify.
+         */
+        assertTrue  ("add",       keys.add("accuracy"));
+        assertFalse ("isEmpty",   keys.isEmpty());
+        assertEquals("size", 2,   keys.size());
+        assertTrue  ("contains",  keys.contains("units"));
+        assertTrue  ("contains",  keys.contains("accuracy"));
+        assertFalse ("contains",  keys.contains("temperature"));
+        assertNotNull("get",      accuracy = characteristics.get("accuracy"));
+        /*
+         * Overwrite values. Map shall stay unchanged.
+         */
+        assertFalse ("add",     keys.add("accuracy"));
+        assertFalse ("add",     keys.add("units"));
+        assertEquals("size", 2, keys.size());
+        /*
+         * Try adding an attribute of the wrong type.
+         * Map shall stay unchanged.
+         */
+        try {
+            keys.add("dummy");
+            fail("Operation shall not have been allowed.");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("temperature"));
+            assertTrue(message, message.contains("dummy"));
+        }
+        assertEntriesEqual(units, accuracy, characteristics);
+        assertSame(characteristics, temperature.characteristics());
+    }
+
+    /**
+     * Verifies that the given characteristics map contains entries for the given attributes.
+     *
+     * @param units           The first expected value in iteration order.
+     * @param accuracy        The second expected value in iteration order.
+     * @param characteristics The map to verify.
+     */
+    private static void assertEntriesEqual(final Attribute<?> units, final Attribute<?>
accuracy,
+            final Map<String,Attribute<?>> characteristics)
+    {
         assertArrayEquals("keySet", new String[] {"accuracy", "units"}, characteristics.keySet().toArray());
         assertArrayEquals("values", new Object[] { accuracy ,  units }, characteristics.values().toArray());
         assertArrayEquals("entrySet", new Object[] {
@@ -148,4 +282,22 @@ public final strictfp class Characterist
                 new SimpleEntry<>("units",    units)
             }, characteristics.entrySet().toArray());
     }
+
+    /**
+     * Tests the reconstruction of {@link CharacteristicTypeMap} after serialization.
+     */
+    @Test
+    @DependsOnMethod("testAddValue") // Implementation of readObject use values().addAll(...).
+    public void testSerialization() {
+        final AbstractAttribute<Float> temperature = temperature();
+        assertTrue(temperature.characteristics().keySet().add("accuracy"));
+        final Attribute<Float> accuracy = Features.cast(temperature.characteristics().get("accuracy"),
Float.class);
+        accuracy.setValue(0.2f);
+
+        final AbstractAttribute<Float> unserialized = assertSerializedEquals(temperature);
+        assertNotSame(temperature, unserialized);
+        assertNotSame(temperature.characteristics(), unserialized.characteristics());
+        assertSame(((DefaultAttributeType<?>) temperature .getType()).characteristics(),
+                   ((DefaultAttributeType<?>) unserialized.getType()).characteristics());
+    }
 }



Mime
View raw message