sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1596975 - in /sis/branches/JDK8/core/sis-feature/src: main/java/org/apache/sis/feature/ test/java/org/apache/sis/feature/
Date Thu, 22 May 2014 21:55:23 GMT
Author: desruisseaux
Date: Thu May 22 21:55:23 2014
New Revision: 1596975

URL: http://svn.apache.org/r1596975
Log:
Added tests related to MultiValuedAttribute.

Modified:
    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/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/DefaultAttributeType.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/Features.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/main/java/org/apache/sis/feature/SparseFeature.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.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/FeatureFormatTest.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/AbstractAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java?rev=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -47,7 +47,7 @@ import org.apache.sis.util.resources.Err
  *       the same SIS version.</li>
  * </ul>
  *
- * @param <T> The type of attribute values.
+ * @param <V> The type of attribute values.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -57,7 +57,7 @@ import org.apache.sis.util.resources.Err
  *
  * @see DefaultAttributeType
  */
-public abstract class AbstractAttribute<T> extends Property implements Serializable {
+public abstract class AbstractAttribute<V> extends Property implements Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -66,19 +66,49 @@ public abstract class AbstractAttribute<
     /**
      * Information about the attribute (base Java class, domain of values, <i>etc.</i>).
      */
-    final DefaultAttributeType<T> type;
+    final DefaultAttributeType<V> type;
 
     /**
      * Creates a new attribute of the given type.
      *
      * @param type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
      */
-    public AbstractAttribute(final DefaultAttributeType<T> type) {
-        ArgumentChecks.ensureNonNull("type", type);
+    protected AbstractAttribute(final DefaultAttributeType<V> type) {
         this.type = type;
     }
 
     /**
+     * Creates a new attribute of the given type initialized to the
+     * {@linkplain DefaultAttributeType#getDefaultValue() default value}.
+     *
+     * @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.
+     */
+    public static <V> AbstractAttribute<V> create(final DefaultAttributeType<V> type) {
+        ArgumentChecks.ensureNonNull("type", type);
+        return (type.getMaximumOccurs() <= 1)
+               ? new SingletonAttribute<>(type)
+               : new MultiValuedAttribute<>(type);
+    }
+
+    /**
+     * Creates a new attribute of the given type initialized to the given value.
+     * Note that a {@code null} value may not be the same as the default value.
+     *
+     * @param  <V>   The type of attribute values.
+     * @param  type  Information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     * @param  value The initial value (may be {@code null}).
+     * @return The new attribute.
+     */
+    static <V> AbstractAttribute<V> create(final DefaultAttributeType<V> type, final Object value) {
+        ArgumentChecks.ensureNonNull("type", type);
+        return (type.getMaximumOccurs() <= 1)
+               ? new SingletonAttribute<>(type, value)
+               : new MultiValuedAttribute<>(type, value);
+    }
+
+    /**
      * Returns the name of this attribute as defined by its {@linkplain #getType() type}.
      * This convenience method delegates to {@link DefaultAttributeType#getName()}.
      *
@@ -97,7 +127,7 @@ public abstract class AbstractAttribute<
      *
      * @return Information about the attribute.
      */
-    public DefaultAttributeType<T> getType() {
+    public DefaultAttributeType<V> getType() {
         return type;
     }
 
@@ -111,7 +141,7 @@ public abstract class AbstractAttribute<
      *
      * @see AbstractFeature#getPropertyValue(String)
      */
-    public abstract T getValue() throws IllegalStateException;
+    public abstract V getValue() throws IllegalStateException;
 
     /**
      * Returns all attribute values, or an empty collection if none.
@@ -123,7 +153,7 @@ public abstract class AbstractAttribute<
      *
      * @return The attribute values in a <cite>live</cite> collection.
      */
-    public Collection<T> getValues() {
+    public Collection<V> getValues() {
         return new PropertySingleton<>(this);
     }
 
@@ -140,7 +170,7 @@ public abstract class AbstractAttribute<
      *
      * @see AbstractFeature#setPropertyValue(String, Object)
      */
-    public abstract void setValue(final T value);
+    public abstract void setValue(final V value);
 
     /**
      * Set the attribute values. All previous values are replaced by the given collection.
@@ -150,10 +180,10 @@ public abstract class AbstractAttribute<
      *
      * @param values The new values.
      */
-    public void setValues(final Collection<? extends T> values) {
-        T value = null;
+    public void setValues(final Collection<? extends V> values) {
+        V value = null;
         ArgumentChecks.ensureNonNull("values", values);
-        final Iterator<? extends T> it = values.iterator();
+        final Iterator<? extends V> it = values.iterator();
         if (it.hasNext()) {
             value = it.next();
             if (it.hasNext()) {
@@ -233,7 +263,7 @@ public abstract class AbstractAttribute<
      */
     public DataQuality quality() {
         final Validator v = new Validator(ScopeCode.ATTRIBUTE);
-        v.validate(type, getValue());
+        v.validate(type, getValues());
         return v.quality;
     }
 

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -18,6 +18,7 @@ package org.apache.sis.feature;
 
 import java.util.Iterator;
 import java.util.Collection;
+import java.util.Collections;
 import java.io.Serializable;
 import org.opengis.util.GenericName;
 import org.opengis.metadata.quality.DataQuality;
@@ -181,7 +182,7 @@ public abstract class AbstractFeature im
     final Property createProperty(final String name, final Object value) {
         final PropertyType pt = getPropertyType(name);
         if (pt instanceof DefaultAttributeType<?>) {
-            return new SingletonAttribute<>((DefaultAttributeType<?>) pt, value);
+            return AbstractAttribute.create((DefaultAttributeType<?>) pt, value);
         } else if (pt instanceof DefaultAssociationRole) {
             return new DefaultAssociation((DefaultAssociationRole) pt, (AbstractFeature) value);
         } else {
@@ -200,10 +201,7 @@ public abstract class AbstractFeature im
     final Property createProperty(final String name) throws IllegalArgumentException {
         final PropertyType pt = getPropertyType(name);
         if (pt instanceof DefaultAttributeType<?>) {
-            final DefaultAttributeType<?> at = (DefaultAttributeType<?>) pt;
-            return (at.getMaximumOccurs() <= 1)
-                   ? new SingletonAttribute<>(at)
-                   : new MultiValuedAttribute<>(at);
+            return AbstractAttribute.create((DefaultAttributeType<?>) pt);
         } else if (pt instanceof DefaultAssociationRole) {
             return new DefaultAssociation((DefaultAssociationRole) pt);
         } else {
@@ -212,7 +210,8 @@ public abstract class AbstractFeature im
     }
 
     /**
-     * Returns the default value for the property of the given name.
+     * Returns the default value to be returned by {@link #getPropertyValue(String)}
+     * for the property of the given name.
      *
      * @param  name The name of the property for which to get the default value.
      * @return The default value for the {@code Property} of the given name.
@@ -221,7 +220,7 @@ public abstract class AbstractFeature im
     final Object getDefaultValue(final String name) throws IllegalArgumentException {
         final PropertyType pt = getPropertyType(name);
         if (pt instanceof DefaultAttributeType<?>) {
-            return ((DefaultAttributeType<?>) pt).getDefaultValue();
+            return getDefaultValue((DefaultAttributeType<?>) pt);
         } else if (pt instanceof DefaultAssociationRole) {
             return null; // No default value for associations.
         } else {
@@ -230,6 +229,19 @@ public abstract class AbstractFeature im
     }
 
     /**
+     * Returns the default value to be returned by {@link #getPropertyValue(String)} for the given attribute type.
+     */
+    private static <V> Object getDefaultValue(final DefaultAttributeType<V> attribute) {
+        final V defaultValue = attribute.getDefaultValue();
+        if (attribute.getMaximumOccurs() <= 1) {
+            return defaultValue;
+        } else {
+            // Following is for compliance with getPropertyValue(String) method contract - see its javadoc.
+            return (defaultValue != null) ? Collections.singletonList(defaultValue) : Collections.emptyList();
+        }
+    }
+
+    /**
      * Returns the value for the property of the given name.
      * This convenience method is equivalent to invoking {@link #getProperty(String)} for the given name,
      * then to perform one of the following actions depending on the property type and the cardinality:
@@ -301,26 +313,26 @@ public abstract class AbstractFeature im
      * use {@link Validator} instead.
      */
     @SuppressWarnings("unchecked")
-    private static <T> void setAttributeValue(final AbstractAttribute<T> attribute, final Object value) {
+    private static <V> void setAttributeValue(final AbstractAttribute<V> attribute, final Object value) {
         if (value != null) {
-            final DefaultAttributeType<T> pt = attribute.getType();
+            final DefaultAttributeType<V> pt = attribute.getType();
             final Class<?> base = pt.getValueClass();
             if (!base.isInstance(value)) {
                 Object element = value;
                 if (value instanceof Collection<?>) {
                     /*
                      * If the given value is a collection, verify the class of all values
-                     * before to delegate to Attribute.setValues(Collection<? extends T>).
+                     * before to delegate to Attribute.setValues(Collection<? extends V>).
                      */
                     final Iterator<?> it = ((Collection<?>) value).iterator();
                     do if (!it.hasNext()) {
                         ((AbstractAttribute) attribute).setValues((Collection) value);
                         return;
-                    } while (base.isInstance(element = it.next()) || element == null);
+                    } while ((element = it.next()) == null || base.isInstance(element));
                     // Found an illegal value. Exeption is thrown below.
                 }
                 throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
-                        pt.getName(), element.getClass()));
+                        pt.getName(), element.getClass())); // 'element' can not be null here.
             }
         }
         ((AbstractAttribute) attribute).setValue(value);
@@ -330,7 +342,7 @@ public abstract class AbstractFeature im
      * Sets the association value after verification of its type.
      * For a more exhaustive validation, use {@link Validator} instead.
      */
-    private static <T> void setAssociationValue(final DefaultAssociation association, final AbstractFeature value) {
+    private static void setAssociationValue(final DefaultAssociation association, final AbstractFeature value) {
         if (value != null) {
             final DefaultAssociationRole pt = association.getRole();
             final DefaultFeatureType base = pt.getValueType();
@@ -344,14 +356,14 @@ public abstract class AbstractFeature im
     }
 
     /**
-     * Returns {@code true} if the caller can skip the call to {@link #verifyValueType(String, Object)}.
+     * Returns {@code true} if the caller can skip the call to {@link #verifyPropertyValue(String, Object)}.
      * This is a slight optimization for the case when we replaced an attribute value by a new value of
      * the same class. Since the type check has already been done by the previous assignation, we do not
      * need to perform it again.
      *
      * @param previous The previous value, or {@code null}.
      * @param value    The new value, or {@code null}.
-     * @return         {@code true} if the caller can skip the verification performed by {@code verifyValueType}.
+     * @return         {@code true} if the caller can skip the verification performed by {@code verifyPropertyValue}.
      */
     static boolean canSkipVerification(final Object previous, final Object value) {
         if (previous != null) {
@@ -366,74 +378,100 @@ public abstract class AbstractFeature im
     }
 
     /**
+     * Verifies if the given properties can be assigned to this feature.
+     *
+     * @param name Shall be {@code property.getName().toString()}.
+     * @param property The property to verify.
+     */
+    final void verifyPropertyType(final String name, final Property property) {
+        final PropertyType type;
+        if (property instanceof AbstractAttribute<?>) {
+            type = ((AbstractAttribute<?>) property).getType();
+        } else if (property instanceof DefaultAssociation) {
+            type = ((DefaultAssociation) property).getRole();
+        } else {
+            throw new IllegalArgumentException(Errors.format(
+                    Errors.Keys.IllegalArgumentClass_2, "property", property.getClass()));
+        }
+        if (type != getPropertyType(name)) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedPropertyType_1, name));
+        }
+    }
+
+    /**
      * Verifies the validity of the given value for the property of the given name, then returns the value
      * to store. The returned value is usually the same than the given one, except in the case of collections.
      */
-    final Object verifyValueType(final String name, final Object value) {
+    final Object verifyPropertyValue(final String name, final Object value) {
         final PropertyType pt = getPropertyType(name);
         if (pt instanceof DefaultAttributeType<?>) {
-            /*
-             * Attribute :
-             *   - May be a singleton,  in which case the value class is verified.
-             *   - May be a collection, in which case the class each elemets in the collection is verified.
-             */
             if (value != null) {
-                final Class<?> valueClass = ((DefaultAttributeType<?>) pt).getValueClass();
-                if (!valueClass.isInstance(value)) {
-                    if (value instanceof Collection<?>) {
-                        return CheckedArrayList.castOrCopy((Collection<?>) value, valueClass);
-                    } else {
-                        throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
-                                name, value.getClass()));
-                    }
-                }
+                return verifyAttributeValue((DefaultAttributeType<?>) pt, value);
             }
         } else if (pt instanceof DefaultAssociationRole) {
-            /*
-             * Association:
-             *   - May be a singleton,  in which case the feature type is verified.
-             */
             if (value != null) {
-                if (value instanceof AbstractFeature) {
-                    final DefaultFeatureType valueType = ((AbstractFeature) value).getType();
-                    if (!((DefaultAssociationRole) pt).getValueType().maybeAssignableFrom(valueType)) {
-                        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
-                                name, valueType.getName()));
-                    }
-                } else {
+                if (!(value instanceof AbstractFeature)) {
                     throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
                             name, value.getClass()));
                 }
+                return verifyAssociationValue((DefaultAssociationRole) pt, (AbstractFeature) value);
             }
         } else {
-            /*
-             * Operation (or any other type):
-             *   - Legal in FeatureType, but not expected in Feature instance.
-             */
             throw new IllegalArgumentException(unsupportedPropertyType(pt.getName()));
         }
         return value;
     }
 
     /**
-     * Verifies if the given properties can be assigned to this feature.
-     *
-     * @param name Shall be {@code property.getName().toString()}.
-     * @param property The property to verify.
+     * Verifies the validity of the given attribute value, and returns the value to store in the feature.
+     * An attribute:
+     * <ul>
+     *   <li>May be a singleton,  in which case the value class is verified.</li>
+     *   <li>May be a collection, in which case the class each elements in the collection is verified.</li>
+     * </ul>
      */
-    final void verifyPropertyType(final String name, final Property property) {
-        final PropertyType type;
-        if (property instanceof AbstractAttribute<?>) {
-            type = ((AbstractAttribute<?>) property).getType();
-        } else if (property instanceof DefaultAssociation) {
-            type = ((DefaultAssociation) property).getRole();
+    private static <T> Object verifyAttributeValue(final DefaultAttributeType<T> type, final Object value) {
+        final Class<T> valueClass = type.getValueClass();
+        final boolean isMultiValued = type.getMaximumOccurs() > 1;
+        if (valueClass.isInstance(value)) {
+            if (isMultiValued) {
+                return singleton(valueClass, type.getMinimumOccurs(), value);
+            }
         } else {
-            throw new IllegalArgumentException(Errors.format(
-                    Errors.Keys.IllegalArgumentClass_2, "property", property.getClass()));
+            if (isMultiValued && value instanceof Collection<?>) {
+                return CheckedArrayList.castOrCopy((Collection<?>) value, valueClass);
+            } else {
+                throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
+                        type.getName(), value.getClass()));
+            }
         }
-        if (type != getPropertyType(name)) {
-            throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedPropertyType_1, name));
+        return value;
+    }
+
+    /**
+     * Verifies the validity of the given association value, and returns the value to store in the feature.
+     * An association:
+     * <ul>
+     *   <li>May be a singleton,  in which case the feature type is verified.</li>
+     * </ul>
+     */
+    private static Object verifyAssociationValue(final DefaultAssociationRole role, final AbstractFeature value) {
+        final DefaultFeatureType valueType = value.getType();
+        if (!role.getValueType().maybeAssignableFrom(valueType)) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
+                    role.getName(), valueType.getName()));
         }
+        return value;
+    }
+
+    /**
+     * Creates a collection which will initially contain only the given value.
+     */
+    @SuppressWarnings("unchecked")
+    private static <V> Collection<V> singleton(final Class<V> valueClass, final int minimumOccurs, final Object value) {
+        final CheckedArrayList<V> values = new CheckedArrayList<>(valueClass, Math.max(minimumOccurs, 4));
+        values.add((V) value); // Type will be checked by CheckedArrayList.
+        return values;
     }
 
     /**

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -71,9 +71,9 @@ public class DefaultAssociation extends 
      * Creates a new association of the given type initialized to the given value.
      *
      * @param role  Information about the association.
-     * @param value The initial value.
+     * @param value The initial value (may be {@code null}).
      */
