sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1754559 [1/2] - 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/u...
Date Fri, 29 Jul 2016 17:53:10 GMT
Author: desruisseaux
Date: Fri Jul 29 17:53:09 2016
New Revision: 1754559

URL: http://svn.apache.org/viewvc?rev=1754559&view=rev
Log:
Provide more default implementations in AbstractMethod methods, for making easier the task of developers who want to create their own subtypes.

Added:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AssociationView.java   (with props)
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AttributeView.java   (with props)
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java   (with props)
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/AbstractFeatureTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.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/FeatureTestCase.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/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/AbstractAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java?rev=1754559&r1=1754558&r2=1754559&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -69,7 +69,7 @@ public abstract class AbstractAssociatio
     /**
      * Creates a new association of the given role.
      *
-     * @param role Information about the association.
+     * @param role  information about the association.
      *
      * @see #create(FeatureAssociationRole)
      */
@@ -80,8 +80,8 @@ public abstract class AbstractAssociatio
     /**
      * Creates a new association of the given role.
      *
-     * @param  role Information about the association.
-     * @return The new association.
+     * @param  role  information about the association.
+     * @return the new association.
      *
      * @see DefaultAssociationRole#newInstance()
      */
@@ -95,9 +95,9 @@ public abstract class AbstractAssociatio
     /**
      * Creates a new association of the given role initialized to the given value.
      *
-     * @param  role  Information about the association.
-     * @param  value The initial value (may be {@code null}).
-     * @return The new association.
+     * @param  role   information about the association.
+     * @param  value  the initial value (may be {@code null}).
+     * @return the new association.
      */
     static AbstractAssociation create(final FeatureAssociationRole role, final Object value) {
         ArgumentChecks.ensureNonNull("role", role);
@@ -110,7 +110,7 @@ public abstract class AbstractAssociatio
      * Returns the name of this association as defined by its {@linkplain #getRole() role}.
      * This convenience method delegates to {@link FeatureAssociationRole#getName()}.
      *
-     * @return The association name specified by its role.
+     * @return the association name specified by its role.
      */
     @Override
     public GenericName getName() {
@@ -120,7 +120,7 @@ public abstract class AbstractAssociatio
     /**
      * Returns information about the association.
      *
-     * @return Information about the association.
+     * @return information about the association.
      */
     @Override
     public FeatureAssociationRole getRole() {
@@ -132,7 +132,7 @@ public abstract class AbstractAssociatio
      * the common case where the {@linkplain DefaultAssociationRole#getMaximumOccurs() maximum number} of
      * features is restricted to 1 or 0.
      *
-     * @return The associated feature (may be {@code null}).
+     * @return the associated feature (may be {@code null}).
      * @throws MultiValuedPropertyException if this association contains more than one value.
      *
      * @see AbstractFeature#getPropertyValue(String)
@@ -148,7 +148,7 @@ public abstract class AbstractAssociatio
      * <p>The default implementation returns a collection which will delegate its work to
      * {@link #getValue()} and {@link #setValue(Object)}.</p>
      *
-     * @return The features in a <cite>live</cite> collection.
+     * @return the features in a <cite>live</cite> collection.
      */
     @Override
     public Collection<Feature> getValues() {
@@ -164,8 +164,8 @@ public abstract class AbstractAssociatio
      * and also because some rules may be temporarily broken while constructing a feature.
      * A more exhaustive verification can be performed by invoking the {@link #quality()} method.
      *
-     * @param  value The new value, or {@code null}.
-     * @throws InvalidPropertyValueException If the given feature is not valid for this association.
+     * @param  value  the new value, or {@code null}.
+     * @throws InvalidPropertyValueException if the given feature is not valid for this association.
      *
      * @see AbstractFeature#setPropertyValue(String, Object)
      */
@@ -178,7 +178,7 @@ public abstract class AbstractAssociatio
      * <p>The default implementation ensures that the given collection contains at most one element,
      * then delegates to {@link #setValue(Feature)}.</p>
      *
-     * @param  values The new values.
+     * @param  values  the new values.
      * @throws InvalidPropertyValueException if the given collection contains too many elements.
      */
     @Override
@@ -208,7 +208,7 @@ public abstract class AbstractAssociatio
      * {@linkplain org.apache.sis.metadata.iso.quality.DefaultConformanceResult conformance result} having a
      * {@linkplain org.apache.sis.metadata.iso.quality.DefaultConformanceResult#pass() pass} value of {@code false}.</p>
      *
-     * @return Reports on all constraint violations found.
+     * @return reports on all constraint violations found.
      *
      * @see AbstractFeature#quality()
      */
@@ -222,14 +222,16 @@ public abstract class AbstractAssociatio
      * Returns a string representation of this association.
      * The returned string is for debugging purpose and may change in any future SIS version.
      *
-     * @return A string representation of this association for debugging purpose.
+     * @return a string representation of this association for debugging purpose.
      */
     @Debug
     @Override
     public String toString() {
         final String pt = DefaultAssociationRole.getTitleProperty(role);
         final Iterator<Feature> it = getValues().iterator();
-        return FieldType.toString("FeatureAssociation", role, DefaultAssociationRole.getValueTypeName(role), new Iterator<Object>() {
+        return FieldType.toString("FeatureAssociation", role.getName(),
+                DefaultAssociationRole.getValueTypeName(role), new Iterator<Object>()
+        {
             @Override public boolean hasNext() {
                 return it.hasNext();
             }
@@ -246,7 +248,7 @@ public abstract class AbstractAssociatio
      * the association {@linkplain #getValue() value} is <strong>not</strong> cloned.
      * However subclasses may choose to do otherwise.
      *
-     * @return A clone of this association.
+     * @return a clone of this association.
      * @throws CloneNotSupportedException if this association can not be cloned.
      *         The default implementation never throw this exception. However subclasses may throw it.
      */

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=1754559&r1=1754558&r2=1754559&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] Fri Jul 29 17:53:09 2016
@@ -464,7 +464,7 @@ public abstract class AbstractAttribute<
     @Debug
     @Override
     public String toString() {
-        final StringBuilder buffer = FieldType.toString("Attribute", type,
+        final StringBuilder buffer = FieldType.toString("Attribute", type.getName(),
                 Classes.getShortName(type.getValueClass()), getValues().iterator());
         if (characteristics != null && !characteristics.isEmpty()) {
             buffer.append(System.lineSeparator());

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=1754559&r1=1754558&r2=1754559&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] Fri Jul 29 17:53:09 2016
@@ -25,10 +25,12 @@ import org.opengis.metadata.quality.Data
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.CorruptedObjectException;
 import org.apache.sis.internal.util.CheckedArrayList;
 
 // Branch-dependent imports
+import java.util.Objects;
 import org.opengis.feature.Property;
 import org.opengis.feature.PropertyType;
 import org.opengis.feature.PropertyNotFoundException;
@@ -74,7 +76,7 @@ import org.opengis.feature.Operation;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  *
  * @see DefaultFeatureType#newInstance()
@@ -93,7 +95,7 @@ public abstract class AbstractFeature im
     /**
      * Creates a new feature of the given type.
      *
-     * @param type Information about the feature (name, characteristics, <i>etc.</i>).
+     * @param type  information about the feature (name, characteristics, <i>etc.</i>).
      *
      * @see DefaultFeatureType#newInstance()
      */
@@ -113,7 +115,7 @@ public abstract class AbstractFeature im
     /**
      * Returns information about the feature (name, characteristics, <i>etc.</i>).
      *
-     * @return Information about the feature.
+     * @return information about the feature.
      */
     @Override
     public FeatureType getType() {
@@ -130,15 +132,31 @@ public abstract class AbstractFeature im
      * <em>value</em> is desired, then {@link #getPropertyValue(String)} is preferred since it gives to SIS a
      * chance to avoid the creation of {@link AbstractAttribute} or {@link AbstractAssociation} instances.</div>
      *
-     * @param  name The property name.
-     * @return The property of the given name (never {@code null}).
+     * <div class="note"><b>Note for subclass implementors:</b>
+     * the default implementation returns an instance that redirect all read and write operations to
+     * {@link #getPropertyValue(String)} and {@link #setPropertyValue(String, Object)} respectively.
+     * That default implementation is intended to make easier for developers to create their own
+     * customized <code>AbstractFacture</code> implementations, but has drawbacks:
+     *
+     * <ul>
+     *   <li>A new {@code Property} instance is created every time that this {@code getProperty(String)} method is invoked.</li>
+     *   <li>The returned {@code Property} implementation is not very efficient since it has to perform multiple lookups and type checks.</li>
+     * </ul>
+     *
+     * Implementors are encouraged to override this method if they can provide a more efficient implementation.
+     * Note that this is already the case when using implementations created by {@link DefaultFeatureType#newInstance()}.</div>
+     *
+     * @param  name  the property name.
+     * @return the property of the given name (never {@code null}).
      * @throws PropertyNotFoundException if the given argument is not a property name of this feature.
      *
      * @see #getPropertyValue(String)
      * @see DefaultFeatureType#getProperty(String)
      */
     @Override
-    public abstract Property getProperty(final String name) throws PropertyNotFoundException;
+    public Property getProperty(final String name) throws PropertyNotFoundException {
+        return PropertyView.create(this, type.getProperty(name));
+    }
 
     /**
      * Sets the property (attribute or feature association).
@@ -160,7 +178,22 @@ public abstract class AbstractFeature im
      * {@code FeatureAssociation} implementations in this feature. When default implementations are sufficient,
      * the {@link #setPropertyValue(String, Object)} method is preferred.</div>
      *
-     * @param  property The property to set.
+     * <div class="note"><b>Note for subclass implementors:</b>
+     * the default implementation verifies that the given property has the expected type and a null or empty
+     * {@linkplain AbstractAttribute#characteristics() map of characteristics}, then delegates to
+     * {@link #setPropertyValue(String, Object)}.
+     * That default implementation is intended to make easier for developers to create their own
+     * customized <code>AbstractFacture</code> implementations, but has drawbacks:
+     *
+     * <ul>
+     *   <li>The given {@code Property} instance is not retained; only its {@linkplain AbstractAttribute#getValue() value} is stored.</li>
+     *   <li>The given {@code Property} instance can not have custom {@linkplain AbstractAttribute#characteristics() characteristics}.</li>
+     * </ul>
+     *
+     * Implementors are encouraged to override this method if they can provide a better implementation.
+     * Note that this is already the case when using implementations created by {@link DefaultFeatureType#newInstance()}.</div>
+     *
+     * @param  property  the property to set.
      * @throws PropertyNotFoundException if the name of the given property is not a property name of this feature.
      * @throws InvalidPropertyValueException if the value of the given property is not valid.
      * @throws IllegalArgumentException if the property can not be set for another reason.
@@ -168,14 +201,22 @@ public abstract class AbstractFeature im
      * @see #setPropertyValue(String, Object)
      */
     @Override
-    public abstract void setProperty(final Property property) throws IllegalArgumentException;
+    public void setProperty(final Property property) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("property", property);
+        final String name = property.getName().toString();
+        verifyPropertyType(name, property);
+        if (property instanceof Attribute<?> && !Containers.isNullOrEmpty(((Attribute<?>) property).characteristics())) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.CanNotAssignCharacteristics_1, name));
+        }
+        setPropertyValue(name, property.getValue());
+    }
 
     /**
      * Wraps the given value in a {@link Property} object. This method is invoked only by
      * {@link #getProperty(String)} when it needs to converts its {@code properties} data.
      *
-     * @param  name  The name of the property to create.
-     * @param  value The value to wrap.
+     * @param  name   the name of the property to create.
+     * @param  value  the value to wrap.
      * @return A {@code Property} wrapping the given value.
      */
     final Property createProperty(final String name, final Object value) {
@@ -193,8 +234,8 @@ public abstract class AbstractFeature im
     /**
      * Creates a new property initialized to its default value.
      *
-     * @param  name The name of the property to create.
-     * @return A {@code Property} of the given name.
+     * @param  name  the name of the property to create.
+     * @return a {@code Property} of the given name.
      * @throws PropertyNotFoundException if the given argument is not the name of an attribute or
      *         feature association of this feature.
      */
@@ -261,8 +302,8 @@ public abstract class AbstractFeature im
      * 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.
+     * @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.
      * @throws PropertyNotFoundException if the given argument is not an attribute or association name of this feature.
      */
     final Object getDefaultValue(final String name) throws PropertyNotFoundException {
@@ -308,8 +349,8 @@ public abstract class AbstractFeature im
      * 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.
+     * @param  name  the property name.
+     * @return the value for the given property, or {@code null} if none.
      * @throws PropertyNotFoundException if the given argument is not an attribute or association name of this feature.
      *
      * @see AbstractAttribute#getValue()
@@ -326,8 +367,8 @@ public abstract class AbstractFeature im
      * and also because some rules may be temporarily broken while constructing a feature.
      * A more exhaustive verification can be performed by invoking the {@link #quality()} method.
      *
-     * @param  name  The attribute name.
-     * @param  value The new value for the given attribute (may be {@code null}).
+     * @param  name   the attribute name.
+     * @param  value  the new value for the given attribute (may be {@code null}).
      * @throws PropertyNotFoundException if the given name is not an attribute or association name of this feature.
      * @throws ClassCastException if the value is not assignable to the expected value class.
      * @throws InvalidPropertyValueException if the given value is not valid for a reason other than its type.
@@ -427,9 +468,9 @@ public abstract class AbstractFeature im
      * 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 verifyPropertyValue}.
+     * @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 verifyPropertyValue}.
      */
     static boolean canSkipVerification(final Object previous, final Object value) {
         if (previous != null) {
@@ -446,8 +487,8 @@ public abstract class AbstractFeature im
     /**
      * Verifies if the given property can be assigned to this feature.
      *
-     * @param name Shall be {@code property.getName().toString()}.
-     * @param property The property to verify.
+     * @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 pt, base = type.getProperty(name);
@@ -495,7 +536,7 @@ public abstract class AbstractFeature im
      *   <li>May be a collection, in which case the class each elements in the collection is verified.</li>
      * </ul>
      *
-     * @param value The value, which shall be non-null.
+     * @param value  the value, which shall be non-null.
      */
     private static <T> Object verifyAttributeValue(final AttributeType<T> type, final Object value) {
         final Class<T> valueClass = type.getValueClass();
@@ -517,7 +558,7 @@ public abstract class AbstractFeature im
      *   <li>May be a collection, in which case the class each elements in the collection is verified.</li>
      * </ul>
      *
-     * @param value The value, which shall be non-null.
+     * @param value  the value, which shall be non-null.
      */
     private static Object verifyAssociationValue(final FeatureAssociationRole role, final Object value) {
         final boolean isSingleton = Field.isSingleton(role.getMaximumOccurs());
@@ -639,7 +680,7 @@ public abstract class AbstractFeature im
      * }
      * </div>
      *
-     * @return Reports on all constraint violations found.
+     * @return reports on all constraint violations found.
      *
      * @see AbstractAttribute#quality()
      * @see AbstractAssociation#quality()
@@ -653,7 +694,7 @@ public abstract class AbstractFeature im
     /**
      * Formats this feature in a tabular format.
      *
-     * @return A string representation of this feature in a tabular format.
+     * @return a string representation of this feature in a tabular format.
      *
      * @see FeatureFormat
      */
@@ -661,4 +702,82 @@ public abstract class AbstractFeature im
     public String toString() {
         return FeatureFormat.sharedFormat(this);
     }
+
+    /**
+     * Returns a hash code value for this feature.
+     * The default implementation performs the following algorithm:
+     *
+     * <ul>
+     *   <li>Iterate over all properties returned by {@code type.getProperty(true)} –
+     *       thus including properties inherited from parent types (if any):
+     *   <ul>
+     *     <li>For each property type, get the value with {@link #getPropertyValue(String)}.</li>
+     *     <li>Compute the hash code from the property name and value, ignoring the properties
+     *         having a null value.</li>
+     *   </ul></li>
+     * </ul>
+     *
+     * Subclasses should override this method with a more efficient algorithm for their internal structure.
+     * There is no need to reproduce the same hash code value than the one computed by this default method.
+     *
+     * @return a hash code value.
+     *
+     * @since 0.8
+     */
+    @Override
+    public int hashCode() {
+        int code = type.hashCode() * 37;
+        for (final PropertyType pt : type.getProperties(true)) {
+            final String name = pt.getName().toString();
+            if (name != null) {                                             // Paranoiac check.
+                final Object value = getPropertyValue(name);
+                if (value != null) {
+                    code += name.hashCode() ^ value.hashCode();
+                }
+            }
+        }
+        return code;
+    }
+
+    /**
+     * Compares this feature with the given object for equality.
+     * The default implementation performs the following algorithm:
+     *
+     * <ul>
+     *   <li>Verify that both objects are non-null and of the same class.</li>
+     *   <li>Iterate over all properties returned by {@code type.getProperty(true)} –
+     *       thus including properties inherited from parent types (if any):
+     *   <ul>
+     *     <li>For each property type, get the value from both {@code FeatureType}
+     *         by a call to {@link #getPropertyValue(String)}.</li>
+     *     <li>Verify that the two values are either both null, or equal in the sense of
+     *         {@link Object#equals(Object)}.</li>
+     *   </ul></li>
+     * </ul>
+     *
+     * Subclasses should override this method with a more efficient algorithm for their internal structure.
+     *
+     * @return {@code true} if both objects are equal.
+     *
+     * @since 0.8
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj != this) {
+            if (obj == null || obj.getClass() != getClass()) {
+                return false;
+            }
+            final AbstractFeature that = (AbstractFeature) obj;
+            if (!type.equals(that.type)) {
+                return false;
+            }
+            for (final PropertyType pt : type.getProperties(true)) {
+                final String name = pt.getName().toString();
+                if (!Objects.equals(getPropertyValue(name), that.getPropertyValue(name))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
 }

Added: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AssociationView.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AssociationView.java?rev=1754559&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AssociationView.java (added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AssociationView.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -0,0 +1,142 @@
+/*
+ * 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.Collection;
+import org.opengis.util.GenericName;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureAssociation;
+import org.opengis.feature.FeatureAssociationRole;
+
+
+/**
+ * An association implementation which delegate its work to the parent feature.
+ * This class is used for default implementation of {@link AbstractFeature#getProperty(String)}.
+ *
+ * <p><strong>This implementation is inefficient!</strong>
+ * This class is for making easier to begin with a custom {@link AbstractFeature} implementation,
+ * but developers are encouraged to provide their own {@link AbstractFeature#getProperty(String)}
+ * implementation.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+class AssociationView extends PropertyView<Feature> implements FeatureAssociation {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -148100967531766909L;
+
+    /**
+     * The role of this association. Must be one of the properties listed in the {@link #feature}.
+     */
+    private final FeatureAssociationRole role;
+
+    /**
+     * Creates a new association which will delegate its work to the given feature.
+     */
+    private AssociationView(final Feature feature, final FeatureAssociationRole role) {
+        super(feature, role.getName().toString());
+        this.role = role;
+    }
+
+    /**
+     * Creates a new association which will delegate its work to the given feature.
+     *
+     * @param feature  the feature from which to read and where to write the association value.
+     * @param role     the role of this association. Must be one of the properties listed in the
+     *                 {@link #feature} (this is not verified by this constructor).
+     */
+    static FeatureAssociation create(final Feature feature, final FeatureAssociationRole role) {
+        if (isSingleton(role.getMaximumOccurs())) {
+            return new Singleton(feature, role);
+        } else {
+            return new AssociationView(feature, role);
+        }
+    }
+
+    /**
+     * Returns the name of the role specified at construction time.
+     */
+    @Override
+    public final GenericName getName() {
+        return role.getName();
+    }
+
+    /**
+     * Returns the role specified at construction time.
+     */
+    @Override
+    public final FeatureAssociationRole getRole() {
+        return role;
+    }
+
+    /**
+     * Returns the class of values.
+     */
+    @Override
+    final Class<Feature> getValueClass() {
+        return Feature.class;
+    }
+
+    /**
+     * Specialization of {@code AssociationView} when the amount of values can be only zero or one.
+     * This implementation takes shortcuts for the {@code getValue()} and {@code getValues()} methods.
+     * This specialization is provided because it is the most common case.
+     */
+    private static final class Singleton extends AssociationView {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 2574475751526292380L;
+
+        /**
+         * Creates a new association which will delegate its work to the given feature.
+         */
+        Singleton(final Feature feature, final FeatureAssociationRole role) {
+            super(feature, role);
+        }
+
+        /**
+         * Returns the single value, or {@code null} if none.
+         */
+        @Override
+        public Feature getValue() {
+            return (Feature) this.feature.getPropertyValue(this.name);
+        }
+
+        /**
+         * Sets the value of this association. This method assumes that the
+         * {@link Feature#setPropertyValue(String, Object)} implementation
+         * will verify the argument type.
+         */
+        @Override
+        public void setValue(final Feature value) {
+            this.feature.setPropertyValue(this.name, value);
+        }
+
+        /**
+         * Wraps the property value in a set.
+         */
+        @Override
+        public Collection<Feature> getValues() {
+            return singletonOrEmpty(getValue());
+        }
+    }
+}

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

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

Added: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AttributeView.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AttributeView.java?rev=1754559&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AttributeView.java (added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AttributeView.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -0,0 +1,152 @@
+/*
+ * 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.Map;
+import java.util.Collection;
+import java.util.Collections;
+import org.opengis.util.GenericName;
+import org.opengis.feature.Feature;
+import org.opengis.feature.Attribute;
+import org.opengis.feature.AttributeType;
+
+
+/**
+ * An attribute implementation which delegate its work to the parent feature.
+ * This class is used for default implementation of {@link AbstractFeature#getProperty(String)}.
+ *
+ * <p><strong>This implementation is inefficient!</strong>
+ * This class is for making easier to begin with a custom {@link AbstractFeature} implementation,
+ * but developers are encouraged to provide their own {@link AbstractFeature#getProperty(String)}
+ * implementation.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+class AttributeView<V> extends PropertyView<V> implements Attribute<V> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3617999929561826634L;
+
+    /**
+     * The type of this attribute. Must be one of the properties listed in the {@link #feature}.
+     */
+    final AttributeType<V> type;
+
+    /**
+     * Creates a new attribute which will delegate its work to the given feature.
+     */
+    private AttributeView(final Feature feature, final AttributeType<V> type) {
+        super(feature, type.getName().toString());
+        this.type = type;
+    }
+
+    /**
+     * Creates a new attribute which will delegate its work to the given feature.
+     *
+     * @param feature  the feature from which to read and where to write the attribute value.
+     * @param type     the type of this attribute. Must be one of the properties listed in the
+     *                 {@link #feature} (this is not verified by this constructor).
+     */
+    static <V> Attribute<V> create(final Feature feature, final AttributeType<V> type) {
+        if (isSingleton(type.getMaximumOccurs())) {
+            return new Singleton<>(feature, type);
+        } else {
+            return new AttributeView<>(feature, type);
+        }
+    }
+
+    /**
+     * Returns the name of the type specified at construction time.
+     */
+    @Override
+    public final GenericName getName() {
+        return type.getName();
+    }
+
+    /**
+     * Returns the type specified at construction time.
+     */
+    @Override
+    public final AttributeType<V> getType() {
+        return type;
+    }
+
+    /**
+     * Returns the class of values.
+     */
+    @Override
+    final Class<V> getValueClass() {
+        return type.getValueClass();
+    }
+
+    /**
+     * Returns an empty map since this simple view does not support characteristics.
+     */
+    @Override
+    public final Map<String,Attribute<?>> characteristics() {
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Specialization of {@code AttributeView} when the amount of values can be only zero or one.
+     * This implementation takes shortcuts for the {@code getValue()} and {@code getValues()} methods.
+     * This specialization is provided because it is the most common case.
+     */
+    private static final class Singleton<V> extends AttributeView<V> {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = -808239726590009163L;
+
+        /**
+         * Creates a new attribute which will delegate its work to the given feature.
+         */
+        Singleton(final Feature feature, final AttributeType<V> type) {
+            super(feature, type);
+        }
+
+        /**
+         * Returns the single value, or {@code null} if none.
+         */
+        @Override
+        public V getValue() {
+            return this.type.getValueClass().cast(this.feature.getPropertyValue(this.name));
+        }
+
+        /**
+         * Sets the value of this attribute. This method assumes that the
+         * {@link Feature#setPropertyValue(String, Object)} implementation
+         * will verify the argument type.
+         */
+        @Override
+        public void setValue(final V value) {
+            this.feature.setPropertyValue(this.name, value);
+        }
+
+        /**
+         * Wraps the property value in a set.
+         */
+        @Override
+        public Collection<V> getValues() {
+            return singletonOrEmpty(getValue());
+        }
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java?rev=1754559&r1=1754558&r2=1754559&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -116,11 +116,11 @@ public class DefaultAssociationRole exte
      *   </tr>
      * </table>
      *
-     * @param identification The name and other information to be given to this association role.
-     * @param valueType      The type of feature values.
-     * @param minimumOccurs  The minimum number of occurrences of the association within its containing entity.
-     * @param maximumOccurs  The maximum number of occurrences of the association within its containing entity,
-     *                       or {@link Integer#MAX_VALUE} if there is no restriction.
+     * @param identification  the name and other information to be given to this association role.
+     * @param valueType       the type of feature values.
+     * @param minimumOccurs   the minimum number of occurrences of the association within its containing entity.
+     * @param maximumOccurs   the maximum number of occurrences of the association within its containing entity,
+     *                        or {@link Integer#MAX_VALUE} if there is no restriction.
      *
      * @see org.apache.sis.feature.builder.AssociationRoleBuilder
      */
@@ -162,11 +162,11 @@ public class DefaultAssociationRole exte
      * If more than one {@code FeatureType} instance of the given name is found at resolution time, the selected one
      * is undetermined.
      *
-     * @param identification The name and other information to be given to this association role.
-     * @param valueType      The name of the type of feature values.
-     * @param minimumOccurs  The minimum number of occurrences of the association within its containing entity.
-     * @param maximumOccurs  The maximum number of occurrences of the association within its containing entity,
-     *                       or {@link Integer#MAX_VALUE} if there is no restriction.
+     * @param identification  the name and other information to be given to this association role.
+     * @param valueType       the name of the type of feature values.
+     * @param minimumOccurs   the minimum number of occurrences of the association within its containing entity.
+     * @param maximumOccurs   the maximum number of occurrences of the association within its containing entity,
+     *                        or {@link Integer#MAX_VALUE} if there is no restriction.
      */
     public DefaultAssociationRole(final Map<String,?> identification, final GenericName valueType,
             final int minimumOccurs, final int maximumOccurs)
@@ -184,7 +184,7 @@ public class DefaultAssociationRole exte
      * to feature <var>B</var> which has an association back to <var>A</var>. It may also be <var>A</var>
      * having an association to itself, <i>etc.</i>
      *
-     * @param  creating The feature type in process of being constructed.
+     * @param  creating  the feature type in process of being constructed.
      * @return {@code true} if this association references a resolved feature type after this method call.
      */
     final boolean resolve(final DefaultFeatureType creating) {
@@ -225,10 +225,10 @@ public class DefaultAssociationRole exte
      * <p>Current implementation does not check that there is no duplicated names.
      * See {@link #deepSearch(List, GenericName)} for a rational.</p>
      *
-     * @param  feature The feature in which to search.
-     * @param  name The name of the feature to search.
-     * @param  deferred Where to store {@code FeatureType}s to be eventually used for a deep search.
-     * @return The feature of the given name, or {@code null} if none.
+     * @param  feature   the feature in which to search.
+     * @param  name      the name of the feature to search.
+     * @param  deferred  where to store {@code FeatureType}s to be eventually used for a deep search.
+     * @return the feature of the given name, or {@code null} if none.
      */
     @SuppressWarnings("null")
     private static FeatureType search(final FeatureType feature, final GenericName name, final List<FeatureType> deferred) {
@@ -281,10 +281,10 @@ public class DefaultAssociationRole exte
      * later. We rather put a warning in {@link #DefaultAssociationRole(Map, GenericName, int, int)}
      * javadoc.</p>
      *
-     * @param  feature The feature in which to search.
-     * @param  name The name of the feature to search.
-     * @param  done The feature types collected by {@link #search(FeatureType, GenericName, List)}.
-     * @return The feature of the given name, or {@code null} if none.
+     * @param  feature  the feature in which to search.
+     * @param  name     the name of the feature to search.
+     * @param  done     the feature types collected by {@link #search(FeatureType, GenericName, List)}.
+     * @return the feature of the given name, or {@code null} if none.
      */
     private static FeatureType deepSearch(final List<FeatureType> deferred, final GenericName name) {
         final Map<FeatureType,Boolean> done = new IdentityHashMap<>(8);
@@ -305,7 +305,7 @@ public class DefaultAssociationRole exte
     /**
      * Returns the type of feature values.
      *
-     * @return The type of feature values.
+     * @return the type of feature values.
      * @throws IllegalStateException if the feature type has been specified
      *         {@linkplain #DefaultAssociationRole(Map, GenericName, int, int) only by its name}
      *         and not yet resolved.
@@ -369,7 +369,7 @@ public class DefaultAssociationRole exte
      * Returns the minimum number of occurrences of the association within its containing entity.
      * The returned value is greater than or equal to zero.
      *
-     * @return The minimum number of occurrences of the association within its containing entity.
+     * @return the minimum number of occurrences of the association within its containing entity.
      */
     @Override
     public final int getMinimumOccurs() {
@@ -381,7 +381,7 @@ public class DefaultAssociationRole exte
      * The returned value is greater than or equal to the {@link #getMinimumOccurs()} value.
      * If there is no maximum, then this method returns {@link Integer#MAX_VALUE}.
      *
-     * @return The maximum number of occurrences of the association within its containing entity,
+     * @return the maximum number of occurrences of the association within its containing entity,
      *         or {@link Integer#MAX_VALUE} if none.
      */
     @Override
@@ -392,7 +392,7 @@ public class DefaultAssociationRole exte
     /**
      * Creates a new association instance of this role.
      *
-     * @return A new association instance.
+     * @return a new association instance.
      *
      * @see AbstractAssociation#create(FeatureAssociationRole)
      */
@@ -437,11 +437,11 @@ public class DefaultAssociationRole exte
      * Returns a string representation of this association role.
      * The returned string is for debugging purpose and may change in any future SIS version.
      *
-     * @return A string representation of this association role for debugging purpose.
+     * @return a string representation of this association role for debugging purpose.
      */
     @Debug
     @Override
     public String toString() {
-        return toString("FeatureAssociationRole", this, valueType.getName()).toString();
+        return toString("FeatureAssociationRole", getName(), valueType.getName()).toString();
     }
 }

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=1754559&r1=1754558&r2=1754559&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] Fri Jul 29 17:53:09 2016
@@ -167,16 +167,16 @@ public class DefaultAttributeType<V> ext
      *   </tr>
      * </table>
      *
-     * @param identification  The name and other information to be given to this attribute type.
-     * @param valueClass      The type of attribute values.
-     * @param minimumOccurs   The minimum number of occurrences of the attribute within its containing entity.
-     * @param maximumOccurs   The maximum number of occurrences of the attribute within its containing entity,
-     *                        or {@link Integer#MAX_VALUE} if there is no restriction.
-     * @param defaultValue    The default value for the attribute, or {@code null} if none.
-     * @param characterizedBy Other attribute types that describes this attribute type (can be {@code null} for none).
-     *                        For example if this new {@code DefaultAttributeType} describes a measurement,
-     *                        then {@code characterizedBy} could holds the measurement accuracy.
-     *                        See <cite>"Attribute characterization"</cite> in class Javadoc for more information.
+     * @param identification   the name and other information to be given to this attribute type.
+     * @param valueClass       the type of attribute values.
+     * @param minimumOccurs    the minimum number of occurrences of the attribute within its containing entity.
+     * @param maximumOccurs    the maximum number of occurrences of the attribute within its containing entity,
+     *                         or {@link Integer#MAX_VALUE} if there is no restriction.
+     * @param defaultValue     the default value for the attribute, or {@code null} if none.
+     * @param characterizedBy  other attribute types that describes this attribute type (can be {@code null} for none).
+     *                         For example if this new {@code DefaultAttributeType} describes a measurement,
+     *                         then {@code characterizedBy} could holds the measurement accuracy.
+     *                         See <cite>"Attribute characterization"</cite> in class Javadoc for more information.
      *
      * @see org.apache.sis.feature.builder.AttributeTypeBuilder
      */
@@ -208,9 +208,9 @@ public class DefaultAttributeType<V> ext
     /**
      * Invoked on deserialization for restoring the {@link #characteristics} field.
      *
-     * @param  in The input stream from which to deserialize an attribute type.
-     * @throws IOException If an I/O error occurred while reading or if the stream contains invalid data.
-     * @throws ClassNotFoundException If the class serialized on the stream is not on the classpath.
+     * @param  in  the input stream from which to deserialize an attribute type.
+     * @throws IOException if an I/O error occurred while reading or if the stream contains invalid data.
+     * @throws ClassNotFoundException if the class serialized on the stream is not on the classpath.
      */
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
@@ -227,7 +227,7 @@ public class DefaultAttributeType<V> ext
     /**
      * Returns the type of attribute values.
      *
-     * @return The type of attribute values.
+     * @return the type of attribute values.
      */
     @Override
     public final Class<V> getValueClass() {
@@ -253,7 +253,7 @@ public class DefaultAttributeType<V> ext
      * <p>To be valid, an {@code Attribute} instance of this {@code AttributeType} shall have at least
      * this minimum number of elements in its {@link AbstractAttribute#getValues() collection of values}.</p>
      *
-     * @return The minimum number of attribute values.
+     * @return the minimum number of attribute values.
      */
     @Override
     public final int getMinimumOccurs() {
@@ -268,7 +268,7 @@ public class DefaultAttributeType<V> ext
      * <p>To be valid, an {@code Attribute} instance of this {@code AttributeType} shall have no more than
      * this maximum number of elements in its {@link AbstractAttribute#getValues() collection of values}.</p>
      *
-     * @return The maximum number of attribute values, or {@link Integer#MAX_VALUE} if none.
+     * @return the maximum number of attribute values, or {@link Integer#MAX_VALUE} if none.
      */
     @Override
     public final int getMaximumOccurs() {
@@ -279,7 +279,7 @@ public class DefaultAttributeType<V> ext
      * Returns the default value for the attribute.
      * This value is used when an attribute is created and no value for it is specified.
      *
-     * @return The default value for the attribute, or {@code null} if none.
+     * @return the default value for the attribute, or {@code null} if none.
      */
     @Override
     public V getDefaultValue() {
@@ -301,7 +301,7 @@ public class DefaultAttributeType<V> ext
      * The {@linkplain Map#keySet() map keys} are the {@code String} representations
      * of characteristics {@linkplain #getName() name}, for more convenient lookups.
      *
-     * @return Other attribute types that describes this attribute type, or an empty map if none.
+     * @return other attribute types that describes this attribute type, or an empty map if none.
      *
      * @see AbstractAttribute#characteristics()
      */
@@ -313,7 +313,7 @@ public class DefaultAttributeType<V> ext
     /**
      * Creates a new attribute instance of this type initialized to the {@linkplain #getDefaultValue() default value}.
      *
-     * @return A new attribute instance.
+     * @return a new attribute instance.
      *
      * @see AbstractAttribute#create(AttributeType)
      */
@@ -356,11 +356,11 @@ public class DefaultAttributeType<V> ext
      * Returns a string representation of this attribute type.
      * The returned string is for debugging purpose and may change in any future SIS version.
      *
-     * @return A string representation of this attribute type for debugging purpose.
+     * @return a string representation of this attribute type for debugging purpose.
      */
     @Debug
     @Override
     public String toString() {
-        return toString("AttributeType", this, Classes.getShortName(valueClass)).toString();
+        return toString("AttributeType", getName(), Classes.getShortName(valueClass)).toString();
     }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java?rev=1754559&r1=1754558&r2=1754559&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -36,7 +36,7 @@ import org.opengis.feature.PropertyType;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  */
 abstract class FieldType extends AbstractIdentifiedType implements PropertyType {
@@ -60,10 +60,10 @@ abstract class FieldType extends Abstrac
      * Constructs a field type from the given properties. The identification map is given unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      *
-     * @param identification The name and other information to be given to this field type.
-     * @param minimumOccurs  The minimum number of occurrences of the property within its containing entity.
-     * @param maximumOccurs  The maximum number of occurrences of the property within its containing entity,
-     *                       or {@link Integer#MAX_VALUE} if there is no restriction.
+     * @param identification  the name and other information to be given to this field type.
+     * @param minimumOccurs   the minimum number of occurrences of the property within its containing entity.
+     * @param maximumOccurs   the maximum number of occurrences of the property within its containing entity,
+     *                        or {@link Integer#MAX_VALUE} if there is no restriction.
      */
     FieldType(final Map<String,?> identification, final int minimumOccurs, final int maximumOccurs) {
         super(identification);
@@ -79,7 +79,7 @@ abstract class FieldType extends Abstrac
      * Returns the minimum number of occurrences of the property within its containing entity.
      * The returned value is greater than or equal to zero.
      *
-     * @return The minimum number of occurrences of the property within its containing entity.
+     * @return the minimum number of occurrences of the property within its containing entity.
      */
     public int getMinimumOccurs() {
         return minimumOccurs;
@@ -90,7 +90,7 @@ abstract class FieldType extends Abstrac
      * The returned value is greater than or equal to the {@link #getMinimumOccurs()} value.
      * If there is no maximum, then this method returns {@link Integer#MAX_VALUE}.
      *
-     * @return The maximum number of occurrences of the property within its containing entity,
+     * @return the maximum number of occurrences of the property within its containing entity,
      *         or {@link Integer#MAX_VALUE} if none.
      */
     public int getMaximumOccurs() {
@@ -130,13 +130,12 @@ abstract class FieldType extends Abstrac
      *     PropertyType[“name” : ValueClass]
      * }
      *
-     * @param className The interface name of the object on which {@code toString()} is invoked.
-     * @param type      The property type, sometime {@code this} or sometime an other object.
-     * @param valueType The name of value class (attribute), or the feature type name (association).
+     * @param className  the interface name of the object on which {@code toString()} is invoked.
+     * @param name       the property type name, sometime {@link #getName()} or sometime the name of another object.
+     * @param valueType  the name of value class (attribute), or the feature type name (association).
      */
-    static StringBuilder toString(final String className, final PropertyType type, final Object valueType) {
+    static StringBuilder toString(final String className, final GenericName name, final Object valueType) {
         final StringBuilder buffer = new StringBuilder(40).append(className).append('[');
-        final GenericName name = type.getName();
         if (name != null) {
             buffer.append('“');
         }
@@ -155,13 +154,13 @@ abstract class FieldType extends Abstrac
      *     Property[“name” : ValueClass] = {value1, value2, ...}
      * }
      *
-     * @param className The interface name of the object on which {@code toString()} is invoked.
-     * @param type      The property type associated to the object to format.
-     * @param valueType The name of value class (attribute), or the feature type name (association).
-     * @param values    The actual values.
+     * @param className  the interface name of the object on which {@code toString()} is invoked.
+     * @param name       the property type name, sometime {@link #getName()} or sometime the name of another object.
+     * @param valueType  the name of value class (attribute), or the feature type name (association).
+     * @param values     the actual values.
      */
-    static StringBuilder toString(final String className, final PropertyType type, final Object valueType, final Iterator<?> values) {
-        final StringBuilder buffer = toString(className, type, valueType);
+    static StringBuilder toString(final String className, final GenericName name, final Object valueType, final Iterator<?> values) {
+        final StringBuilder buffer = toString(className, name, valueType);
         if (values.hasNext()) {
             final Object value = values.next();
             final boolean isMultiValued = values.hasNext();

Added: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java?rev=1754559&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java (added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -0,0 +1,215 @@
+/*
+ * 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.List;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.Collections;
+import java.io.Serializable;
+import org.apache.sis.util.collection.CheckedContainer;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.Debug;
+
+// Branch-dependent imports
+import java.util.Objects;
+import org.opengis.feature.Feature;
+import org.opengis.feature.Property;
+import org.opengis.feature.Operation;
+import org.opengis.feature.PropertyType;
+import org.opengis.feature.AttributeType;
+import org.opengis.feature.FeatureAssociationRole;
+import org.opengis.feature.MultiValuedPropertyException;
+
+
+/**
+ * An attribute or association implementation which delegate its work to the parent feature.
+ * This class is used for default implementation of {@link AbstractFeature#getProperty(String)}.
+ *
+ * <p><strong>This implementation is inefficient!</strong>
+ * This class is for making easier to begin with a custom {@link AbstractFeature} implementation,
+ * but developers are encouraged to provide their own {@link AbstractFeature#getProperty(String)}
+ * implementation.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+abstract class PropertyView<V> extends Field<V> implements Property, Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -5605415150581699255L;
+
+    /**
+     * The feature from which to read and where to write the attribute or association value.
+     */
+    final Feature feature;
+
+    /**
+     * The string representation of the property name. This is the value to be given in calls to
+     * {@link Feature#getPropertyValue(String)} and {@link Feature#setPropertyValue(String, Object)}.
+     */
+    final String name;
+
+    /**
+     * Creates a new property which will delegate its work to the given feature.
+     *
+     * @param feature  the feature from which to read and where to write the property value.
+     * @param name     the string representation of the property name.
+     */
+    PropertyView(final Feature feature, final String name) {
+        this.feature = feature;
+        this.name = name;
+    }
+
+    /**
+     * Creates a new property which will delegate its work to the given feature.
+     *
+     * @param feature  the feature from which to read and where to write the property value.
+     * @param type     the type of the property. Must be one of the properties listed in the
+     *                 {@code feature} (this is not verified by this constructor).
+     */
+    static Property create(final Feature feature, final PropertyType type) {
+        if (type instanceof AttributeType<?>) {
+            return AttributeView.create(feature, (AttributeType<?>) type);
+        } else if (type instanceof FeatureAssociationRole) {
+            return AssociationView.create(feature, (FeatureAssociationRole) type);
+        } else if (type instanceof Operation) {
+            return ((Operation) type).apply(feature, null);
+        } else {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnknownType_1, Classes.getClass(type)));
+        }
+    }
+
+    /**
+     * Returns the class of values.
+     */
+    abstract Class<V> getValueClass();
+
+    /**
+     * Returns the singleton value. This default implementation assumes that the property is multi-valued
+     * (single-valued properties shall override this method), but we nevertheless provide a fallback for
+     * non-{@code Iterable} values as a safety against implementations that are not strictly compliant
+     * to our {@link Feature#getPropertyValue(String)} method contract. Then this method verifies that
+     * the value is a collection containing zero or one element and returns that element or {@code null}.
+     */
+    @Override
+    public V getValue() throws MultiValuedPropertyException {
+        Object value = feature.getPropertyValue(name);
+        if (value instanceof Iterable<?>) {
+            final Iterator<?> it = ((Iterable<?>) value).iterator();
+            if (!it.hasNext()) {
+                return null;
+            }
+            value = it.next();
+            if (it.hasNext()) {
+                throw new MultiValuedPropertyException(Errors.format(Errors.Keys.NotASingleton_1, name));
+            }
+        }
+        return getValueClass().cast(value);
+    }
+
+    /**
+     * Sets the values of the given attribute. This default implementation assumes that the property
+     * is multi-valued (single-valued properties shall override this method) and that the
+     * {@link Feature#setPropertyValue(String, Object)} implementation will verify the argument type.
+     */
+    @Override
+    public void setValue(final V value) {
+        feature.setPropertyValue(name, singletonOrEmpty(value));
+    }
+
+    /**
+     * Returns the given value as a singleton if non-null, or returns an empty list otherwise.
+     *
+     * @param  <V>      the element type.
+     * @param  element  the element to returns in a collection if non-null.
+     * @return a collection containing the given element if non-null, or an empty collection otherwise.
+     */
+    static <V> List<V> singletonOrEmpty(final V element) {
+        return (element != null) ? Collections.singletonList(element) : Collections.emptyList();
+    }
+
+    /**
+     * Returns the values as a collection. This method tries to verify that the collection
+     * contains elements of the expected type, but this verification is not always possible.
+     * Consequently this method may, sometime, be actually unsafe.
+     */
+    @Override
+    @SuppressWarnings("unchecked")              // Actually not 100% safe, but we have done our best.
+    public Collection<V> getValues() {
+        final Object values = feature.getPropertyValue(name);
+        if (values instanceof Collection<?>) {
+            if (values instanceof CheckedContainer<?>) {
+                final Class<?> expected = getValueClass();
+                final Class<?> actual = ((CheckedContainer<?>) values).getElementType();
+                if (expected != actual) {       // Really exact match, not Class.isAssignableFrom(Class).
+                    throw new ClassCastException(Errors.format(Errors.Keys.UnexpectedTypeForReference_3, name, expected, actual));
+                }
+            }
+            return (Collection<V>) values;
+        } else {
+            return singletonOrEmpty(getValueClass().cast(values));
+        }
+    }
+
+    /**
+     * Sets the values of the given attribute. This method assumes that the
+     * {@link Feature#setPropertyValue(String, Object)} implementation will
+     * verify the argument type.
+     */
+    @Override
+    public final void setValues(final Collection<? extends V> values) {
+        feature.setPropertyValue(name, values);
+    }
+
+    /**
+     * Returns a hash code value for this property.
+     */
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(name) ^ System.identityHashCode(feature);
+    }
+
+    /**
+     * Compares this attribute with the given object for equality.
+     */
+    @Override
+    public final boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj != null && obj.getClass() == getClass()) {
+            final PropertyView<?> that = (PropertyView<?>) obj;
+            return feature == that.feature && Objects.equals(name, that.name);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a string representation of this property for debugging purposes.
+     */
+    @Debug
+    @Override
+    public final String toString() {
+        return FieldType.toString(getClass().getSimpleName(), getName(),
+                Classes.getShortName(getValueClass()), getValues().iterator()).toString();
+    }
+}

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

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

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=1754559&r1=1754558&r2=1754559&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] Fri Jul 29 17:53:09 2016
@@ -82,10 +82,10 @@ final class Validator {
      * We are not strictly forbidden to use the same identifier for both the quality measurement than the measurement
      * itself. However strictly speaking, maybe we should use a different scope.</div>
      *
-     * @param  report      Where to add the result, or {@code null} if not yet created.
-     * @param  type        Description of the property for which a constraint violation has been found.
-     * @param  explanation Explanation of the constraint violation.
-     * @return The {@code report}, or a new report if {@code report} was null.
+     * @param  report       where to add the result, or {@code null} if not yet created.
+     * @param  type         description of the property for which a constraint violation has been found.
+     * @param  explanation  explanation of the constraint violation.
+     * @return the {@code report}, or a new report if {@code report} was null.
      */
     private AbstractElement addViolationReport(AbstractElement report,
             final PropertyType type, final InternationalString explanation)
@@ -128,10 +128,10 @@ final class Validator {
             } else if (property instanceof AbstractAssociation) {
                 pq = ((AbstractAssociation) property).quality();
             } else if (property instanceof Attribute<?>) {
-                validateAny(((Attribute<?>) property).getType(), ((Attribute<?>) property).getValues());
+                validate(((Attribute<?>) property).getType(), ((Attribute<?>) property).getValues());
                 continue;
             } else if (property instanceof FeatureAssociation) {
-                validateAny(((FeatureAssociation) property).getRole(), ((FeatureAssociation) property).getValues());
+                validate(((FeatureAssociation) property).getRole(), ((FeatureAssociation) property).getValues());
                 continue;
             } else {
                 continue;
@@ -202,7 +202,7 @@ final class Validator {
     /**
      * Verifies if the given value mets the cardinality constraint.
      *
-     * @param report Where to add the result, or {@code null} if not yet created.
+     * @param report  where to add the result, or {@code null} if not yet created.
      */
     private void verifyCardinality(final AbstractElement report, final PropertyType type,
             final int minimumOccurs, final int maximumOccurs, final int count)

Added: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/AbstractFeatureTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/AbstractFeatureTest.java?rev=1754559&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/AbstractFeatureTest.java (added)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/AbstractFeatureTest.java [UTF-8] Fri Jul 29 17:53:09 2016
@@ -0,0 +1,163 @@
+/*
+ * 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.HashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.apache.sis.test.DependsOn;
+
+import static org.apache.sis.test.Assert.*;
+
+// Branch-dependent imports
+import org.opengis.feature.AttributeType;
+import org.opengis.feature.FeatureType;
+import org.opengis.feature.Property;
+import org.opengis.feature.PropertyType;
+
+
+/**
+ * Tests some default method implementations provided in {@link AbstractFeature}.
+ * This test also has the side-effect of testing {@link Validator} with attribute
+ * instances that are not {@link AbstractAttribute}.
+ *
+ * <p>This class inherits all tests defined in {@link FeatureTestCase}.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+@DependsOn({
+    DenseFeatureTest.class,
+    SparseFeatureTest.class
+})
+public final strictfp class AbstractFeatureTest extends FeatureTestCase {
+    /**
+     * A minimalist feature implementation on top of {@link AbstractFeature}. In order to get the tests to pass,
+     * we need to reproduce in this class some of the verifications performed by the more sophisticated
+     * {@link SingletonAttribute} and {@link MultiValuedAttribute} implementations.
+     * However other developers that provide their own implementations may not perform all those checks.
+     */
+    @SuppressWarnings("serial")
+    private static final class CustomFeature extends AbstractFeature implements Cloneable {
+        /**
+         * All property values.
+         */
+        private HashMap<String,Object> values = new HashMap<>();
+
+        /**
+         * Creates a new feature of the given type.
+         */
+        CustomFeature(final DefaultFeatureType type) {
+            super(type);
+            for (final PropertyType pt : type.getProperties(true)) {
+                if (pt instanceof AttributeType<?>) {
+                    Object value = ((AttributeType<?>) pt).getDefaultValue();
+                    if (isMultiValued(pt)) {
+                        value = new ArrayList<>(PropertyView.singletonOrEmpty(value));
+                    }
+                    if (value != null) {
+                        values.put(pt.getName().toString(), value);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Returns {@code true} if the given property can contains more than one value.
+         */
+        private static boolean isMultiValued(final PropertyType pt) {
+            return (pt instanceof AttributeType<?>) && (((AttributeType<?>) pt).getMaximumOccurs() > 1);
+        }
+
+        @Override
+        public Object getPropertyValue(final String name) {
+            return values.get(name);
+        }
+
+        @Override
+        public void setPropertyValue(final String name, Object value) {
+            final PropertyType type = getType().getProperty(name);
+            final boolean isMultiValued = isMultiValued(type);
+            if (isMultiValued && !(value instanceof Collection<?>)) {
+                value = new ArrayList<>(PropertyView.singletonOrEmpty(value));
+            }
+            if (value != null) {
+                final Class<?> base;
+                if (type instanceof AttributeType<?>) {
+                    base = ((AttributeType<?>) type).getValueClass();
+                } else {
+                    base = FeatureType.class;
+                }
+                for (final Object element : (isMultiValued ? (Iterable<?>) value : PropertyView.singletonOrEmpty(value))) {
+                    if (!base.isInstance(element)) {
+                        throw new ClassCastException("Can not cast " + value.getClass() + " to " + base + " in " + name + '.');
+                    }
+                }
+            }
+            values.put(name, value);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public CustomFeature clone() {
+            final CustomFeature c;
+            try {
+                c = (CustomFeature) super.clone();
+            } catch (CloneNotSupportedException e) {
+                throw new AssertionError(e);
+            }
+            c.values = (HashMap<String,Object>) c.values.clone();
+            return c;
+        }
+    }
+
+    /**
+     * Relax the requirements about {@link AbstractFeature#getProperty(String)} to return the exact same instance.
+     */
+    @Override
+    boolean assertSameProperty(final String name, final Property expected, final boolean modified) {
+        final Property actual = feature.getProperty(name);
+        if ((expected instanceof PropertyView) == (actual instanceof PropertyView)) {
+            assertEquals(name, expected, actual);
+        }
+        assertSame("name", expected.getName(), actual.getName());
+        if (!modified) {
+            assertSame("value", expected.getValue(), actual.getValue());
+        }
+        return actual == expected;
+    }
+
+    /**
+     * Creates a new feature for the given type.
+     */
+    @Override
+    final AbstractFeature createFeature(final DefaultFeatureType type) {
+        return new CustomFeature(type);
+    }
+
+    /**
+     * Clones the {@link #feature} instance.
+     */
+    @Override
+    final AbstractFeature cloneFeature() throws CloneNotSupportedException {
+        return ((CustomFeature) feature).clone();
+    }
+
+    // Inherit all tests from the super-class.
+}

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

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

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=1754559&r1=1754558&r2=1754559&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] Fri Jul 29 17:53:09 2016
@@ -31,6 +31,10 @@ import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
 
+// Branch-dependent imports
+import org.opengis.feature.Attribute;
+import org.opengis.feature.Property;
+
 
 /**
  * Tests common to {@link DenseFeatureTest} and {@link SparseFeatureTest}.
@@ -38,7 +42,7 @@ import static org.apache.sis.test.Assert
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Marc le Bihan
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public abstract strictfp class FeatureTestCase extends TestCase {
@@ -70,12 +74,27 @@ public abstract strictfp class FeatureTe
     abstract AbstractFeature cloneFeature() throws CloneNotSupportedException;
 
     /**
+     * Asserts that {@link AbstractFeature#getProperty(String)} returns the given instance.
+     * This method is overridden in subclasses than need to relax this check.
+     *
+     * @param name      the property name to check.
+     * @param expected  the expected property instance.
+     * @param modified  {@code true} if {@code expected} has been modified <strong>after</strong> it has been set
+     *                  to the {@link #feature} instance. Not all feature implementations can see such changes.
+     * @return {@code true} if the property is the expected instance, or {@code false} if it is another instance.
+     */
+    boolean assertSameProperty(final String name, final Property expected, final boolean modified) {
+        assertSame(name, expected, feature.getProperty(name));
+        return true;
+    }
+
+    /**
      * Returns the attribute value of the current {@link #feature} for the given name.
      */
     private Object getAttributeValue(final String name) {
         final Object value = feature.getPropertyValue(name);
         if (getValuesFromProperty) {
-            final AbstractAttribute<?> property = (AbstractAttribute<?>) feature.getProperty(name);
+            final Attribute<?> property = (Attribute<?>) 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), property.getType());
@@ -92,7 +111,7 @@ public abstract strictfp class FeatureTe
             }
 
             // Invoking getProperty(name) twice shall return the same Property instance.
-            assertSame(name, property, feature.getProperty(name));
+            assertSameProperty(name, property, false);
         }
         return value;
     }
@@ -102,9 +121,9 @@ public abstract strictfp class FeatureTe
      * First, this method verifies that the previous value is equals to the given one.
      * Then, this method set the attribute to the given value and check if the result.
      *
-     * @param name     The name of the attribute to set.
-     * @param oldValue The expected old value (may be {@code null}).
-     * @param newValue The new value to set.
+     * @param name      the name of the attribute to set.
+     * @param oldValue  the expected old value (may be {@code null}).
+     * @param newValue  the new value to set.
      */
     private void setAttributeValue(final String name, final Object oldValue, final Object newValue) {
         assertEquals(name, oldValue, getAttributeValue(name));
@@ -167,9 +186,9 @@ public abstract strictfp class FeatureTe
          * Before we set the population attribute, the feature should be considered invalid.
          * After we set it, the feature should be valid since all mandatory attributes are set.
          */
-        assertQualityReports("population");
+        verifyQualityReports("population");
         setAttributeValue("population", null, 1000);
-        assertQualityReports();
+        verifyQualityReports();
         /*
          * Opportunist tests using the existing instance.
          */
@@ -242,9 +261,9 @@ public abstract strictfp class FeatureTe
          * Before we set the 'isGlobal' attribute, the feature should be considered invalid.
          * After we set it, the feature should be valid since all mandatory attributes are set.
          */
-        assertQualityReports("isGlobal", "temperature");
+        verifyQualityReports("isGlobal", "temperature");
         setAttributeValue("isGlobal", null, Boolean.TRUE);
-        assertQualityReports("temperature");
+        verifyQualityReports("temperature");
         /*
          * Opportunist tests using the existing instance.
          */
@@ -277,33 +296,34 @@ public abstract strictfp class FeatureTe
             assertTrue(message, message.contains("parliament"));
             assertTrue(message, message.contains("City"));
         }
-        assertSame(city, feature.getProperty("city"));
-        /*
-         * The quality report is expected to contains a custom element.
-         */
-        int numOccurrences = 0;
-        final DataQuality quality = assertQualityReports("population");
-        for (final Element report : quality.getReports()) {
-            final String identifier = report.getMeasureIdentification().toString();
-            if (identifier.equals("city")) {
-                numOccurrences++;
-                final Result result = TestUtilities.getSingleton(report.getResults());
-                assertInstanceOf("result", QuantitativeResult.class, result);
-                assertEquals("quality.report.result.errorStatistic",
-                        CustomAttribute.ADDITIONAL_QUALITY_INFO,
-                        String.valueOf(((QuantitativeResult) result).getErrorStatistic()));
+        if (assertSameProperty("city", city, true)) {
+            /*
+             * The quality report is expected to contains a custom element.
+             */
+            int numOccurrences = 0;
+            final DataQuality quality = verifyQualityReports("population");
+            for (final Element report : quality.getReports()) {
+                final String identifier = report.getMeasureIdentification().toString();
+                if (identifier.equals("city")) {
+                    numOccurrences++;
+                    final Result result = TestUtilities.getSingleton(report.getResults());
+                    assertInstanceOf("result", QuantitativeResult.class, result);
+                    assertEquals("quality.report.result.errorStatistic",
+                            CustomAttribute.ADDITIONAL_QUALITY_INFO,
+                            String.valueOf(((QuantitativeResult) result).getErrorStatistic()));
+                }
             }
+            assertEquals("Number of reports.", 1, numOccurrences);
         }
-        assertEquals("Number of reports.", 1, numOccurrences);
     }
 
     /**
      * Asserts that {@link AbstractFeature#quality()} reports no anomaly, or only anomalies for the given properties.
      *
-     * @param  anomalousProperties The property for which we expect a report.
-     * @return The data quality report.
+     * @param  anomalousProperties  the property for which we expect a report.
+     * @return the data quality report.
      */
-    private DataQuality assertQualityReports(final String... anomalousProperties) {
+    private DataQuality verifyQualityReports(final String... anomalousProperties) {
         int anomalyIndex  = 0;
         final DataQuality quality = feature.quality();
         for (final Element report : quality.getReports()) {
@@ -327,9 +347,9 @@ public abstract strictfp class FeatureTe
      * Tests the {@link AbstractFeature#clone()} method on the current {@link #feature} instance.
      * This method is invoked from other test methods using the existing feature instance in an opportunist way.
      *
-     * @param  property The name of a property to change.
-     * @param  oldValue The old value of the given property.
-     * @param  newValue The new value of the given property.
+     * @param  property  the name of a property to change.
+     * @param  oldValue  the old value of the given property.
+     * @param  newValue  the new value of the given property.
      * @throws CloneNotSupportedException Should never happen.
      */
     private void testClone(final String property, final Object oldValue, final Object newValue)

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=1754559&r1=1754558&r2=1754559&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] Fri Jul 29 17:53:09 2016
@@ -27,7 +27,7 @@ import org.junit.BeforeClass;
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  */
 @Suite.SuiteClasses({
@@ -40,6 +40,7 @@ import org.junit.BeforeClass;
     org.apache.sis.feature.MultiValuedAttributeTest.class,
     org.apache.sis.feature.DenseFeatureTest.class,
     org.apache.sis.feature.SparseFeatureTest.class,
+    org.apache.sis.feature.AbstractFeatureTest.class,
     org.apache.sis.feature.DefaultAssociationRoleTest.class,
     org.apache.sis.feature.SingletonAssociationTest.class,
     org.apache.sis.feature.AbstractOperationTest.class,




Mime
View raw message