sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1596919 - in /sis/branches/JDK8/core: sis-feature/src/main/java/org/apache/sis/feature/ sis-feature/src/test/java/org/apache/sis/feature/ sis-feature/src/test/java/org/apache/sis/test/suite/ sis-utility/src/main/java/org/apache/sis/interna...
Date Thu, 22 May 2014 17:04:25 GMT
Author: desruisseaux
Date: Thu May 22 17:04:24 2014
New Revision: 1596919

URL: http://svn.apache.org/r1596919
Log:
Continue on MultiValuedAttribute support.

Added:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
  (with props)
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
  (with props)
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/DenseFeature.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/test/java/org/apache/sis/feature/DefaultAttributeTypeTest.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

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=1596919&r1=1596918&r2=1596919&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 17:04:24 2014
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.feature;
 
+import java.util.Iterator;
+import java.util.Collection;
 import java.io.Serializable;
 import org.opengis.util.GenericName;
 import org.opengis.metadata.quality.DataQuality;
@@ -23,6 +25,7 @@ import org.opengis.metadata.maintenance.
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.CorruptedObjectException;
+import org.apache.sis.internal.util.CheckedArrayList;
 
 
 /**
@@ -197,7 +200,10 @@ public abstract class AbstractFeature im
     final Property createProperty(final String name) throws IllegalArgumentException {
         final PropertyType pt = getPropertyType(name);
         if (pt instanceof DefaultAttributeType<?>) {
-            return new SingletonAttribute<>((DefaultAttributeType<?>) pt);
+            final DefaultAttributeType<?> at = (DefaultAttributeType<?>) pt;
+            return (at.getMaximumOccurs() <= 1)
+                   ? new SingletonAttribute<>(at)
+                   : new MultiValuedAttribute<>(at);
         } else if (pt instanceof DefaultAssociationRole) {
             return new DefaultAssociation((DefaultAssociationRole) pt);
         } else {
@@ -225,13 +231,21 @@ public abstract class AbstractFeature im
 
     /**
      * Returns the value for the property of the given name.
-     * This convenience method is equivalent to the following steps:
+     * 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:
      *
-     * <ul>
-     *   <li>Get the property of the given name.</li>
-     *   <li>Delegates to {@link AbstractAttribute#getValue()} or {@link DefaultAssociation#getValue()},
-     *       depending on the property type.
-     * </ul>
+     * <table class="sis">
+     *   <caption>Class of returned value</caption>
+     *   <tr><th>Property type</th>           <th>max. occurs</th>
<th>Method invoked</th>                  <th>Return type</th></tr>
+     *   <tr><td>{@link AttributeType}</td>   <td>0 or 1</td>
     <td>{@link Attribute#getValue()}</td>    <td>{@link Object}</td></tr>
+     *   <tr><td>{@code AttributeType}</td>   <td>2 or more</td>
  <td>{@link Attribute#getValues()}</td>   <td>{@code Collection<?>}</td></tr>
+     *   <tr><td>{@link AssociationRole}</td> <td>0 or 1</td>
     <td>{@link Association#getValue()}</td>  <td>{@link Feature}</td></tr>
+     *   <tr><td>{@code AssociationRole}</td> <td>2 or more</td>
  <td>{@link Association#getValues()}</td> <td>{@code Collection<Feature>}</td></tr>
+     * </table>
+     *
+     * <div class="note"><b>Note:</b> “max. occurs” is the {@linkplain
DefaultAttributeType#getMaximumOccurs() maximum
+     * number of occurrences} and does not depend on the actual number of values. If an attribute
allows more than one
+     * value, then this method will always return a collection for that attribute even if
the collection is empty.</div>
      *
      * @param  name The property name.
      * @return The value for the given property, or {@code null} if none.
@@ -260,6 +274,14 @@ public abstract class AbstractFeature im
     public abstract void setPropertyValue(final String name, final Object value) throws IllegalArgumentException;
 
     /**
+     * Returns the value of the given attribute, as a singleton or as a collection depending
+     * on the maximum number of occurrences.
+     */
+    static Object getAttributeValue(final AbstractAttribute<?> attribute) {
+        return attribute.getType().getMaximumOccurs() <= 1 ? attribute.getValue() : attribute.getValues();
+    }
+
+    /**
      * Sets the value of the given property, with some minimal checks.
      */
     static void setPropertyValue(final Property property, final Object value) {
@@ -284,8 +306,21 @@ public abstract class AbstractFeature im
             final DefaultAttributeType<T> 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>).
