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/05/21 16:17:16 UTC
svn commit: r1596580 - in /sis/branches/JDK8/core/sis-feature/src:
main/java/org/apache/sis/feature/ test/java/org/apache/sis/feature/
Author: desruisseaux
Date: Wed May 21 14:17:16 2014
New Revision: 1596580
URL: http://svn.apache.org/r1596580
Log:
Added tests.
Modified:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java [UTF-8] Wed May 21 14:17:16 2014
@@ -62,7 +62,7 @@ import org.apache.sis.util.CorruptedObje
*
* @see DefaultFeatureType#newInstance()
*/
-public abstract class AbstractFeature implements Serializable {
+public abstract class AbstractFeature implements Cloneable, Serializable {
/**
* For cross-version compatibility.
*/
@@ -376,6 +376,22 @@ public abstract class AbstractFeature im
}
/**
+ * Returns a copy of this feature
+ * This method clones also all {@linkplain Cloneable cloneable} property instances in this feature,
+ * but not necessarily property values. Whether the property values are cloned or not (i.e. whether
+ * the clone operation is <cite>deep</cite> or <cite>shallow</cite>) depends on the behavior or
+ * property {@code clone()} methods (see for example {@link DefaultAttribute#clone()}).
+ *
+ * @return A clone of this attribute.
+ * @throws CloneNotSupportedException if this feature can not be cloned, typically because
+ * {@code clone()} on a property instance failed.
+ */
+ @Override
+ public AbstractFeature clone() throws CloneNotSupportedException {
+ return (AbstractFeature) super.clone();
+ }
+
+ /**
* Returns a hash code value for this feature.
*
* @return A hash code value.
Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java [UTF-8] Wed May 21 14:17:16 2014
@@ -176,20 +176,18 @@ public class DefaultAssociation extends
}
/**
- * Returns a shallow copy of this association.
- * The association {@linkplain #getValue() value} is <strong>not</strong> cloned.
+ * Returns a copy of this association.
+ * The default implementation returns a <em>shallow</em> copy:
+ * the association {@linkplain #getValue() value} is <strong>not</strong> cloned.
+ * However subclasses may choose to do otherwise.
*
* @return A clone of this association.
+ * @throws CloneNotSupportedException if this association can not be cloned.
+ * The default implementation never throw this exception. However subclasses may throw it.
*/
@Override
- public DefaultAssociation clone() {
- final DefaultAssociation clone;
- try {
- clone = (DefaultAssociation) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new AssertionError(e); // Should never happen since we are cloneable.
- }
- return clone;
+ public DefaultAssociation clone() throws CloneNotSupportedException {
+ return (DefaultAssociation) super.clone();
}
/**
Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java [UTF-8] Wed May 21 14:17:16 2014
@@ -219,21 +219,20 @@ public class DefaultAttribute<T> extends
}
/**
- * Returns a shallow copy of this attribute.
- * The attribute {@linkplain #getValue() value} is <strong>not</strong> cloned.
+ * Returns a copy of this attribute.
+ * The default implementation returns a <em>shallow</em> copy:
+ * the attribute {@linkplain #getValue() value} is <strong>not</strong> cloned.
+ * However subclasses may choose to do otherwise.
*
* @return A clone of this attribute.
+ * @throws CloneNotSupportedException if this attribute can not be cloned.
+ * The default implementation never throw this exception. However subclasses may throw it,
+ * for example on attempt to clone the attribute value.
*/
@Override
@SuppressWarnings("unchecked")
- public DefaultAttribute<T> clone() {
- final DefaultAttribute<T> clone;
- try {
- clone = (DefaultAttribute<T>) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new AssertionError(e); // Should never happen since we are cloneable.
- }
- return clone;
+ public DefaultAttribute<T> clone() throws CloneNotSupportedException {
+ return (DefaultAttribute<T>) super.clone();
}
/**
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=1596580&r1=1596579&r2=1596580&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] Wed May 21 14:17:16 2014
@@ -286,8 +286,17 @@ public class DefaultFeatureType extends
}
}
indices = 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
+ * itself consumes approximatively 8 "words" in memory. Consequently there is no advantage in using HashMap
+ * unless the number of properties is greater than 16 + 8 (note: we could specify a smaller initial capacity,
+ * but the memory consumed by each internal Map.Entry quickly exceed the few saved words). Next, the default
+ * HashMap threshold is 0.75, so there is again no advantage in using HashMap if we do not expect at least 25%
+ * of unused properties. Our current implementation arbitrarily sets the threshold to 50%.
+ */
final int n = indices.size();
- isSparse = (n > 16) && (mandatory < n/2); // Arbitrary criterion.
+ isSparse = (n > 24) && (mandatory <= n/2);
}
/**
Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] Wed May 21 14:17:16 2014
@@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Arrays;
import org.opengis.metadata.maintenance.ScopeCode;
import org.opengis.metadata.quality.DataQuality;
+import org.apache.sis.internal.util.Cloner;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
@@ -214,6 +215,29 @@ final class DenseFeature extends Abstrac
}
/**
+ * Returns a shallow copy of this feature.
+ * The properties are cloned, but not the property values.
+ *
+ * @return A clone of this feature.
+ */
+ @Override
+ public AbstractFeature clone() throws CloneNotSupportedException {
+ final DenseFeature clone = (DenseFeature) super.clone();
+ clone.properties = clone.properties.clone();
+ if (clone.properties instanceof Property[]) {
+ final Property[] p = (Property[]) clone.properties;
+ final Cloner cloner = new Cloner();
+ for (int i=0; i<p.length; i++) {
+ final Property property = p[i];
+ if (property instanceof Cloneable) {
+ p[i] = (Property) cloner.clone(property);
+ }
+ }
+ }
+ return clone;
+ }
+
+ /**
* Returns a hash code value for this feature.
*
* @return A hash code value.
Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] Wed May 21 14:17:16 2014
@@ -19,8 +19,10 @@ package org.apache.sis.feature;
import java.util.Map;
import java.util.HashMap;
import java.util.ConcurrentModificationException;
+import java.lang.reflect.Field;
import org.opengis.metadata.maintenance.ScopeCode;
import org.opengis.metadata.quality.DataQuality;
+import org.apache.sis.internal.util.Cloner;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.CorruptedObjectException;
@@ -71,7 +73,7 @@ final class SparseFeature extends Abstra
*
* @see #valuesKind
*/
- private final Map<String, Object> properties;
+ private final HashMap<String, Object> properties;
/**
* {@link #PROPERTIES} if the values in the {@link #properties} map are {@link Property} instances,
@@ -236,6 +238,40 @@ final class SparseFeature extends Abstra
}
/**
+ * Returns a copy of this feature.
+ * The properties are cloned, but not the property values.
+ *
+ * @return A clone of this feature.
+ */
+ @Override
+ public AbstractFeature clone() throws CloneNotSupportedException {
+ final SparseFeature clone = (SparseFeature) super.clone();
+ try {
+ final Field field = SparseFeature.class.getDeclaredField("properties");
+ field.setAccessible(true);
+ field.set(clone, clone.properties.clone());
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ switch (clone.valuesKind) {
+ default: throw new AssertionError(clone.valuesKind);
+ case CORRUPTED: throw new CorruptedObjectException(clone.getName());
+ case VALUES: break; // Nothing to do.
+ case PROPERTIES: {
+ final Cloner cloner = new Cloner();
+ for (final Map.Entry<String,Object> entry : clone.properties.entrySet()) {
+ final Property property = (Property) entry.getValue();
+ if (property instanceof Cloneable) {
+ entry.setValue(cloner.clone(property));
+ }
+ }
+ break;
+ }
+ }
+ return clone;
+ }
+
+ /**
* Returns a hash code value for this feature.
*
* @return A hash code value.
Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java [UTF-8] Wed May 21 14:17:16 2014
@@ -59,7 +59,7 @@ public final strictfp class DefaultAssoc
public void testWrongValue() {
final DefaultAssociation association = twinTown();
final PropertyType population = association.getRole().getValueType().getProperty("population");
- final AbstractFeature otherFeature = new DefaultFeatureType(
+ final AbstractFeature otherFeature = new DefaultFeatureType(
singletonMap(DefaultFeatureType.NAME_KEY, "Population"), false, null, population).newInstance();
try {
association.setValue(otherFeature);
@@ -76,7 +76,8 @@ public final strictfp class DefaultAssoc
*/
@Test
public void testSerialization() {
- assertSerializedEquals(twinTown());
+ final DefaultAssociation twinTown = twinTown();
+ assertNotSame(twinTown, assertSerializedEquals(twinTown));
}
/**
Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java [UTF-8] Wed May 21 14:17:16 2014
@@ -132,10 +132,12 @@ public final strictfp class DefaultAttri
/**
* Tests {@link DefaultAttribute#clone()}.
+ *
+ * @throws CloneNotSupportedException Should never happen.
*/
@Test
@DependsOnMethod("testEquals")
- public void testClone() {
+ public void testClone() throws CloneNotSupportedException {
final DefaultAttribute<Integer> a1 = population();
final DefaultAttribute<Integer> a2 = a1.clone();
assertNotSame(a1, a2);
@@ -149,7 +151,7 @@ public final strictfp class DefaultAttri
@DependsOnMethod("testEquals")
public void testSerialization() {
final DefaultAttribute<String> attribute = city();
- assertSerializedEquals(attribute);
+ assertNotSame(attribute, assertSerializedEquals(attribute));
}
/**
Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java [UTF-8] Wed May 21 14:17:16 2014
@@ -107,6 +107,14 @@ public final strictfp class DefaultFeatu
}
/**
+ * Creates a sub-type of the "metropolis" type with the "region" attribute overridden to
+ * {@link InternationalString}.
+ */
+ static DefaultFeatureType worldMetropolis() {
+ return worldMetropolis(metropolis(), InternationalString.class);
+ }
+
+ /**
* Creates a sub-type of the "metropolis" type with the "region" attribute overridden to the given type.
* The given type should be {@link InternationalString}, but we allow other type for testing argument checks.
*/
Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java?rev=1596580&r1=1596579&r2=1596580&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] Wed May 21 14:17:16 2014
@@ -16,6 +16,10 @@
*/
package org.apache.sis.feature;
+import org.opengis.metadata.quality.Element;
+import org.opengis.metadata.quality.Result;
+import org.opengis.metadata.quality.ConformanceResult;
+import org.apache.sis.util.iso.SimpleInternationalString;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.TestCase;
import org.junit.Test;
@@ -98,23 +102,51 @@ abstract strictfp class FeatureTestCase
}
/**
+ * Sets the attribute of the given name to the given value.
+ * First, this method verifies that the previous value is equals to the given one.
+ * Then, this method set the attribute to the given value and check if the result.
+ *
+ * @param name The name of the attribute to set.
+ * @param oldValue The expected old value (may be {@code null}).
+ * @param newValue The new value to set.
+ */
+ private void setAttributeValue(final String name, final Object oldValue, final Object newValue) {
+ assertEquals(name, oldValue, getAttributeValue(name));
+ feature.setPropertyValue(name, newValue);
+ assertEquals(name, newValue, getAttributeValue(name));
+ }
+
+ /**
* Tests the {@link AbstractFeature#getPropertyValue(String)} method on a simple feature without super-types.
+ * This method also tests that attempts to set a value of the wrong type throw an exception and leave the
+ * previous value unchanged, that the feature is cloneable and that serialization works.
*/
@Test
public void testSimpleValues() {
feature = createFeature(DefaultFeatureTypeTest.city());
-
- assertEquals("Utopia", getAttributeValue("city"));
- feature.setPropertyValue("city", "Atlantide");
- assertEquals("Atlantide", feature.getPropertyValue("city"));
-
- assertNull(getAttributeValue("population"));
- feature.setPropertyValue("population", 1000);
- assertEquals(1000, getAttributeValue("population"));
+ setAttributeValue("city", "Utopia", "Atlantide");
+ try {
+ feature.setPropertyValue("city", 2000);
+ } catch (ClassCastException e) {
+ final String message = e.getMessage();
+ assertTrue(message, message.contains("city"));
+ assertTrue(message, message.contains("Integer"));
+ }
+ assertEquals("Property shall not have been modified.", "Atlantide", getAttributeValue("city"));
+ setAttributeValue("population", null, 1000);
+ assertValid();
+ testSerialization();
+ try {
+ testClone("population", 1000, 1500);
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e);
+ }
}
/**
* Tests the {@link AbstractFeature#getProperty(String)} method on a simple feature without super-types.
+ * This method also tests that attempts to set a value of the wrong type throw an exception and leave the
+ * previous value unchanged.
*/
@Test
@DependsOnMethod("testSimpleValues")
@@ -122,4 +154,87 @@ abstract strictfp class FeatureTestCase
getValuesFromProperty = true;
testSimpleValues();
}
+
+ /**
+ * Tests {@link AbstractFeature#getProperty(String)} and {@link AbstractFeature#getPropertyValue(String)}
+ * on a "complex" feature, involving inheritance and property overriding.
+ */
+ @Test
+ @DependsOnMethod({"testSimpleValues", "testSimpleProperties"})
+ public void testComplexFeature() {
+ feature = createFeature(DefaultFeatureTypeTest.worldMetropolis());
+ setAttributeValue("city", "Utopia", "New York");
+ setAttributeValue("population", null, 8405837); // Estimation for 2013.
+ /*
+ * Switch to 'getProperty' mode only after we have set at least one value,
+ * in order to test the conversion of existing values to property instances.
+ */
+ getValuesFromProperty = true;
+ setAttributeValue("isGlobal", null, Boolean.TRUE);
+ final SimpleInternationalString region = new SimpleInternationalString("State of New York");
+ setAttributeValue("region", null, region);
+ /*
+ * In our 'metropolis' feature type, the region can be any CharSequence. But 'worldMetropolis'
+ * feature type overrides the region property with a restriction to InternationalString.
+ * Verifiy that this restriction is checked.
+ */
+ try {
+ feature.setPropertyValue("region", "State of New York");
+ } catch (ClassCastException e) {
+ final String message = e.getMessage();
+ assertTrue(message, message.contains("region"));
+ assertTrue(message, message.contains("String"));
+ }
+ assertSame("region", region, getAttributeValue("region"));
+ assertValid();
+ testSerialization();
+ try {
+ testClone("population", 8405837, 8405838); // A birth...
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Asserts that {@link AbstractFeature#quality()} reports no anomaly.
+ */
+ private void assertValid() {
+ for (final Element report : feature.quality().getReports()) {
+ for (final Result result : report.getResults()) {
+ assertInstanceOf("result", ConformanceResult.class, result);
+ assertTrue("result.pass", ((ConformanceResult) result).pass());
+ }
+ }
+ }
+
+ /**
+ * Tests the {@link AbstractFeature#clone()} method on the current {@link #feature} instance.
+ * This method is invoked from other test methods using the existing feature instance in an opportunist way.
+ *
+ * @param property The name of a property to change.
+ * @param oldValue The old value of the given property.
+ * @param newValue The new value of the given property.
+ * @throws CloneNotSupportedException Should never happen.
+ */
+ private void testClone(final String property, final Object oldValue, final Object newValue)
+ throws CloneNotSupportedException
+ {
+ final AbstractFeature clone = feature.clone();
+ assertNotSame("clone", clone, feature);
+ assertTrue ("equals", clone.equals(feature));
+ assertTrue ("hashCode", clone.hashCode() == feature.hashCode());
+ setAttributeValue(property, oldValue, newValue);
+ assertEquals (property, oldValue, clone .getPropertyValue(property));
+ assertEquals (property, newValue, feature.getPropertyValue(property));
+ assertFalse ("equals", clone.equals(feature));
+ assertFalse ("hashCode", clone.hashCode() == feature.hashCode());
+ }
+
+ /**
+ * Tests serialization of current {@link #feature} instance.
+ * This method is invoked from other test methods using the existing feature instance in an opportunist way.
+ */
+ private void testSerialization() {
+ assertNotSame(feature, assertSerializedEquals(feature));
+ }
}