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 2014/11/17 08:27:53 UTC
svn commit: r1640079 - in /sis/branches/JDK8/core/sis-feature/src:
main/java/org/apache/sis/feature/ test/java/org/apache/sis/feature/
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());
+ }
}