+                     */
+                    final Iterator<?> it = ((Collection<?>) value).iterator();
+                    do if (!it.hasNext()) {
+                        ((AbstractAttribute) attribute).setValues((Collection) value);
+                        return;
+                    } while (base.isInstance(element = it.next()) || element == null);
+                    // Found an illegal value. Exeption is thrown below.
+                }
                 throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
-                        pt.getName(), value.getClass()));
+                        pt.getName(), element.getClass()));
             }
         }
         ((AbstractAttribute) attribute).setValue(value);
@@ -331,33 +366,49 @@ public abstract class AbstractFeature im
     }
 
     /**
-     * Verifies the validity of the given value for the property of the given name. If a
check failed,
-     * returns the exception to throw. Otherwise returns {@code null}. This method does not
throw the
-     * exception immediately in order to give to the caller a chance to perform cleanup operation
first.
+     * 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 RuntimeException verifyValueType(final String name, final Object value) {
+    final Object verifyValueType(final String name, final Object value) {
         final PropertyType pt = getPropertyType(name);
+        /*
+         * 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 (pt instanceof DefaultAttributeType<?>) {
-            if (value == null || ((DefaultAttributeType<?>) pt).getValueClass().isInstance(value))
{
-                return null;
-            }
-        } else if (pt instanceof DefaultAssociationRole) {
-            if (value == null) {
-                return null;
+            if (value != null) {
+                final Class<?> valueClass = ((DefaultAttributeType<?>) pt).getValueClass();
+                if (!valueClass.isInstance(value)) {
+                    if (value instanceof Collection<?>) {
+                        return CheckedArrayList.wrapOrCopy((Collection<?>) value, valueClass);
+                    }
+                    throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
+                            name, value.getClass()));
+                }
             }
-            if (value instanceof AbstractFeature) {
-                final DefaultFeatureType valueType = ((AbstractFeature) value).getType();
-                if (((DefaultAssociationRole) pt).getValueType().maybeAssignableFrom(valueType))
{
-                    return null;
+            return value;
+        }
+        /*
+         * Association:
+         *   - May be a singleton,  in which case the feature type is verified.
+         */
+        if (pt instanceof DefaultAssociationRole) {
+            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 {
+                    throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
+                            name, value.getClass()));
                 }
-                return new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
-                        name, valueType.getName()));
             }
-        } else {
-            return new IllegalArgumentException(unsupportedPropertyType(pt.getName()));
+            return value;
         }
-        return new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
-                name, value.getClass())); // 'value' should not be null at this point.
+        throw new IllegalArgumentException(unsupportedPropertyType(pt.getName()));
     }
 
     /**

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=1596919&r1=1596918&r2=1596919&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 17:04:24 2014
@@ -163,7 +163,7 @@ final class DenseFeature extends Abstrac
                 if (!(properties instanceof Property[])) {
                     return element; // Most common case.
                 } else if (element instanceof AbstractAttribute<?>) {
-                    return ((AbstractAttribute<?>) element).getValue();
+                    return getAttributeValue((AbstractAttribute<?>) element);
                 } else if (element instanceof DefaultAssociation) {
                     return ((DefaultAssociation) element).getValue();
                 } else {
@@ -183,7 +183,7 @@ final class DenseFeature extends Abstrac
      * @throws IllegalArgumentException If the given value can not be assigned for an other
reason.
      */
     @Override