-    public DefaultAssociation(final DefaultAssociationRole role, final AbstractFeature value) {
+    DefaultAssociation(final DefaultAssociationRole role, final AbstractFeature value) {
         ArgumentChecks.ensureNonNull("role", role);
         this.role  = role;
         this.value = value;
@@ -119,6 +119,12 @@ public class DefaultAssociation extends 
         return value;
     }
 
+    final java.util.Collection<AbstractFeature> getValues() {
+        return (value == null)
+               ? java.util.Collections.emptyList()
+               : java.util.Collections.singletonList(value);
+    }
+
     /**
      * Sets the associated feature.
      *
@@ -171,7 +177,7 @@ public class DefaultAssociation extends 
      */
     public DataQuality quality() {
         final Validator v = new Validator(null);
-        v.validate(role, value);
+        v.validate(role, getValues());
         return v.quality;
     }
 

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -65,7 +65,7 @@ import java.util.Objects;
  * This means that the same {@code defaultValue} instance may be shared by many {@link AbstractAttribute} instances.
  * Consequently the default value should be immutable for avoiding unexpected behavior.</p>
  *
- * @param <T> The type of attribute values.
+ * @param <V> The type of attribute values.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -75,7 +75,7 @@ import java.util.Objects;
  *
  * @see AbstractAttribute
  */
-public class DefaultAttributeType<T> extends FieldType {
+public class DefaultAttributeType<V> extends FieldType {
     /**
      * For cross-version compatibility.
      */
@@ -86,14 +86,14 @@ public class DefaultAttributeType<T> ext
      *
      * @see #getValueClass()
      */
-    private final Class<T> valueClass;
+    private final Class<V> valueClass;
 
     /**
      * The default value for the attribute, or {@code null} if none.
      *
      * @see #getDefaultValue()
      */
-    private final T defaultValue;
+    private final V defaultValue;
 
     /**
      * Constructs an attribute type from the given properties. The identification map is given unchanged to
@@ -136,8 +136,8 @@ public class DefaultAttributeType<T> ext
      *                       or {@link Integer#MAX_VALUE} if there is no restriction.
      * @param defaultValue   The default value for the attribute, or {@code null} if none.
      */
-    public DefaultAttributeType(final Map<String,?> identification, final Class<T> valueClass,
-            final int minimumOccurs, final int maximumOccurs, final T defaultValue)
+    public DefaultAttributeType(final Map<String,?> identification, final Class<V> valueClass,
+            final int minimumOccurs, final int maximumOccurs, final V defaultValue)
     {
         super(identification, minimumOccurs, maximumOccurs);
         ensureNonNull("valueClass",   valueClass);
@@ -151,7 +151,7 @@ public class DefaultAttributeType<T> ext
      *
      * @return The type of attribute values.
      */
-    public final Class<T> getValueClass() {
+    public final Class<V> getValueClass() {
         return valueClass;
     }
 
@@ -202,7 +202,7 @@ public class DefaultAttributeType<T> ext
      *
      * @return The default value for the attribute, or {@code null} if none.
      */
-    public T getDefaultValue() {
+    public V getDefaultValue() {
         return defaultValue;
     }
 

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -193,7 +193,7 @@ final class DenseFeature extends Abstrac
         if (!(properties instanceof Property[])) {
             if (value != null) {
                 if (!canSkipVerification(properties[index], value)) {
-                    value = verifyValueType(name, value);
+                    value = verifyPropertyValue(name, value);
                 }
                 properties[index] = value;
                 return;

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -40,7 +40,7 @@ public final class Features extends Stat
      * An exception is thrown immediately if the given type does not have the expected
      * {@linkplain DefaultAttributeType#getValueClass() value class}.
      *
-     * @param  <T>        The expected value class.
+     * @param  <V>        The expected value class.
      * @param  type       The attribute type to cast, or {@code null}.
      * @param  valueClass The expected value class.
      * @return The attribute type casted to the given value class, or {@code null} if the given type was null.
@@ -49,19 +49,19 @@ public final class Features extends Stat
      * @category verification
      */
     @SuppressWarnings("unchecked")
-    public static <T> DefaultAttributeType<T> cast(final DefaultAttributeType<?> type, final Class<T> valueClass)
+    public static <V> DefaultAttributeType<V> cast(final DefaultAttributeType<?> type, final Class<V> valueClass)
             throws ClassCastException
     {
         if (type != null) {
             final Class<?> actual = type.getValueClass();
             // We require a strict equality - not type.isAssignableFrom(actual) - because in
-            // the later case we could have (to be strict) to return a <? extends T> type.
+            // the later case we could have (to be strict) to return a <? extends V> type.
             if (!valueClass.equals(actual)) {
                 throw new ClassCastException(Errors.format(Errors.Keys.MismatchedValueClass_3,
                         type.getName(), valueClass, actual));
             }
         }
-        return (DefaultAttributeType<T>) type;
+        return (DefaultAttributeType<V>) type;
     }
 
     /**
@@ -69,7 +69,7 @@ public final class Features extends Stat
      * An exception is thrown immediately if the given instance does not have the expected
      * {@linkplain DefaultAttributeType#getValueClass() value class}.
      *
-     * @param  <T>        The expected value class.
+     * @param  <V>        The expected value class.
      * @param  attribute  The attribute instance to cast, or {@code null}.
      * @param  valueClass The expected value class.
      * @return The attribute instance casted to the given value class, or {@code null} if the given instance was null.
@@ -78,18 +78,18 @@ public final class Features extends Stat
      * @category verification
      */
     @SuppressWarnings("unchecked")
-    public static <T> AbstractAttribute<T> cast(final AbstractAttribute<?> attribute, final Class<T> valueClass)
+    public static <V> AbstractAttribute<V> cast(final AbstractAttribute<?> attribute, final Class<V> valueClass)
             throws ClassCastException
     {
         if (attribute != null) {
             final Class<?> actual = attribute.getType().getValueClass();
             // We require a strict equality - not type.isAssignableFrom(actual) - because in
-            // the later case we could have (to be strict) to return a <? extends T> type.
+            // the later case we could have (to be strict) to return a <? extends V> type.
             if (!valueClass.equals(actual)) {
                 throw new ClassCastException(Errors.format(Errors.Keys.MismatchedValueClass_3,
                         attribute.getName(), valueClass, actual));
             }
         }
-        return (AbstractAttribute<T>) attribute;
+        return (AbstractAttribute<V>) attribute;
     }
 }

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -16,9 +16,9 @@
  */
 package org.apache.sis.feature;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.lang.reflect.Field;
+import org.apache.sis.internal.util.CheckedArrayList;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 
@@ -33,9 +33,12 @@ import org.apache.sis.util.resources.Err
  * <ul>
  *   <li><b>Multi-threading:</b> {@code MultiValuedAttribute} instances are <strong>not</strong> thread-safe.
  *       Synchronization, if needed, shall be done externally by the caller.</li>
+ *   <li><b>Serialization:</b> serialized objects of this class are not guaranteed to be compatible with future
+ *       versions. Serialization should be used only for short term storage or RMI between applications running
+ *       the same SIS version.</li>
  * </ul>
  *
- * @param <T> The type of the attribute values.
+ * @param <V> The type of the attribute values.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -45,7 +48,7 @@ import org.apache.sis.util.resources.Err
  *
  * @see DefaultAttributeType
  */
-final class MultiValuedAttribute<T> extends AbstractAttribute<T> implements Cloneable {
+final class MultiValuedAttribute<V> extends AbstractAttribute<V> implements Cloneable {
     /**
      * For cross-version compatibility.
      */
@@ -54,7 +57,7 @@ final class MultiValuedAttribute<T> exte
     /**
      * The attribute values.
      */
-    private final ArrayList<T> values;
+    private final CheckedArrayList<V> values;
 
     /**
      * Creates a new attribute of the given type initialized to the
@@ -62,23 +65,43 @@ final class MultiValuedAttribute<T> exte
      *
      * @param type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
      */
-    public MultiValuedAttribute(final DefaultAttributeType<T> type) {
+    public MultiValuedAttribute(final DefaultAttributeType<V> type) {
         super(type);
-        values = new ArrayList<>();
-        final T value = type.getDefaultValue();
+        values = new CheckedArrayList<>(type.getValueClass());
+        final V value = type.getDefaultValue();
         if (value != null) {
             values.add(value);
         }
     }
 
     /**
+     * Creates a new attribute of the given type initialized to the given values.
+     * Note that a {@code null} value may not be the same as the default value.
+     *
+     * @param type   Information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     * @param values The initial values, or {@code null} for initializing to an empty list.
+     */
+    @SuppressWarnings("unchecked")
+    MultiValuedAttribute(final DefaultAttributeType<V> type, final Object values) {
+        super(type);
+        final Class<V> valueClass = type.getValueClass();
+        if (values == null) {
+            this.values = new CheckedArrayList<>(valueClass);
+        } else if (((CheckedArrayList<?>) values).getElementType() == valueClass) {
+            this.values = (CheckedArrayList<V>) values;
+        } else {
+            throw new ClassCastException();
+        }
+    }
+
+    /**
      * Returns the attribute value, or {@code null} if none.
      *
      * @return The attribute value (may be {@code null}).
      * @throws IllegalStateException if this attribute contains more than one value.
      */
     @Override
-    public T getValue() {
+    public V getValue() {
         switch (values.size()) {
             case 0:  return null;
             case 1:  return values.get(0);
@@ -94,7 +117,7 @@ final class MultiValuedAttribute<T> exte
      * @return The attribute values in a <cite>live</cite> collection.
      */
     @Override
-    public Collection<T> getValues() {
+    public Collection<V> getValues() {
         return values;
     }
 
@@ -104,7 +127,7 @@ final class MultiValuedAttribute<T> exte
      * @param value The new value, or {@code null} for removing all values from this attribute.
      */
     @Override
-    public void setValue(final T value) {
+    public void setValue(final V value) {
         values.clear();
         if (value != null) {
             values.add(value);
@@ -117,7 +140,7 @@ final class MultiValuedAttribute<T> exte
      * @param values The new values.
      */
     @Override
-    public void setValues(final Collection<? extends T> values) {
+    public void setValues(final Collection<? extends V> values) {
         ArgumentChecks.ensureNonNull("values", values);
         this.values.clear();
         this.values.addAll(values);
@@ -136,8 +159,8 @@ final class MultiValuedAttribute<T> exte
      */
     @Override
     @SuppressWarnings("unchecked")
-    public MultiValuedAttribute<T> clone() throws CloneNotSupportedException {
-        final MultiValuedAttribute<T> clone = (MultiValuedAttribute<T>) super.clone();
+    public MultiValuedAttribute<V> clone() throws CloneNotSupportedException {
+        final MultiValuedAttribute<V> clone = (MultiValuedAttribute<V>) super.clone();
         try {
             final Field field = MultiValuedAttribute.class.getDeclaredField("values");
             field.setAccessible(true);

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -32,7 +32,7 @@ import java.util.Objects;
  *       Synchronization, if needed, shall be done externally by the caller.</li>
  * </ul>
  *
- * @param <T> The type of the attribute value.
+ * @param <V> The type of the attribute value.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -42,7 +42,7 @@ import java.util.Objects;
  *
  * @see DefaultAttributeType
  */
-final class SingletonAttribute<T> extends AbstractAttribute<T> implements Cloneable {
+final class SingletonAttribute<V> extends AbstractAttribute<V> implements Cloneable {
     /**
      * For cross-version compatibility.
      */
@@ -51,7 +51,7 @@ final class SingletonAttribute<T> extend
     /**
      * The attribute value, or {@code null} if none.
      */
-    private T value;
+    private V value;
 
     /**
      * Creates a new attribute of the given type initialized to the
@@ -59,7 +59,7 @@ final class SingletonAttribute<T> extend
      *
      * @param type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
      */
-    public SingletonAttribute(final DefaultAttributeType<T> type) {
+    public SingletonAttribute(final DefaultAttributeType<V> type) {
         super(type);
         assert type.getMaximumOccurs() <= 1;
         value = type.getDefaultValue();
@@ -67,12 +67,12 @@ final class SingletonAttribute<T> extend
 
     /**
      * Creates a new attribute of the given type initialized to the given value.
-     * Note that a {@code null} value may not the same as the default value.
+     * Note that a {@code null} value may not be the same as the default value.
      *
      * @param type  Information about the attribute (base Java class, domain of values, <i>etc.</i>).
-     * @param value The initial value (may be null {@code null}).
+     * @param value The initial value (may be {@code null}).
      */
-    SingletonAttribute(final DefaultAttributeType<T> type, final Object value) {
+    SingletonAttribute(final DefaultAttributeType<V> type, final Object value) {
         super(type);
         assert type.getMaximumOccurs() <= 1;
         this.value = type.getValueClass().cast(value);
@@ -86,7 +86,7 @@ final class SingletonAttribute<T> extend
      * @see AbstractFeature#getPropertyValue(String)
      */
     @Override
-    public T getValue() {
+    public V getValue() {
         return value;
     }
 
@@ -96,7 +96,7 @@ final class SingletonAttribute<T> extend
      * @param value The new value.
      */
     @Override
-    public void setValue(final T value) {
+    public void setValue(final V value) {
         this.value = value;
     }
 
@@ -113,8 +113,8 @@ final class SingletonAttribute<T> extend
      */
     @Override
     @SuppressWarnings("unchecked")
-    public SingletonAttribute<T> clone() throws CloneNotSupportedException {
-        return (SingletonAttribute<T>) super.clone();
+    public SingletonAttribute<V> clone() throws CloneNotSupportedException {
+        return (SingletonAttribute<V>) super.clone();
     }
 
     /**

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -213,7 +213,7 @@ final class SparseFeature extends Abstra
             if (!canSkipVerification(previous, value)) {
                 Object toStore = previous; // This initial value will restore the previous value if the check fail.
                 try {
-                    toStore = verifyValueType(name, value);
+                    toStore = verifyPropertyValue(name, value);
                 } finally {
                     if (toStore != value) {
                         replace(name, value, toStore);

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java?rev=1596975&r1=1596974&r2=1596975&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] Thu May 22 21:55:23 2014
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.feature;
 
+import java.util.Collection;
+import java.util.Collections;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
@@ -90,50 +92,65 @@ final class Validator {
     }
 
     /**
+     * Wraps singleton value in a collection for processing by {@code validate(…)} methods.
+     */
+    private static Collection<?> asList(final Object value, final int maximumOccurrences) {
+        if (maximumOccurrences <= 1) {
+            return (value != null) ? Collections.singletonList(value) : Collections.emptyList();
+        } else {
+            return (Collection<?>) value;
+        }
+    }
+
+    /**
      * Verifies if the given value is valid for the given attribute type.
      * This method delegates to one of the {@code validate(…)} methods depending of the value type.
      */
     void validateAny(final PropertyType type, final Object value) {
         if (type instanceof DefaultAttributeType<?>) {
-            validate((DefaultAttributeType<?>) type, value);
+            validate((DefaultAttributeType<?>) type, asList(value,
+                    ((DefaultAttributeType<?>) type).getMaximumOccurs()));
         }
         if (type instanceof DefaultAssociationRole) {
-            validate((DefaultAssociationRole) type, (AbstractFeature) value);
+            validate((DefaultAssociationRole) type, asList(value,
+                    ((DefaultAssociationRole) type).getMaximumOccurs()));
         }
     }
 
     /**
-     * Verifies if the given value is valid for the given attribute type.
+     * Verifies if the given values are valid for the given attribute type.
      */
-    void validate(final DefaultAttributeType<?> type, final Object value) {
+    void validate(final DefaultAttributeType<?> type, final Collection<?> values) {
         AbstractElement report = null;
-        if (value != null) {
+        for (final Object value : values) {
             /*
-             * In theory, the following check is unnecessary since the type was constrained by the Attribute.setValue(T)
+             * In theory, the following check is unnecessary since the type was constrained by the Attribute.setValue(V)
              * method signature. However in practice the call to Attribute.setValue(…) is sometime done after type erasure,
              * so we are better to check.
              */
             if (!type.getValueClass().isInstance(value)) {
                 report = addViolationReport(report, type, Errors.formatInternational(
                         Errors.Keys.IllegalPropertyClass_2, type.getName(), value.getClass()));
+                break; // Report only the first violation for now.
             }
         }
-        verifyCardinality(report, type, type.getMinimumOccurs(), type.getMaximumOccurs(), value);
+        verifyCardinality(report, type, type.getMinimumOccurs(), type.getMaximumOccurs(), values.size());
     }
 
     /**
      * Verifies if the given value is valid for the given association role.
      */
-    void validate(final DefaultAssociationRole role, final AbstractFeature value) {
+    void validate(final DefaultAssociationRole role, final Collection<?> values) {
         AbstractElement report = null;
-        if (value != null) {
-            final DefaultFeatureType type = value.getType();
+        for (final Object value : values) {
+            final DefaultFeatureType type = ((AbstractFeature) value).getType();
             if (!role.getValueType().isAssignableFrom(type)) {
                 report = addViolationReport(report, role, Errors.formatInternational(
                         Errors.Keys.IllegalPropertyClass_2, role.getName(), type.getName()));
+                break; // Report only the first violation for now.
             }
         }
-        verifyCardinality(report, role, role.getMinimumOccurs(), role.getMaximumOccurs(), value);
+        verifyCardinality(report, role, role.getMinimumOccurs(), role.getMaximumOccurs(), values.size());
     }
 
     /**
@@ -142,18 +159,24 @@ final class Validator {
      * @param report Where to add the result, or {@code null} if not yet created.
      */
     private void verifyCardinality(final AbstractElement report, final AbstractIdentifiedType type,
-            final int minimumOccurs, final int maximumOccurs, final Object value)
+            final int minimumOccurs, final int maximumOccurs, final int count)
     {
-        if (value == null) {
-            if (minimumOccurs != 0) {
-                addViolationReport(report, type, Errors.formatInternational(
-                        Errors.Keys.MissingValueForProperty_1, type.getName()));
+        if (count < minimumOccurs) {
+            final InternationalString message;
+            if (count == 0) {
+                message = Errors.formatInternational(Errors.Keys.MissingValueForProperty_1, type.getName());
+            } else {
+                message = Errors.formatInternational(Errors.Keys.TooFewOccurrences_2, minimumOccurs, type.getName());
             }
-        } else {
+            addViolationReport(report, type, message);
+        } else if (count > maximumOccurs) {
+            final InternationalString message;
             if (maximumOccurs == 0) {
-                addViolationReport(report, type, Errors.formatInternational(
-                        Errors.Keys.ForbiddenProperty_1, type.getName()));
+                message = Errors.formatInternational(Errors.Keys.ForbiddenProperty_1, type.getName());
+            } else {
+                message = Errors.formatInternational(Errors.Keys.TooManyOccurrences_2, maximumOccurs, type.getName());
             }
+            addViolationReport(report, type, message);
         }
     }
 }

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -63,6 +63,23 @@ public final strictfp class DefaultFeatu
     }
 
     /**
+     * Creates a sub-type of the "city" type with only one additional property, an arbitrary amount of strings.
+     * The feature contains the following attribute:
+     *
+     * <ul>
+     *   <li>{@code city}         as a  {@link String}  (mandatory)</li>
+     *   <li>{@code population}   as an {@link Integer} (mandatory)</li>
+     *   <li>{@code universities} as an arbitrary amount of {@link String}</li>
+     * </ul>
+     *
+     * @return The feature for an university city.
+     */
+    public static DefaultFeatureType universityCity() {
+        return new DefaultFeatureType(singletonMap(DefaultFeatureType.NAME_KEY, "University city"), false,
+                new DefaultFeatureType[] {city()}, DefaultAttributeTypeTest.universities());
+    }
+
+    /**
      * Creates a sub-type of the "city" type with only one additional property, a string giving the parliament name.
      * The feature contains the following attribute:
      *
@@ -106,19 +123,21 @@ public final strictfp class DefaultFeatu
 
     /**
      * Creates a sub-type of the "metropolis" type with the "region" attribute overridden to
-     * {@link InternationalString}.
+     * {@link InternationalString} and an arbitrary amount of universities.
      */
     static DefaultFeatureType worldMetropolis() {
-        return worldMetropolis(metropolis(), InternationalString.class);
+        return worldMetropolis(metropolis(), universityCity(), 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.
      */
-    private static DefaultFeatureType worldMetropolis(final DefaultFeatureType metropolis, final Class<?> regionType) {
+    private static DefaultFeatureType worldMetropolis(final DefaultFeatureType metropolis,
+            final DefaultFeatureType universityCity, final Class<?> regionType)
+    {
         return new DefaultFeatureType(singletonMap(DefaultFeatureType.NAME_KEY, "World metropolis"), false,
-                new DefaultFeatureType[] {metropolis},
+                new DefaultFeatureType[] {metropolis, universityCity},
                 new DefaultAttributeType<>(singletonMap(DefaultAttributeType.NAME_KEY, "region"),
                         regionType, 1, 1, null));
 
@@ -359,26 +378,27 @@ public final strictfp class DefaultFeatu
     @Test
     @DependsOnMethod({"testMultiInheritance", "testNameCollision"})
     public void testPropertyOverride() {
-        final DefaultFeatureType metropolis = metropolis();
+        final DefaultFeatureType metropolis     = metropolis();
+        final DefaultFeatureType universityCity = universityCity();
         try {
-            worldMetropolis(metropolis, Integer.class);
+            worldMetropolis(metropolis, universityCity, Integer.class);
             fail("Shall not be allowed to override a 'CharSequence' attribute with an 'Integer' one.");
         } catch (IllegalArgumentException e) {
             final String message = e.getMessage();
             assertTrue(message, message.contains("region"));
             assertTrue(message, message.contains("Metropolis"));
         }
-        final DefaultFeatureType worldMetropolis = worldMetropolis(metropolis, InternationalString.class);
+        final DefaultFeatureType worldMetropolis = worldMetropolis(metropolis, universityCity, InternationalString.class);
         assertUnmodifiable(worldMetropolis);
         assertEquals     ("name", "World metropolis", worldMetropolis.getName().toString());
-        assertArrayEquals("superTypes", new Object[] {metropolis}, worldMetropolis.getSuperTypes().toArray());
+        assertArrayEquals("superTypes", new Object[] {metropolis, universityCity}, worldMetropolis.getSuperTypes().toArray());
         assertFalse      ("isAbstract",      worldMetropolis.isAbstract());
         assertFalse      ("isSparse",        worldMetropolis.isSparse());
-        assertTrue       ("isSimple",        worldMetropolis.isSimple());
-        assertEquals     ("instanceSize", 4, worldMetropolis.indices().size());
+        assertFalse      ("isSimple",        worldMetropolis.isSimple()); // Because of the arbitrary amount of universities.
+        assertEquals     ("instanceSize", 5, worldMetropolis.indices().size());
 
         assertPropertiesEquals(worldMetropolis, false, "region");
-        assertPropertiesEquals(worldMetropolis, true, "city", "population", "region", "isGlobal");
+        assertPropertiesEquals(worldMetropolis, true, "city", "population", "region", "isGlobal", "universities");
         assertEquals("property(“region”).valueClass", InternationalString.class,
                 ((DefaultAttributeType) worldMetropolis.getProperty("region")).getValueClass());
 

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java?rev=1596975&r1=1596974&r2=1596975&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java [UTF-8] Thu May 22 21:55:23 2014
@@ -41,18 +41,19 @@ public final strictfp class FeatureForma
      */
     @Test
     public void testFeatureType() {
-        final DefaultFeatureType feature = DefaultFeatureTypeTest.metropolis();
+        final DefaultFeatureType feature = DefaultFeatureTypeTest.worldMetropolis();
         final FeatureFormat format = new FeatureFormat(Locale.US, null);
         final String text = format.format(feature);
-        assertMultilinesEquals("Metropolis\n" +
-                "┌────────────┬──────────────┬─────────────┬───────────────┐\n" +
-                "│ Name       │ Type         │ Cardinality │ Default value │\n" +
-                "├────────────┼──────────────┼─────────────┼───────────────┤\n" +
-                "│ city       │ String       │ [1 … 1]     │ Utopia        │\n" +
-                "│ population │ Integer      │ [1 … 1]     │               │\n" +
-                "│ region     │ CharSequence │ [1 … 1]     │               │\n" +
-                "│ isGlobal   │ Boolean      │ [1 … 1]     │               │\n" +
-                "└────────────┴──────────────┴─────────────┴───────────────┘\n", text);
+        assertMultilinesEquals("World metropolis\n" +
+                "┌──────────────┬─────────────────────┬─────────────┬───────────────┐\n" +
+                "│ Name         │ Type                │ Cardinality │ Default value │\n" +
+                "├──────────────┼─────────────────────┼─────────────┼───────────────┤\n" +
+                "│ city         │ String              │ [1 … 1]     │ Utopia        │\n" +
+                "│ population   │ Integer             │ [1 … 1]     │               │\n" +
+                "│ region       │ InternationalString │ [1 … 1]     │               │\n" +
+                "│ isGlobal     │ Boolean             │ [1 … 1]     │               │\n" +
+                "│ universities │ String              │ [0 … ∞]     │               │\n" +
+                "└──────────────┴─────────────────────┴─────────────┴───────────────┘\n", text);
     }
 
     /**

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=1596975&r1=1596974&r2=1596975&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] Thu May 22 21:55:23 2014
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.feature;
 
+import java.util.Collection;
+import java.util.Collections;
 import org.opengis.metadata.quality.DataQuality;
 import org.opengis.metadata.quality.Element;
 import org.opengis.metadata.quality.Result;
@@ -94,14 +96,21 @@ public abstract strictfp class FeatureTe
     private Object getAttributeValue(final String name) {
         final Object value = feature.getPropertyValue(name);
         if (getValuesFromProperty) {
-            final Property property = (Property) feature.getProperty(name);
-            assertInstanceOf(name, AbstractAttribute.class, property);
+            final AbstractAttribute<?> property = (AbstractAttribute<?>) feature.getProperty(name);
 
             // The AttributeType shall be the same than the one provided by FeatureType for the given name.
-            assertSame(name, feature.getType().getProperty(name), ((AbstractAttribute<?>) property).getType());
+            assertSame(name, feature.getType().getProperty(name), property.getType());
 
             // Attribute value shall be the same than the one provided by FeatureType convenience method.
-            assertSame(name, feature.getPropertyValue(name), ((AbstractAttribute<?>) property).getValue());
+            assertSame(name, value, property.getValue());
+
+            // Collection view shall contains the same value, or be empty.
+            final Collection<?> values = property.getValues();
+            if (value != null) {
+                assertSame(name, value, TestUtilities.getSingleton(values));
+            } else {
+                assertTrue(name, values.isEmpty());
+            }
 
             // Invoking getProperty(name) twice shall return the same Property instance.
             assertSame(name, property, feature.getProperty(name));
@@ -174,7 +183,7 @@ public abstract strictfp class FeatureTe
 
     /**
      * Tests {@link AbstractFeature#getProperty(String)} and {@link AbstractFeature#getPropertyValue(String)}
-     * on a "complex" feature, involving inheritance and property overriding.
+     * on a "complex" feature, involving multi-valued properties, inheritances and property overriding.
      */
     @Test
     @DependsOnMethod({"testSimpleValues", "testSimpleProperties"})
@@ -183,6 +192,13 @@ public abstract strictfp class FeatureTe
         setAttributeValue("city", "Utopia", "New York");
         setAttributeValue("population", null, 8405837); // Estimation for 2013.
         /*
+         * Set the attribute value on a property having [0 … ∞] cardinality.
+         * The feature implementation should put the value in a list.
+         */
+        assertEquals("universities", Collections.emptyList(), getAttributeValue("universities"));
+        feature.setPropertyValue("universities", "University of arts");
+        assertEquals("universities", Collections.singletonList("University of arts"), getAttributeValue("universities"));
+        /*
          * 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.
          */
@@ -190,6 +206,13 @@ public abstract strictfp class FeatureTe
         final SimpleInternationalString region = new SimpleInternationalString("State of New York");
         setAttributeValue("region", null, region);
         /*
+         * Adds more universities.
+         */
+        @SuppressWarnings("unchecked")
+        final Collection<String> universities = (Collection<String>) feature.getPropertyValue("universities");
+        assertTrue(universities.add("University of sciences"));
+        assertTrue(universities.add("University of international development"));
+        /*
          * 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.



Mime
View raw message