-    public void setPropertyValue(final String name, final Object value) throws IllegalArgumentException
{
+    public void setPropertyValue(final String name, Object value) throws IllegalArgumentException
{
         ArgumentChecks.ensureNonNull("name", name);
         final int index = getIndex(name);
         if (properties == null) {
@@ -193,10 +193,7 @@ final class DenseFeature extends Abstrac
         if (!(properties instanceof Property[])) {
             if (value != null) {
                 if (!canSkipVerification(properties[index], value)) {
-                    final RuntimeException e = verifyValueType(name, value);
-                    if (e != null) {
-                        throw e;
-                    }
+                    value = verifyValueType(name, value);
                 }
                 properties[index] = value;
                 return;

Added: 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=1596919&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
(added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.feature;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.lang.reflect.Field;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * An instance of an {@linkplain DefaultAttributeType attribute type} containing an arbitrary
amount of values.
+ *
+ * <div class="note"><b>Note:</b> in the common case where the {@linkplain
DefaultAttributeType attribute type}
+ * restricts the cardinality to [0 … 1], the {@link SingletonAttribute} implementation
consumes less memory.</div>
+ *
+ * {@section Limitations}
+ * <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>
+ * </ul>
+ *
+ * @param <T> The type of the attribute values.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ *
+ * @see DefaultAttributeType
+ */
+final class MultiValuedAttribute<T> extends AbstractAttribute<T> implements Cloneable
{
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7824265855672575215L;
+
+    /**
+     * The attribute values.
+     */
+    private final ArrayList<T> values;
+
+    /**
+     * Creates a new attribute of the given type initialized to the
+     * {@linkplain DefaultAttributeType#getDefaultValue() default value}.
+     *
+     * @param type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     */
+    public MultiValuedAttribute(final DefaultAttributeType<T> type) {
+        super(type);
+        values = new ArrayList<>();
+        final T value = type.getDefaultValue();
+        if (value != null) {
+            values.add(value);
+        }
+    }
+
+    /**
+     * 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() {
+        switch (values.size()) {
+            case 0:  return null;
+            case 1:  return values.get(0);
+            default: throw new IllegalStateException(Errors.format(Errors.Keys.NotASingleton_1,
getName()));
+        }
+    }
+
+    /**
+     * Returns all attribute values, or an empty collection if none.
+     * The returned collection is <cite>live</cite>: changes in the returned
collection
+     * will be reflected immediately in this {@code Attribute} instance, and conversely.
+     *
+     * @return The attribute values in a <cite>live</cite> collection.
+     */
+    @Override
+    public Collection<T> getValues() {
+        return values;
+    }
+
+    /**
+     * Sets the attribute value.
+     *
+     * @param value The new value, or {@code null} for removing all values from this attribute.
+     */
+    @Override
+    public void setValue(final T value) {
+        values.clear();
+        if (value != null) {
+            values.add(value);
+        }
+    }
+
+    /**
+     * Set the attribute values. All previous values are replaced by the given collection.
+     *
+     * @param values The new values.
+     */
+    @Override
+    public void setValues(final Collection<? extends T> values) {
+        ArgumentChecks.ensureNonNull("values", values);
+        this.values.clear();
+        this.values.addAll(values);
+    }
+
+    /**
+     * Returns a copy of this attribute.
+     * The default implementation returns a <em>shallow</em> copy:
+     * the attribute {@linkplain #getValues() values} are <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 values.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public MultiValuedAttribute<T> clone() throws CloneNotSupportedException {
+        final MultiValuedAttribute<T> clone = (MultiValuedAttribute<T>) super.clone();
+        try {
+            final Field field = MultiValuedAttribute.class.getDeclaredField("values");
+            field.setAccessible(true);
+            field.set(clone, clone.values.clone());
+        } catch (ReflectiveOperationException e) {
+            throw new AssertionError(e);
+        }
+        return clone;
+    }
+
+    /**
+     * Returns a hash code value for this attribute type.
+     *
+     * @return A hash code value.
+     */
+    @Override
+    public int hashCode() {
+        return type.hashCode() + values.hashCode();
+    }
+
+    /**
+     * Compares this attribute with the given object for equality.
+     *
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof MultiValuedAttribute<?>) {
+            final MultiValuedAttribute<?> that = (MultiValuedAttribute<?>) obj;
+            return type.equals(that.type) && values.equals(that.values);
+        }
+        return false;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

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=1596919&r1=1596918&r2=1596919&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 17:04:24 2014
@@ -61,6 +61,7 @@ final class SingletonAttribute<T> extend
      */
     public SingletonAttribute(final DefaultAttributeType<T> type) {
         super(type);
+        assert type.getMaximumOccurs() <= 1;
         value = type.getDefaultValue();
     }
 
@@ -73,6 +74,7 @@ final class SingletonAttribute<T> extend
      */
     SingletonAttribute(final DefaultAttributeType<T> type, final Object value) {
         super(type);
+        assert type.getMaximumOccurs() <= 1;
         this.value = type.getValueClass().cast(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=1596919&r1=1596918&r2=1596919&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 17:04:24 2014
@@ -177,7 +177,7 @@ final class SparseFeature extends Abstra
             if (valuesKind == VALUES) {
                 return element; // Most common case.
             } else if (element instanceof AbstractAttribute<?>) {
-                return ((AbstractAttribute<?>) element).getValue();
+                return getAttributeValue((AbstractAttribute<?>) element);
             } else if (element instanceof DefaultAssociation) {
                 return ((DefaultAssociation) element).getValue();
             } else if (valuesKind == PROPERTIES) {
@@ -211,10 +211,13 @@ final class SparseFeature extends Abstra
              * a new value or a value of a different type, then we need to check the name
and type validity.
              */
             if (!canSkipVerification(previous, value)) {
-                final RuntimeException e = verifyValueType(name, value);
-                if (e != null) {
-                    replace(name, value, previous); // Restore the previous value.
-                    throw e;
+                Object toStore = previous; // This initial value will restore the previous
value if the check fail.
+                try {
+                    toStore = verifyValueType(name, value);
+                } finally {
+                    if (toStore != value) {
+                        replace(name, value, toStore);
+                    }
                 }
             }
         } else if (valuesKind == PROPERTIES) {

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTypeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTypeTest.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTypeTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTypeTest.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -42,6 +42,15 @@ public final strictfp class DefaultAttri
     /**
      * Creates an attribute type for city name.
      *
+     * @return An attribute type for a city name.
+     */
+    public static DefaultAttributeType<String> city() {
+        return city(new HashMap<>());
+    }
+
+    /**
+     * Implementation of {@link #city()} using the given map (for reusing existing objects).
+     *
      * @param identification An empty temporary map (provided only for recycling existing
instances).
      */
     static DefaultAttributeType<String> city(final Map<String,Object> identification)
{
@@ -66,6 +75,8 @@ public final strictfp class DefaultAttri
      */
     static DefaultAttributeType<Integer> population(final Map<String,Object>
identification) {
         assertNull(identification.put(DefaultAttributeType.NAME_KEY, "population"));
+        // We may add more properties here in a future version.
+
         final DefaultAttributeType<Integer> population = new DefaultAttributeType<>(
                 identification, Integer.class, 1, 1, null);
         identification.clear();
@@ -74,10 +85,27 @@ public final strictfp class DefaultAttri
 
     /**
      * Creates an attribute type for a parliament name.
+     * This applies only to features of type "Capital".
+     * This is used for testing feature type inheritance.
+     *
+     * @return An attribute type for the name of the parliament in a capital.
+     */
+    public static DefaultAttributeType<String> parliament() {
+        return new DefaultAttributeType<>(
+                singletonMap(DefaultAttributeType.NAME_KEY, "parliament"),
+                String.class, 1, 1, null);
+    }
+
+    /**
+     * Creates an attribute type for a list of universities.
+     * The cardinality is [0 … ∞].
+     *
+     * @return An attribute type for university names.
      */
-    static DefaultAttributeType<String> parliament() {
-        return new DefaultAttributeType<>(singletonMap(DefaultAttributeType.NAME_KEY,
"parliament"),
-                        String.class, 1, 1, null);
+    public static DefaultAttributeType<String> universities() {
+        return new DefaultAttributeType<>(
+                singletonMap(DefaultAttributeType.NAME_KEY, "universities"),
+                String.class, 0, Integer.MAX_VALUE, null);
     }
 
     /**
@@ -85,7 +113,7 @@ public final strictfp class DefaultAttri
      */
     @Test
     public void testMandatorySingleton() {
-        final DefaultAttributeType<String> city = city(new HashMap<>());
+        final DefaultAttributeType<String> city = city();
         final GenericName name = city.getName();
         assertInstanceOf("city.name", LocalName.class, name);
         assertEquals("city.name", "city", name.toString());
@@ -129,7 +157,7 @@ public final strictfp class DefaultAttri
     @Test
     @DependsOnMethod("testEquals")
     public void testSerialization() {
-        final DefaultAttributeType<String> attribute = city(new HashMap<>(4));
+        final DefaultAttributeType<String> attribute = city();
         assertSerializedEquals(attribute);
     }
 
@@ -138,7 +166,7 @@ public final strictfp class DefaultAttri
      */
     @Test
     public void testToString() {
-        final DefaultAttributeType<String> city = city(new HashMap<>());
+        final DefaultAttributeType<String> city = city();
         assertEquals("AttributeType[“city” : String]", city.toString());
     }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.feature;
 
-import java.util.HashMap;
 import org.opengis.parameter.ParameterDescriptor;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.test.DependsOn;
@@ -47,7 +46,7 @@ public final strictfp class DefaultOpera
         };
         return new DefaultOperation(singletonMap(DefaultOperation.NAME_KEY, "found city"),
                 builder.addName("found city").createGroup(parameters),
-                DefaultAttributeTypeTest.city(new HashMap<>(4)));
+                DefaultAttributeTypeTest.city());
     }
 
     /**

Added: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java?rev=1596919&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
(added)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.feature;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.DependsOnMethod;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests {@link MultiValuedAttribute}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+@DependsOn(SingletonAttributeTest.class)
+public final strictfp class MultiValuedAttributeTest extends TestCase {
+    /**
+     * Creates an attribute for a list of universities.
+     * The cardinality is [0 … ∞].
+     */
+    static MultiValuedAttribute<String> universities() {
+        return new MultiValuedAttribute<>(DefaultAttributeTypeTest.universities());
+    }
+
+    /**
+     * Creates an attribute for a city population value.
+     * This attribute has no default value.
+     */
+    private static MultiValuedAttribute<Integer> population() {
+        return new MultiValuedAttribute<>(DefaultAttributeTypeTest.population(new HashMap<>(4)));
+    }
+
+    /**
+     * Tests getting and setting a single attribute value.
+     */
+    @Test
+    public void testValue() {
+        final AbstractAttribute<String> attribute = universities();
+        assertNull("value",  attribute.getValue());
+        assertTrue("values", attribute.getValues().isEmpty());
+
+        final String   value  = "University of arts";
+        final String[] values = {value};
+
+        attribute.setValue(value);
+        assertEquals     ("value",  value,  attribute.getValue());
+        assertArrayEquals("values", values, attribute.getValues().toArray());
+
+        attribute.setValue(null);
+        assertNull("value",  attribute.getValue());
+        assertTrue("values", attribute.getValues().isEmpty());
+
+        attribute.setValues(Arrays.asList(values));
+        assertEquals     ("value",  value,  attribute.getValue());
+        assertArrayEquals("values", values, attribute.getValues().toArray());
+    }
+
+    /**
+     * Tests getting and setting multiple attribute values.
+     */
+    @Test
+    @DependsOnMethod("testValue")
+    public void testValues() {
+        final AbstractAttribute<String> attribute = universities();
+        final String[] values = {
+            "University of arts",
+            "University of sciences",
+            "University of international development"
+        };
+        attribute.setValues(Arrays.asList(values));
+        assertArrayEquals("values", values, attribute.getValues().toArray());
+        try {
+            attribute.getValue();
+            fail("getValue() shall not be allowed when there is more than one value.");
+        } catch (IllegalStateException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("universities"));
+        }
+    }
+
+    /**
+     * Tests initialization to the default value.
+     */
+    @Test
+    @DependsOnMethod("testValue")
+    public void testDefaultValue() {
+        final AbstractAttribute<String> attribute = new MultiValuedAttribute<>(DefaultAttributeTypeTest.city());
+        assertEquals     ("value",                "Utopia",  attribute.getValue());
+        assertArrayEquals("values", new String[] {"Utopia"}, attribute.getValues().toArray());
+
+        attribute.setValue("Atlantide");
+        assertEquals     ("value",                "Atlantide",  attribute.getValue());
+        assertArrayEquals("values", new String[] {"Atlantide"}, attribute.getValues().toArray());
+
+        attribute.setValue(null);
+        assertNull("value",  attribute.getValue());
+        assertTrue("values", attribute.getValues().isEmpty());
+    }
+
+    /**
+     * Tests attribute comparison.
+     */
+    @Test
+    @DependsOnMethod("testValue")
+    public void testEquals() {
+        final AbstractAttribute<Integer> a1 = population();
+        final AbstractAttribute<Integer> a2 = population();
+        assertFalse("equals(null)", a1.equals(null));
+        SingletonAttributeTest.testEquals(a1, a2);
+    }
+
+    /**
+     * Tests {@link MultiValuedAttribute#clone()}.
+     *
+     * @throws CloneNotSupportedException Should never happen.
+     */
+    @Test
+    @DependsOnMethod("testEquals")
+    public void testClone() throws CloneNotSupportedException {
+        final MultiValuedAttribute<Integer> a1 = population();
+        final MultiValuedAttribute<Integer> a2 = a1.clone();
+        assertNotSame(a1, a2);
+        SingletonAttributeTest.testEquals(a1, a2);
+    }
+
+    /**
+     * Tests serialization.
+     */
+    @Test
+    @DependsOnMethod("testEquals")
+    public void testSerialization() {
+        final AbstractAttribute<String> attribute = universities();
+        attribute.setValue("University of international development");
+        assertNotSame(attribute, assertSerializedEquals(attribute));
+    }
+}

Propchange: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -18,7 +18,6 @@ package org.apache.sis.feature;
 
 import java.util.Set;
 import org.apache.sis.test.DependsOnMethod;
-import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -36,7 +35,6 @@ import static org.apache.sis.test.TestUt
  * @version 0.5
  * @module
  */
-@DependsOn(SingletonAttributeTest.class)
 public final strictfp class PropertySingletonTest extends TestCase {
     /**
      * The instance to test.

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -39,18 +39,21 @@ import static org.apache.sis.test.TestUt
  * @version 0.5
  * @module
  */
-@DependsOn(DefaultAttributeTypeTest.class)
+@DependsOn({
+    DefaultAttributeTypeTest.class,
+    PropertySingletonTest.class
+})
 public final strictfp class SingletonAttributeTest extends TestCase {
     /**
      * Creates an attribute for the city name.
      * This attribute has a default value.
      */
     static SingletonAttribute<String> city() {
-        return new SingletonAttribute<>(DefaultAttributeTypeTest.city(new HashMap<>(4)));
+        return new SingletonAttribute<>(DefaultAttributeTypeTest.city());
     }
 
     /**
-     * Creates an attribute for a singleton value.
+     * Creates an attribute for a city population value.
      * This attribute has no default value.
      */
     static SingletonAttribute<Integer> population() {
@@ -58,8 +61,8 @@ public final strictfp class SingletonAtt
     }
 
     /**
-     * Creates an attribute for a singleton value.
-     * This attribute has no default value.
+     * Creates an attribute type for a parliament name.
+     * This applies only to features of type "Capital".
      */
     static SingletonAttribute<String> parliament() {
         return new SingletonAttribute<>(DefaultAttributeTypeTest.parliament());
@@ -71,11 +74,17 @@ public final strictfp class SingletonAtt
     @Test
     public void testValue() {
         final AbstractAttribute<Integer> attribute = population();
-        assertNull("value", attribute.getValue());
-        attribute.setValue(1000);
-        assertEquals("value", Integer.valueOf(1000), attribute.getValue());
+        assertNull("value",  attribute.getValue());
+        assertTrue("values", attribute.getValues().isEmpty());
+
+        final Integer value = 1000;
+        attribute.setValue(value);
+        assertEquals     ("value",                 value,  attribute.getValue());
+        assertArrayEquals("values", new Integer[] {value}, attribute.getValues().toArray());
+
         attribute.setValue(null);
-        assertNull("value", attribute.getValue());
+        assertNull("value",  attribute.getValue());
+        assertTrue("values", attribute.getValues().isEmpty());
     }
 
     /**
@@ -120,6 +129,7 @@ public final strictfp class SingletonAtt
      * Tests attribute comparison.
      */
     @Test
+    @DependsOnMethod("testValue")
     public void testEquals() {
         final AbstractAttribute<Integer> a1 = population();
         final AbstractAttribute<Integer> a2 = population();
@@ -130,7 +140,7 @@ public final strictfp class SingletonAtt
     /**
      * Implementation of {@link #testEquals()} used also by {@link #testClone()}.
      */
-    private static void testEquals(final AbstractAttribute<Integer> a1, final AbstractAttribute<Integer>
a2) {
+    static void testEquals(final AbstractAttribute<Integer> a1, final AbstractAttribute<Integer>
a2) {
         assertTrue  ("equals",   a1.equals(a2));
         assertEquals("hashCode", a1.hashCode(), a2.hashCode());
         a2.setValue(1000);
@@ -159,6 +169,7 @@ public final strictfp class SingletonAtt
     @DependsOnMethod("testEquals")
     public void testSerialization() {
         final AbstractAttribute<String> attribute = city();
+        attribute.setValue("Atlantide");
         assertNotSame(attribute, assertSerializedEquals(attribute));
     }
 

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -32,8 +32,9 @@ import org.junit.BeforeClass;
 @Suite.SuiteClasses({
     org.apache.sis.feature.DefaultAttributeTypeTest.class,
     org.apache.sis.feature.DefaultFeatureTypeTest.class,
-    org.apache.sis.feature.SingletonAttributeTest.class,
     org.apache.sis.feature.PropertySingletonTest.class,
+    org.apache.sis.feature.SingletonAttributeTest.class,
+    org.apache.sis.feature.MultiValuedAttributeTest.class,
     org.apache.sis.feature.DenseFeatureTest.class,
     org.apache.sis.feature.SparseFeatureTest.class,
     org.apache.sis.feature.DefaultAssociationRoleTest.class,

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -90,6 +90,31 @@ public final class CheckedArrayList<E> e
     }
 
     /**
+     * Returns the given collection as a {@code CheckedArrayList} instance of the given element
type.
+     *
+     * @param  <E>        The element type.
+     * @param  collection The collection or {@code null}.
+     * @param  type       The element type.
+     * @return The given collection as a {@code CheckedArrayList}, or {@code null} if the
given collection was null.
+     * @throws ClassCastException if an element is not of the expected type.
+     *
+     * @since 0.5
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> CheckedArrayList<E> wrapOrCopy(final Collection<?>
collection, final Class<E> type) {
+        if (collection == null) {
+            return null;
+        }
+        if (collection instanceof CheckedArrayList<?> && ((CheckedArrayList<?>)
collection).type == type) {
+            return (CheckedArrayList<E>) collection;
+        } else {
+            final CheckedArrayList<E> list = new CheckedArrayList<>(type, collection.size());
+            list.addAll((Collection) collection); // addAll will perform the type checks.
+            return list;
+        }
+    }
+
+    /**
      * Returns the element type given at construction time.
      */
     @Override

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
[UTF-8] Thu May 22 17:04:24 2014
@@ -593,6 +593,11 @@ public final class Errors extends Indexe
         public static final short NotAPrimitiveWrapper_1 = 90;
 
         /**
+         * The “{0}” collection is not a singleton.
+         */
+        public static final short NotASingleton_1 = 158;
+
+        /**
          * Matrix is not skew-symmetric.
          */
         public static final short NotASkewSymmetricMatrix = 91;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
[ISO-8859-1] Thu May 22 17:04:24 2014
@@ -128,6 +128,7 @@ NonUniformScale                   = Scal
 NotAnAffineTransform              = Transform is not affine.
 NotANumber_1                      = Argument \u2018{0}\u2019 shall not be NaN (Not-a-Number).
 NotAPrimitiveWrapper_1            = Class \u2018{0}\u2019 is not a primitive type wrapper.
+NotASingleton_1                   = The \u201c{0}\u201d collection is not a singleton.
 NotASkewSymmetricMatrix           = Matrix is not skew-symmetric.
 NotAUnicodeIdentifier_1           = Text \u201c{0}\u201d is not a Unicode identifier.
 NotComparableClass_1              = Class \u2018{0}\u2019 is not a comparable.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1596919&r1=1596918&r2=1596919&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
[ISO-8859-1] Thu May 22 17:04:24 2014
@@ -118,6 +118,7 @@ NonUniformScale                   = L\u2
 NotAnAffineTransform              = La transformation n\u2019est pas affine.
 NotANumber_1                      = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre
NaN (Not-a-Number).
 NotAPrimitiveWrapper_1            = La classe \u2018{0}\u2019 n\u2019est pas un adaptateur
d\u2019un type primitif.
+NotASingleton_1                   = La collection de \u00ab\u202f{0}\u202f\u00bb n\u2019est
pas un singleton.
 NotASkewSymmetricMatrix           = La matrice n\u2019est pas antisym\u00e9trique.
 NotAUnicodeIdentifier_1           = Le texte \u00ab\u202f{0}\u202f\u00bb n\u2019est pas un
identifiant Unicode.
 NotComparableClass_1              = La classe \u2018{0}\u2019 n\u2019est pas comparable.



Mime
View raw message