sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1758562 [2/9] - in /sis/trunk: ./ core/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/main/java/org/apache/sis/feature/builder/ core/sis-feature/src/test/java/org/apache/sis/feature/ core/sis-feature/src/test/...
Date Wed, 31 Aug 2016 09:28:19 GMT
Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -170,16 +170,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
      */
@@ -211,9 +211,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();
@@ -230,7 +230,7 @@ public class DefaultAttributeType<V> ext
     /**
      * Returns the type of attribute values.
      *
-     * @return The type of attribute values.
+     * @return the type of attribute values.
      */
     public final Class<V> getValueClass() {
         return valueClass;
@@ -255,7 +255,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() {
@@ -270,7 +270,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() {
@@ -281,7 +281,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.
      */
     public V getDefaultValue() {
         return defaultValue;
@@ -302,7 +302,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(DefaultAttributeType)
      */
@@ -355,11 +355,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/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -16,7 +16,7 @@
  */
 package org.apache.sis.feature;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.HashSet;
@@ -25,6 +25,7 @@ import java.util.LinkedHashMap;
 import java.util.IdentityHashMap;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import org.opengis.util.NameFactory;
@@ -40,6 +41,7 @@ import org.apache.sis.internal.util.Unmo
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk8.JDK8;
+import org.apache.sis.internal.jdk7.Objects;
 
 
 /**
@@ -93,7 +95,7 @@ import org.apache.sis.internal.jdk8.JDK8
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  *
  * @see DefaultAttributeType
@@ -162,6 +164,7 @@ public class DefaultFeatureType extends
     /**
      * Any feature operation, any feature attribute type and any feature association role
      * that carries characteristics of a feature type.
+     * This list does not include the properties inherited from the super-types.
      *
      * @see #getProperties(boolean)
      */
@@ -239,11 +242,11 @@ public class DefaultFeatureType extends
      * array contains only attribute types, association roles or operations, <strong>not</strong> other
      * feature types since the later are not properties in the ISO sense.</div>
      *
-     * @param identification The name and other information to be given to this feature type.
-     * @param isAbstract     If {@code true}, the feature type acts as an abstract super-type.
-     * @param superTypes     The parents of this feature type, or {@code null} or empty if none.
-     * @param properties     Any feature operation, any feature attribute type and any feature
-     *                       association role that carries characteristics of a feature type.
+     * @param identification  the name and other information to be given to this feature type.
+     * @param isAbstract      if {@code true}, the feature type acts as an abstract super-type.
+     * @param superTypes      the parents of this feature type, or {@code null} or empty if none.
+     * @param properties      any feature operation, any feature attribute type and any feature
+     *                        association role that carries characteristics of a feature type.
      *
      * @see org.apache.sis.feature.builder.FeatureTypeBuilder
      */
@@ -265,13 +268,40 @@ public class DefaultFeatureType extends
                 }
             }
         }
-        switch (properties.length) {
+        /*
+         * We need to copy the properties in a temporary modifiable list in order to allow removal of elements
+         * in case of duplicated values. Opportunistically verify for null values. The same verification could
+         * be done in the scanPropertiesFrom(…) method, but doing it here produces a less confusing stacktrace.
+         */
+        final List<AbstractIdentifiedType> sourceProperties = new ArrayList<AbstractIdentifiedType>(properties.length);
+        for (int i=0; i<properties.length; i++) {
+            final AbstractIdentifiedType property = properties[i];
+            ArgumentChecks.ensureNonNullElement("properties", i, property);
+            sourceProperties.add(property);
+        }
+        computeTransientFields(sourceProperties);
+        final int size = sourceProperties.size();
+        switch (size) {
             case 0:  this.properties = Collections.emptyList(); break;
-            case 1:  this.properties = Collections.singletonList(properties[0]); break;
-            default: this.properties = UnmodifiableArrayList.wrap(Arrays.copyOf(properties, properties.length, AbstractIdentifiedType[].class)); break;
+            case 1:  this.properties = Collections.singletonList(sourceProperties.get(0)); break;
+            default: this.properties = UnmodifiableArrayList.wrap(sourceProperties.toArray(new AbstractIdentifiedType[size])); break;
+        }
+        /*
+         * Before to resolve cyclic associations, verify that operations depend only on existing properties.
+         * Note: the 'allProperties' collection has been created by computeTransientFields(…) above.
+         */
+        for (final AbstractIdentifiedType property : allProperties) {
+            if (property instanceof AbstractOperation) {
+                for (final String dependency : ((AbstractOperation) property).getDependencies()) {
+                    if (!byName.containsKey(dependency)) {
+                        throw new IllegalArgumentException(Errors.format(Errors.Keys.DependencyNotFound_3,
+                                property.getName(), dependency, super.getName()));
+                    }
+                }
+            }
         }
-        computeTransientFields();
-        isResolved = resolve(this, null, isSimple);
+        // Do not invoke before DefaultFeatureType construction succeed.
+        isResolved = resolve(this, this.properties, null, isSimple);
     }
 
     /**
@@ -286,14 +316,19 @@ public class DefaultFeatureType extends
     /**
      * Invoked on deserialization for restoring the {@link #byName} and other transient fields.
      *
-     * @param  in The input stream from which to deserialize a feature 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 a feature 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();
-        computeTransientFields();
-        isResolved = isSimple; // Conservative value. The 'resolve' method will compute a more accurate value if needed.
+        computeTransientFields(properties);
+        /*
+         * Set isResolved to a conservative value. The 'resolve' method will compute a more accurate value if needed,
+         * the first time that another DefaultFeatureType will have a dependency to this DefaultFeatureType through a
+         * DefaultAssociationRole.
+         */
+        isResolved = isSimple;
     }
 
     /**
@@ -301,15 +336,18 @@ public class DefaultFeatureType extends
      *
      * <p>As a side effect, this method checks for missing or duplicated names.</p>
      *
+     * @param  properties  same content as {@link #properties} (may be the reference to the same list), but
+     *         optionally in a temporarily modifiable list if we want to allow removal of duplicated values.
+     *         See {@link #scanPropertiesFrom(FeatureType, Collection)} javadoc for more explanation.
      * @throws IllegalArgumentException if two properties have the same name.
      */
-    private void computeTransientFields() {
+    private void computeTransientFields(final List<AbstractIdentifiedType> properties) {
         final int capacity = Containers.hashMapCapacity(properties.size());
         byName       = new LinkedHashMap<String,AbstractIdentifiedType>(capacity);
         indices      = new LinkedHashMap<String,Integer>(capacity);
         assignableTo = new HashSet<GenericName>(4);
         assignableTo.add(super.getName());
-        scanPropertiesFrom(this);
+        scanPropertiesFrom(this, properties);
         allProperties = UnmodifiableArrayList.wrap(byName.values().toArray(new AbstractIdentifiedType[byName.size()]));
         /*
          * Now check if the feature is simple/complex or dense/sparse. We perform this check after we finished
@@ -318,7 +356,7 @@ public class DefaultFeatureType extends
          */
         isSimple = true;
         int index = 0;
-        int mandatory = 0; // Count of mandatory properties.
+        int mandatory = 0;                                                  // Count of mandatory properties.
         for (final Map.Entry<String,AbstractIdentifiedType> entry : byName.entrySet()) {
             final int minimumOccurs, maximumOccurs;
             final AbstractIdentifiedType property = entry.getValue();
@@ -334,7 +372,7 @@ public class DefaultFeatureType extends
                 if (isParameterlessOperation(property)) {
                     indices.put(entry.getKey(), OPERATION_INDEX);
                 }
-                continue; // For feature operations, maximumOccurs is implicitly 0.
+                continue;                           // For feature operations, maximumOccurs is implicitly 0.
             }
             if (maximumOccurs != 0) {
                 isSimple &= (maximumOccurs == 1);
@@ -355,7 +393,7 @@ public class DefaultFeatureType extends
         for (final AbstractIdentifiedType property : allProperties) {
             final GenericName name = property.getName();
             final LocalName tip = name.tip();
-            if (tip != name) {  // Slight optimization for a common case.
+            if (tip != name) {                                          // Slight optimization for a common case.
                 final String key = tip.toString();
                 if (key != null && !key.isEmpty() && !key.equals(name.toString())) {
                     aliases.put(key, aliases.containsKey(key) ? null : property);
@@ -403,23 +441,40 @@ public class DefaultFeatureType extends
      * <p>{@code this} shall be the instance in process of being created, not any other instance
      * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
      *
-     * @param  source The feature from which to get properties.
+     * <p>This method requires that the caller gives {@code source.getProperties(false)} himself for two reasons:</p>
+     * <ul>
+     *   <li>Avoid a call to the user-overrideable {@link #getProperties(boolean)} method
+     *       while this {@code DefaultFeatureType} instance is still under constructor.</li>
+     *   <li>Allow the {@link #DefaultFeatureType(Map, boolean, FeatureType[], PropertyType[])} constructor
+     *       to pass a temporary modifiable list that allow element removal.</li>
+     * </ul>
+     *
+     * @param  source            the feature from which to get properties.
+     * @param  sourceProperties  {@code source.getProperties(false)} (see above method javadoc).
      * @throws IllegalArgumentException if two properties have the same name.
      */
-    private void scanPropertiesFrom(final DefaultFeatureType source) {
+    private void scanPropertiesFrom(final DefaultFeatureType source,
+            final Collection<? extends AbstractIdentifiedType> sourceProperties)
+    {
         for (final DefaultFeatureType parent : source.getSuperTypes()) {
             if (assignableTo.add(parent.getName())) {
-                scanPropertiesFrom(parent);
+                scanPropertiesFrom(parent, parent.getProperties(false));
             }
         }
         int index = -1;
-        for (final AbstractIdentifiedType property : source.getProperties(false)) {
-            ArgumentChecks.ensureNonNullElement("properties", ++index, property);
-            final String name = toString(property.getName(), source, "properties", index);
+        final Iterator<? extends AbstractIdentifiedType> it = sourceProperties.iterator();
+        while (it.hasNext()) {
+            final AbstractIdentifiedType property = it.next();
+            final String name = toString(property.getName(), source, "properties", ++index);
             final AbstractIdentifiedType previous = byName.put(name, property);
             if (previous != null) {
-                if (!isAssignableIgnoreName(previous, property)) {
-                    final GenericName owner = ownerOf(this, previous);
+                if (previous.equals(property)) {
+                    byName.put(name, previous);         // Keep the instance declared in super-type.
+                    if (source == this) {
+                        it.remove();                    // Remove duplicated values in instance under construction.
+                    }
+                } else if (!isAssignableIgnoreName(previous, property)) {
+                    final GenericName owner = ownerOf(this, sourceProperties, previous);
                     throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyAlreadyExists_2,
                             (owner != null) ? owner : "?", name));
                 }
@@ -436,12 +491,14 @@ public class DefaultFeatureType extends
      * However this method needs to be static in other SIS branches, because they work with interfaces
      * rather than SIS implementation. We keep the method static in this branch too for easier merges.</p>
      */
-    private static GenericName ownerOf(final DefaultFeatureType type, final AbstractIdentifiedType property) {
-        if (type.getProperties(false).contains(property)) {
+    private static GenericName ownerOf(final DefaultFeatureType type, final Collection<? extends AbstractIdentifiedType> properties,
+            final AbstractIdentifiedType toSearch)
+    {
+        if (properties.contains(toSearch)) {
             return type.getName();
         }
         for (final DefaultFeatureType superType : type.getSuperTypes()) {
-            final GenericName owner = ownerOf(superType, property);
+            final GenericName owner = ownerOf(superType, superType.getProperties(false), toSearch);
             if (owner != null) {
                 return owner;
             }
@@ -460,8 +517,8 @@ public class DefaultFeatureType extends
      * <p>{@code this} shall be the instance in process of being created, not other instance
      * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
      *
-     * @param  feature  The feature type for which to resolve the properties.
-     * @param  previous Previous results, for avoiding never ending loop.
+     * @param  feature   the feature type for which to resolve the properties.
+     * @param  previous  previous results, for avoiding never ending loop.
      * @return {@code true} if all names have been resolved.
      */
     private boolean resolve(final DefaultFeatureType feature, final Map<FeatureType,Boolean> previous) {
@@ -469,24 +526,30 @@ public class DefaultFeatureType extends
          * The isResolved field is used only as a cache for skipping completely the DefaultFeatureType instance if
          * we have determined that there is no unresolved name.
          */
-        return feature.isResolved = resolve(feature, previous, feature.isResolved);
+        return feature.isResolved = resolve(feature, feature.properties, previous, feature.isResolved);
     }
 
     /**
      * Implementation of {@link #resolve(FeatureType, Map)}, also to be invoked from the constructor.
      *
-     * @param  feature  The feature type for which to resolve the properties.
-     * @param  previous Previous results, for avoiding never ending loop. Initially {@code null}.
-     * @param  resolved {@code true} if we already know that all names are resolved.
+     * <p>{@code this} shall be the instance in process of being created, not other instance
+     * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
+     *
+     * @param  feature     the feature type for which to resolve the properties.
+     * @param  toUpdate    {@code feature.getProperties(false)}, which may contain the associations to update.
+     * @param  previous    previous results, for avoiding never ending loop. Initially {@code null}.
+     * @param  resolved    {@code true} if we already know that all names are resolved.
      * @return {@code true} if all names have been resolved.
      */
-    private boolean resolve(final DefaultFeatureType feature, Map<FeatureType,Boolean> previous, boolean resolved) {
+    private boolean resolve(final DefaultFeatureType feature, final Collection<? extends AbstractIdentifiedType> toUpdate,
+            Map<FeatureType,Boolean> previous, boolean resolved)
+    {
         if (!resolved) {
             resolved = true;
             for (final DefaultFeatureType type : feature.getSuperTypes()) {
                 resolved &= resolve(type, previous);
             }
-            for (final AbstractIdentifiedType property : feature.getProperties(false)) {
+            for (final AbstractIdentifiedType property : toUpdate) {
                 if (property instanceof DefaultAssociationRole) {
                     if (!((DefaultAssociationRole) property).resolve(this)) {
                         resolved = false;
@@ -587,13 +650,13 @@ public class DefaultFeatureType extends
      * if we compare {@code FeatureType} to {@link Class} in the Java language, then this method is equivalent
      * to {@link Class#isAssignableFrom(Class)}.</div>
      *
-     * @param  type The type to be checked.
+     * @param  type  the type to be checked.
      * @return {@code true} if instances of the given type can be assigned to association of this type.
      */
     @Override
     public boolean isAssignableFrom(final DefaultFeatureType type) {
         if (type == this) {
-            return true; // Optimization for a common case.
+            return true;                            // Optimization for a common case.
         }
         ArgumentChecks.ensureNonNull("type", type);
         if (!maybeAssignableFrom(this, type)) {
@@ -660,9 +723,31 @@ public class DefaultFeatureType extends
                 }
                 final DefaultFeatureType f0 = p0.getValueType();
                 final DefaultFeatureType f1 = p1.getValueType();
-                if (f0 != f1) {
-                    if (!f0.isAssignableFrom(f1)) {
-                        return false;
+                if (f0 != f1 && !f0.isAssignableFrom(f1)) {
+                    return false;
+                }
+            }
+            if (base instanceof AbstractOperation) {
+                if (!(other instanceof AbstractOperation)) {
+                    return false;
+                }
+                final AbstractOperation p0 = (AbstractOperation) base;
+                final AbstractOperation p1 = (AbstractOperation) other;
+                if (!Objects.equals(p0.getParameters(), p1.getParameters())) {
+                    return false;
+                }
+                final AbstractIdentifiedType r0 = p0.getResult();
+                final AbstractIdentifiedType r1 = p1.getResult();
+                if (r0 != r1) {
+                    if (r0 instanceof FeatureType) {
+                        if (!(r1 instanceof DefaultFeatureType) || !((FeatureType) r0).isAssignableFrom((DefaultFeatureType) r1)) {
+                            return false;
+                        }
+                    }
+                    if (r0 != null) {
+                        if (r1 == null || !isAssignableIgnoreName(r0, r1)) {
+                            return false;
+                        }
                     }
                 }
             }
@@ -687,7 +772,7 @@ public class DefaultFeatureType extends
      * the stability of this collection.
      * </div>
      *
-     * @return The parents of this feature type, or an empty set if none.
+     * @return  the parents of this feature type, or an empty set if none.
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
     public final Set<DefaultFeatureType> getSuperTypes() {
@@ -704,19 +789,13 @@ public class DefaultFeatureType extends
      * The type of list elements will be changed to {@code PropertyType} if and when such interface
      * will be defined in GeoAPI.</div>
      *
-     * <div class="note"><b>Note for subclasses:</b>
-     * this method is final because it is invoked (indirectly) by constructors, and invoking a user-overrideable
-     * method at construction time is not recommended. Furthermore, many Apache SIS methods need guarantees about
-     * the stability of this collection.
-     * </div>
-     *
      * @param  includeSuperTypes {@code true} for including the properties inherited from the super-types,
      *         or {@code false} for returning only the properties defined explicitely in this type.
-     * @return Feature operation, attribute type and association role that carries characteristics of this
+     * @return feature operation, attribute type and association role that carries characteristics of this
      *         feature type (not including parent types).
      */
     @Override
-    public final Collection<AbstractIdentifiedType> getProperties(final boolean includeSuperTypes) {
+    public Collection<AbstractIdentifiedType> getProperties(final boolean includeSuperTypes) {
         return includeSuperTypes ? allProperties : properties;
     }
 
@@ -727,9 +806,9 @@ public class DefaultFeatureType extends
      * The type of returned element will be changed to {@code PropertyType} if and when such interface
      * will be defined in GeoAPI.</div>
      *
-     * @param  name The name of the property to search.
-     * @return The property for the given name, or {@code null} if none.
-     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     * @param  name  the name of the property to search.
+     * @return the property for the given name, or {@code null} if none.
+     * @throws IllegalArgumentException if the given argument is not a property name of this feature.
      *
      * @see AbstractFeature#getProperty(String)
      */
@@ -757,7 +836,7 @@ public class DefaultFeatureType extends
      * if we compare {@code FeatureType} to {@link Class} and {@code Feature} to {@link Object} in the Java language,
      * then this method is equivalent to {@link Class#newInstance()}.</div>
      *
-     * @return A new feature instance.
+     * @return a new feature instance.
      * @throws IllegalStateException if this feature type {@linkplain #isAbstract() is abstract}.
      */
     public AbstractFeature newInstance() throws IllegalStateException {
@@ -799,7 +878,7 @@ public class DefaultFeatureType extends
     /**
      * 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
      */

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -76,8 +76,8 @@ final class DenseFeature extends Abstrac
      * Returns the index for the property of the given name, or {@link DefaultFeatureType#OPERATION_INDEX}
      * if the property is a parameterless operation.
      *
-     * @param  name The property name.
-     * @return The index for the property of the given name,
+     * @param  name  the property name.
+     * @return the index for the property of the given name,
      *         or a negative value if the property is a parameterless operation.
      * @throws IllegalArgumentException if the given argument is not a property name of this feature.
      */
@@ -92,9 +92,9 @@ final class DenseFeature extends Abstrac
     /**
      * Returns the property (attribute, operation or association) of the given name.
      *
-     * @param  name The property name.
-     * @return The property of the given name.
-     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     * @param  name  the property name.
+     * @return the property of the given name.
+     * @throws IllegalArgumentException if the given argument is not a property name of this feature.
      */
     @Override
     public Object getProperty(final String name) throws IllegalArgumentException {
@@ -125,7 +125,7 @@ final class DenseFeature extends Abstrac
     /**
      * Sets the property (attribute, operation or association).
      *
-     * @param  property The property to set.
+     * @param  property  the property to set.
      * @throws IllegalArgumentException if the type of the given property is not one of the types
      *         known to this feature, or if the property can not be set or another reason.
      */
@@ -168,9 +168,9 @@ final class DenseFeature extends Abstrac
     /**
      * Returns the value for the property of the given name.
      *
-     * @param  name The property name.
-     * @return The value for the given property, or {@code null} if none.
-     * @throws IllegalArgumentException If the given argument is not an attribute or association name of this feature.
+     * @param  name  the property name.
+     * @return the value for the given property, or {@code null} if none.
+     * @throws IllegalArgumentException if the given argument is not an attribute or association name of this feature.
      */
     @Override
     public Object getPropertyValue(final String name) throws IllegalArgumentException {
@@ -199,10 +199,10 @@ final class DenseFeature extends Abstrac
     /**
      * Sets the value for the property of the given name.
      *
-     * @param  name  The attribute name.
-     * @param  value The new value for the given attribute (may be {@code null}).
-     * @throws ClassCastException If the value is not assignable to the expected value class.
-     * @throws IllegalArgumentException If the given value can not be assigned for another reason.
+     * @param  name   the attribute name.
+     * @param  value  the new value for the given attribute (may be {@code null}).
+     * @throws ClassCastException if the value is not assignable to the expected value class.
+     * @throws IllegalArgumentException if the given value can not be assigned for another reason.
      */
     @Override
     public void setPropertyValue(final String name, Object value) throws IllegalArgumentException {
@@ -262,7 +262,7 @@ final class DenseFeature extends Abstrac
      * the clone operation is <cite>deep</cite> or <cite>shallow</cite>) depends on the behavior or
      * property {@code clone()} methods.
      *
-     * @return A clone of this attribute.
+     * @return a clone of this attribute.
      * @throws CloneNotSupportedException if this feature can not be cloned, typically because
      *         {@code clone()} on a property instance failed.
      */
@@ -289,7 +289,7 @@ final class DenseFeature extends Abstrac
      * in order to keep the hash code value stable before and after the {@code properties} array is promoted from the
      * {@code Object[]} type to the {@code Property[]} type.
      *
-     * @return A hash code value.
+     * @return a hash code value.
      */
     @Override
     public int hashCode() {

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -38,6 +38,9 @@ import org.apache.sis.util.resources.Err
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.referencing.IdentifiedObjects;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.UncheckedIOException;
+
 
 /**
  * Formats {@linkplain AbstractFeature features} or {@linkplain DefaultFeatureType feature types} in a tabular format.
@@ -64,7 +67,7 @@ import org.apache.sis.referencing.Identi
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public class FeatureFormat extends TabularFormat<Object> {
@@ -167,9 +170,20 @@ public class FeatureFormat extends Tabul
             }
         }
         /*
-         * Format the column header.
+         * Format the feature type name. In the case of feature type, format also the names of super-type
+         * after the UML symbol for inheritance (an arrow with white head). We do not use the " : " ASCII
+         * character for avoiding confusion with the ":" separator in namespaces. After the feature (type)
+         * name, format the column header: property name, type, cardinality and (default) value.
          */
-        toAppendTo.append(toString(featureType.getName())).append(getLineSeparator());
+        toAppendTo.append(toString(featureType.getName()));
+        if (feature == null) {
+            String separator = " ⇾ ";   // UML symbol for inheritance.
+            for (final FeatureType parent : featureType.getSuperTypes()) {
+                toAppendTo.append(separator).append(toString(parent.getName()));
+                separator = ", ";
+            }
+        }
+        toAppendTo.append(getLineSeparator());
         final Vocabulary resources = Vocabulary.getResources(displayLocale);
         final TableAppender table = new TableAppender(toAppendTo, columnSeparator);
         table.setMultiLinesCells(true);
@@ -214,10 +228,14 @@ header: for (int i=0; ; i++) {
             } else if (propertyType instanceof DefaultAttributeType<?>) {
                 value = ((DefaultAttributeType<?>) propertyType).getDefaultValue();
             } else if (propertyType instanceof AbstractOperation) {
-                if (((AbstractOperation) propertyType).formatResultFormula(buffer)) {
-                    value = CharSequences.trimWhitespaces(buffer).toString();
-                    buffer.setLength(0);
+                buffer.append(" = ");
+                try {
+                    ((AbstractOperation) propertyType).formatResultFormula(buffer);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);      // Should never happen since we write in a StringBuffer.
                 }
+                value = CharSequences.trimWhitespaces(buffer).toString();
+                buffer.setLength(0);
             }
             /*
              * Column 0 - Name.
@@ -277,7 +295,7 @@ header: for (int i=0; ; i++) {
                 final boolean isInstance = valueClass != null && valueClass.isInstance(value);
                 final Format format = isInstance ? getFormat(valueClass) : null;
                 final Iterator<?> it = (!isInstance && (value instanceof Collection<?>)
-                        ? (Collection<?>) value : Collections.singleton(value)).iterator();
+                        ? (Iterable<?>) value : Collections.singleton(value)).iterator();
                 String separator = "";
                 while (it.hasNext()) {
                     value = it.next();

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.feature;
 
+import org.opengis.util.GenericName;
+import org.opengis.util.NameFactory;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.quality.ConformanceResult;
 import org.opengis.metadata.quality.DataQuality;
@@ -23,6 +25,8 @@ import org.opengis.metadata.quality.Elem
 import org.opengis.metadata.quality.Result;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.iso.DefaultNameFactory;
+import org.apache.sis.internal.system.DefaultFactories;
 
 
 /**
@@ -31,7 +35,7 @@ import org.apache.sis.util.resources.Err
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  */
 public final class Features extends Static {
@@ -100,6 +104,43 @@ public final class Features extends Stat
     }
 
     /**
+     * Returns the name of the type of values that the given property can take.
+     * The type of value can be a {@link Class}, a {@link FeatureType} or another {@code PropertyType}
+     * depending on given argument:
+     *
+     * <ul>
+     *   <li>If {@code property} is an {@link AttributeType}, then this method gets the
+     *       {@linkplain DefaultAttributeType#getValueClass() value class} and
+     *       {@linkplain DefaultNameFactory#toTypeName(Class) maps that class to a name}.</li>
+     *   <li>If {@code property} is a {@link FeatureAssociationRole}, then this method gets
+     *       the name of the {@linkplain DefaultAssociationRole#getValueType() value type}.
+     *       This methods can work even if the associated {@code FeatureType} is not yet resolved.</li>
+     *   <li>If {@code property} is an {@link Operation}, then this method returns the name of the
+     *       {@linkplain AbstractOperation#getResult() result type}.</li>
+     * </ul>
+     *
+     * @param  property  the property for which to get the name of value type.
+     * @return the name of value type, or {@code null} if none.
+     *
+     * @since 0.8
+     */
+    public static GenericName getValueTypeName(final AbstractIdentifiedType property) {
+        if (property instanceof DefaultAssociationRole) {
+            // Tested first because this is the main interest for this method.
+            return DefaultAssociationRole.getValueTypeName((DefaultAssociationRole) property);
+        } else if (property instanceof DefaultAttributeType<?>) {
+            final DefaultNameFactory factory = DefaultFactories.forBuildin(NameFactory.class, DefaultNameFactory.class);
+            return factory.toTypeName(((DefaultAttributeType<?>) property).getValueClass());
+        } else if (property instanceof AbstractOperation) {
+            final AbstractIdentifiedType result = ((AbstractOperation) property).getResult();
+            if (result != null) {
+                return result.getName();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Ensures that all characteristics and property values in the given feature are valid.
      * An attribute is valid if it contains a number of values between the
      * {@linkplain DefaultAttributeType#getMinimumOccurs() minimum} and

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -33,7 +33,7 @@ import org.apache.sis.util.resources.Err
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.8
  * @module
  */
 abstract class FieldType extends AbstractIdentifiedType {
@@ -57,10 +57,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);
@@ -76,7 +76,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;
@@ -87,7 +87,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() {
@@ -127,13 +127,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 AbstractIdentifiedType 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('“');
         }
@@ -152,15 +151,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.
-     */
-    static StringBuilder toString(final String className, final AbstractIdentifiedType type,
-            final Object valueType, final Iterator<?> values)
-    {
-        final StringBuilder buffer = toString(className, type, valueType);
+     * @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 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();

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -30,7 +30,6 @@ import org.apache.sis.metadata.iso.citat
 import org.apache.sis.util.ArgumentChecks;
 
 // Branch-dependent imports
-import org.apache.sis.internal.jdk8.UncheckedIOException;
 
 
 /**
@@ -154,15 +153,9 @@ final class LinkOperation extends Abstra
      * Appends a string representation of the "formula" used for computing the result.
      *
      * @param  buffer where to format the "formula".
-     * @return {@code true} since this method has formatted a formula.
      */
     @Override
-    boolean formatResultFormula(final Appendable buffer) {
-        try {
-            buffer.append(" → ").append(referentName);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        return true;
+    void formatResultFormula(final Appendable buffer) throws IOException {
+        buffer.append(referentName);
     }
 }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -29,7 +29,7 @@ import org.opengis.util.GenericName;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 final class NamedFeatureType implements FeatureType, Serializable {
@@ -44,6 +44,14 @@ final class NamedFeatureType implements
     private final GenericName name;
 
     /**
+     * The feature type to use instead of the {@code NamedFeatureType}. Initially null, then set to the "real"
+     * feature type after {@link DefaultAssociationRole#resolve(DefaultFeatureType)} has been able to create it.
+     * This information is stored in case the same {@code NamedFeatureType} instance has been used in more than
+     * one {@link DefaultFeatureType}.
+     */
+    volatile FeatureType resolved;
+
+    /**
      * Creates a new placeholder for a feature of the given name.
      */
     NamedFeatureType(final GenericName name) {

Copied: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java (from r1758410, sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java?p2=sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java&p1=sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java&r1=1758410&r2=1758562&rev=1758562&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -20,7 +20,7 @@ import java.util.List;
 import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
-import java.io.Serializable;
+import org.opengis.util.GenericName;
 import org.apache.sis.util.collection.CheckedContainer;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.Classes;
@@ -28,13 +28,6 @@ import org.apache.sis.util.Debug;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.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;
 
 
 /**
@@ -51,32 +44,11 @@ import org.opengis.feature.MultiValuedPr
  * @version 0.8
  * @module
  */
-abstract class PropertyView<V> extends Field<V> implements Property, Serializable {
+final class PropertyView {
     /**
-     * For cross-version compatibility.
+     * Do not allow instantiation of this class.
      */
-    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;
+    private PropertyView() {
     }
 
     /**
@@ -86,32 +58,26 @@ abstract class PropertyView<V> extends F
      * @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);
+    static Property create(final AbstractFeature feature, final AbstractIdentifiedType type) {
+        if (type instanceof DefaultAttributeType<?>) {
+            return AttributeView.create(feature, (DefaultAttributeType<?>) type);
+        } else if (type instanceof DefaultAssociationRole) {
+            return AssociationView.create(feature, (DefaultAssociationRole) type);
+        } else if (type instanceof AbstractOperation) {
+            return (Property) ((AbstractOperation) 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 {
+    static Object getValue(final AbstractFeature feature, final String name) {
         Object value = feature.getPropertyValue(name);
         if (value instanceof Iterable<?>) {
             final Iterator<?> it = ((Iterable<?>) value).iterator();
@@ -120,10 +86,10 @@ abstract class PropertyView<V> extends F
             }
             value = it.next();
             if (it.hasNext()) {
-                throw new MultiValuedPropertyException(Errors.format(Errors.Keys.NotASingleton_1, name));
+                throw new IllegalStateException(Errors.format(Errors.Keys.NotASingleton_1, name));
             }
         }
-        return getValueClass().cast(value);
+        return value;
     }
 
     /**
@@ -131,8 +97,7 @@ abstract class PropertyView<V> extends F
      * 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) {
+    static void setValue(final AbstractFeature feature, final String name, final Object value) {
         feature.setPropertyValue(name, singletonOrEmpty(value));
     }
 
@@ -152,13 +117,11 @@ abstract class PropertyView<V> extends F
      * 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() {
+    static <V> Collection<V> getValues(final AbstractFeature feature, final String name, final Class<V> expected) {
         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));
@@ -166,7 +129,7 @@ abstract class PropertyView<V> extends F
             }
             return (Collection<V>) values;
         } else {
-            return singletonOrEmpty(getValueClass().cast(values));
+            return singletonOrEmpty(expected.cast(values));
         }
     }
 
@@ -175,41 +138,23 @@ abstract class PropertyView<V> extends F
      * {@link Feature#setPropertyValue(String, Object)} implementation will
      * verify the argument type.
      */
-    @Override
-    public final void setValues(final Collection<? extends V> values) {
+    static void setValues(final AbstractFeature feature, final String name, final Collection<?> values) {
         feature.setPropertyValue(name, values);
     }
 
     /**
      * Returns a hash code value for this property.
      */
-    @Override
-    public final int hashCode() {
+    static int hashCode(final AbstractFeature feature, final String name) {
         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();
+    static String toString(final Class<?> classe, final Class<?> valueClass, final GenericName name, final Collection<?> values) {
+        return FieldType.toString(classe.getSimpleName(), name,
+                Classes.getShortName(valueClass), values.iterator()).toString();
     }
 }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -39,7 +39,7 @@ import org.apache.sis.internal.jdk7.Obje
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  *
  * @see DenseFeature
@@ -101,7 +101,7 @@ final class SparseFeature extends Abstra
     /**
      * 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>).
      */
     public SparseFeature(final DefaultFeatureType type) {
         super(type);
@@ -113,10 +113,10 @@ final class SparseFeature extends Abstra
      * Returns the index for the property of the given name, or {@link DefaultFeatureType#OPERATION_INDEX}
      * if the property is a parameterless operation.
      *
-     * @param  name The property name.
-     * @return The index for the property of the given name,
+     * @param  name  the property name.
+     * @return the index for the property of the given name,
      *         or a negative value if the property is a parameterless operation.
-     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     * @throws IllegalArgumentException if the given argument is not a property name of this feature.
      */
     private int getIndex(final String name) throws IllegalArgumentException {
         final Integer index = indices.get(name);
@@ -146,7 +146,7 @@ final class SparseFeature extends Abstra
      */
     private void requireMapOfProperties() {
         if (valuesKind != PROPERTIES) {
-            if (!properties.isEmpty()) { // The map is typically empty when this method is first invoked.
+            if (!properties.isEmpty()) {        // The map is typically empty when this method is first invoked.
                 if (valuesKind != VALUES) {
                     throw new CorruptedObjectException(getName());
                 }
@@ -159,16 +159,16 @@ final class SparseFeature extends Abstra
                     }
                 }
             }
-            valuesKind = PROPERTIES; // Set only on success.
+            valuesKind = PROPERTIES;            // Set only on success.
         }
     }
 
     /**
      * Returns the property (attribute, operation or association) of the given name.
      *
-     * @param  name The property name.
-     * @return The property of the given name.
-     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     * @param  name  the property name.
+     * @return the property of the given name.
+     * @throws IllegalArgumentException if the given argument is not a property name of this feature.
      */
     @Override
     public Object getProperty(final String name) throws IllegalArgumentException {
@@ -198,7 +198,7 @@ final class SparseFeature extends Abstra
     /**
      * Sets the property (attribute, operation or association).
      *
-     * @param  property The property to set.
+     * @param  property  the property to set.
      * @throws IllegalArgumentException if the type of the given property is not one of the types
      *         known to this feature, or if the property can not be set for another reason.
      */
@@ -218,9 +218,9 @@ final class SparseFeature extends Abstra
     /**
      * Returns the value for the property of the given name.
      *
-     * @param  name The property name.
-     * @return The value for the given property, or {@code null} if none.
-     * @throws IllegalArgumentException If the given argument is not an attribute or association name of this feature.
+     * @param  name  the property name.
+     * @return the value for the given property, or {@code null} if none.
+     * @throws IllegalArgumentException if the given argument is not an attribute or association name of this feature.
      */
     @Override
     public Object getPropertyValue(final String name) throws IllegalArgumentException {
@@ -243,7 +243,7 @@ final class SparseFeature extends Abstra
                 throw new CorruptedObjectException(getName());
             }
         } else if (properties.containsKey(index)) {
-            return null; // Null has been explicitely set.
+            return null;                                                // Null has been explicitely set.
         } else {
             return getDefaultValue(name);
         }
@@ -252,10 +252,10 @@ final class SparseFeature extends Abstra
     /**
      * Sets the value for the property of the given name.
      *
-     * @param  name  The attribute name.
-     * @param  value The new value for the given attribute (may be {@code null}).
-     * @throws ClassCastException If the value is not assignable to the expected value class.
-     * @throws IllegalArgumentException If the given value can not be assigned for another reason.
+     * @param  name   the attribute name.
+     * @param  value  the new value for the given attribute (may be {@code null}).
+     * @throws ClassCastException if the value is not assignable to the expected value class.
+     * @throws IllegalArgumentException if the given value can not be assigned for another reason.
      */
     @Override
     public void setPropertyValue(final String name, final Object value) throws IllegalArgumentException {
@@ -292,9 +292,9 @@ final class SparseFeature extends Abstra
     /**
      * Sets a value in the {@link #properties} map.
      *
-     * @param index    The key of the property to set.
-     * @param oldValue The old value, used for verification purpose.
-     * @param newValue The new value.
+     * @param index     the key of the property to set.
+     * @param oldValue  the old value, used for verification purpose.
+     * @param newValue  the new value.
      */
     private void replace(final Integer index, final Object oldValue, final Object newValue) {
         if (properties.put(index, newValue) != oldValue) {
@@ -316,9 +316,7 @@ final class SparseFeature extends Abstra
             }
             return v.quality;
         }
-        /*
-         * Slower path when there is a possibility that user overridden the Property.quality() methods.
-         */
+        // Slower path when there is a possibility that user overridden the Property.quality() methods.
         return super.quality();
     }
 
@@ -329,7 +327,7 @@ final class SparseFeature extends Abstra
      * the clone operation is <cite>deep</cite> or <cite>shallow</cite>) depends on the behavior or
      * property {@code clone()} methods.
      *
-     * @return A clone of this attribute.
+     * @return a clone of this attribute.
      * @throws CloneNotSupportedException if this feature can not be cloned, typically because
      *         {@code clone()} on a property instance failed.
      */
@@ -341,7 +339,7 @@ final class SparseFeature extends Abstra
         switch (clone.valuesKind) {
             default:        throw new AssertionError(clone.valuesKind);
             case CORRUPTED: throw new CorruptedObjectException(clone.getName());
-            case VALUES:    break; // Nothing to do.
+            case VALUES:    break;                             // Nothing to do.
             case PROPERTIES: {
                 final Cloner cloner = new Cloner();
                 for (final Map.Entry<Integer,Object> entry : clone.properties.entrySet()) {
@@ -362,7 +360,7 @@ final class SparseFeature extends Abstra
      * in order to keep the hash code value stable before and after the {@code properties} map is (conceptually)
      * promoted from the {@code Map<Integer,Object>} type to the {@code Map<Integer,Property>} type.
      *
-     * @return A hash code value.
+     * @return a hash code value.
      */
     @Override
     public int hashCode() {

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -34,7 +34,6 @@ import org.apache.sis.util.Classes;
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.Objects;
-import org.apache.sis.internal.jdk8.UncheckedIOException;
 
 
 /**
@@ -421,21 +420,14 @@ final class StringJoinOperation extends
      * Appends a string representation of the "formula" used for computing the result.
      *
      * @param  buffer where to format the "formula".
-     * @return {@code true} since this method has formatted a formula.
      */
     @Override
-    boolean formatResultFormula(final Appendable buffer) {
-        try {
-            buffer.append(" → ");
-            String separator = "(";
-            for (final String element : attributeNames) {
-                buffer.append(separator).append(element);
-                separator = ", ";
-            }
-            buffer.append(')');
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
+    void formatResultFormula(final Appendable buffer) throws IOException {
+        if (prefix != null) buffer.append(prefix);
+        for (int i=0; i<attributeNames.length; i++) {
+            if (i != 0) buffer.append(delimiter);
+            buffer.append(attributeNames[i]);
         }
-        return true;
+        if (suffix != null) buffer.append(suffix);
     }
 }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -72,10 +72,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 AbstractIdentifiedType type, final InternationalString explanation)
@@ -118,10 +118,10 @@ final class Validator {
             } else if (property instanceof AbstractAssociation) {
                 pq = ((AbstractAssociation) property).quality();
             } else if (property instanceof AbstractAttribute<?>) {
-                validateAny(((AbstractAttribute<?>) property).getType(), ((AbstractAttribute<?>) property).getValues());
+                validate(((AbstractAttribute<?>) property).getType(), ((AbstractAttribute<?>) property).getValues());
                 continue;
             } else if (property instanceof AbstractAssociation) {
-                validateAny(((AbstractAssociation) property).getRole(), ((AbstractAssociation) property).getValues());
+                validate(((AbstractAssociation) property).getRole(), ((AbstractAssociation) property).getValues());
                 continue;
             } else {
                 continue;
@@ -192,7 +192,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 AbstractIdentifiedType type,
             final int minimumOccurs, final int maximumOccurs, final int count)

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -17,10 +17,10 @@
 package org.apache.sis.feature.builder;
 
 import org.opengis.util.GenericName;
+import org.apache.sis.feature.Features;
 import org.apache.sis.feature.DefaultAssociationRole;
 
 // Branch-dependent imports
-import org.apache.sis.feature.AbstractIdentifiedType;
 import org.apache.sis.feature.DefaultFeatureType;
 
 
@@ -52,6 +52,12 @@ public final class AssociationRoleBuilde
     private final GenericName typeName;
 
     /**
+     * The association created by this builder, or {@code null} if not yet created.
+     * This field must be cleared every time that a setter method is invoked on this builder.
+     */
+    private transient DefaultAssociationRole property;
+
+    /**
      * Creates a new {@code AssociationRole} builder for values of the given type.
      * The {@code type} argument can be null if unknown, but {@code typeName} is mandatory.
      *
@@ -70,10 +76,26 @@ public final class AssociationRoleBuilde
      */
     AssociationRoleBuilder(final FeatureTypeBuilder owner, final DefaultAssociationRole template) {
         super(owner, template);
+        property      = template;
         minimumOccurs = template.getMinimumOccurs();
         maximumOccurs = template.getMaximumOccurs();
-        type          = template.getValueType();
-        typeName      = type.getName();
+        if (!template.isResolved()) {
+            type     = null;
+            typeName = Features.getValueTypeName(template);
+        } else {
+            type     = template.getValueType();
+            typeName = type.getName();
+        }
+    }
+
+    /**
+     * If the {@code FeatureAssociationRole} created by the last call to {@link #build()} has been cached,
+     * clears that cache. This method must be invoked every time that a setter method is invoked.
+     */
+    @Override
+    final void clearCache() {
+        property = null;
+        super.clearCache();
     }
 
     /**
@@ -136,6 +158,34 @@ public final class AssociationRoleBuilde
     }
 
     /**
+     * Sets the minimum number of associations. If the given number is greater than the
+     * {@linkplain #getMaximumOccurs() maximal number} of associations, than the maximum
+     * is also set to that value.
+     *
+     * @param  occurs the new minimum number of associations.
+     * @return {@code this} for allowing method calls chaining.
+     */
+    @Override
+    public AssociationRoleBuilder setMinimumOccurs(final int occurs) {
+        super.setMinimumOccurs(occurs);
+        return this;
+    }
+
+    /**
+     * Sets the maximum number of associations. If the given number is less than the
+     * {@linkplain #getMinimumOccurs() minimal number} of associations, than the minimum
+     * is also set to that value.
+     *
+     * @param  occurs the new maximum number of associations.
+     * @return {@code this} for allowing method calls chaining.
+     */
+    @Override
+    public AssociationRoleBuilder setMaximumOccurs(final int occurs) {
+        super.setMaximumOccurs(occurs);
+        return this;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -163,14 +213,24 @@ public final class AssociationRoleBuilde
     }
 
     /**
-     * Creates a new property type from the current setting.
+     * Builds the association role from the information specified to this builder.
+     * If a role has already been built and this builder state has not changed since the role creation,
+     * then the previously created {@code FeatureAssociationRole} instance is returned.
+     *
+     * <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed to the
+     * {@code org.opengis.feature.FeatureAssociationRole} interface. This change is pending GeoAPI revision.</div>
+     *
+     * @return the association role.
      */
     @Override
-    final AbstractIdentifiedType create() {
-        if (type != null) {
-            return new DefaultAssociationRole(identification(), type, minimumOccurs, maximumOccurs);
-        } else {
-            return new DefaultAssociationRole(identification(), typeName, minimumOccurs, maximumOccurs);
+    public DefaultAssociationRole build() {
+        if (property == null) {
+            if (type != null) {
+                property = new DefaultAssociationRole(identification(), type, minimumOccurs, maximumOccurs);
+            } else {
+                property = new DefaultAssociationRole(identification(), typeName, minimumOccurs, maximumOccurs);
+            }
         }
+        return property;
     }
 }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java?rev=1758562&r1=1758561&r2=1758562&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java [UTF-8] Wed Aug 31 09:28:16 2016
@@ -16,7 +16,10 @@
  */
 package org.apache.sis.feature.builder;
 
+import java.util.Set;
+import java.util.EnumSet;
 import org.apache.sis.feature.FeatureOperations;
+import org.apache.sis.internal.jdk8.BiFunction;
 
 
 /**
@@ -66,5 +69,21 @@ public enum AttributeRole {
      * Feature can have an arbitrary amount of geometry attributes,
      * but only one can be flagged as the default geometry.
      */
-    DEFAULT_GEOMETRY
+    DEFAULT_GEOMETRY;
+
+    /**
+     * Returns the union of the given set of attribute roles.
+     * Note: this is a lambda function on the JDK8 branch.
+     */
+    static final BiFunction<Set<AttributeRole>, Set<AttributeRole>, Set<AttributeRole>> merge =
+            new BiFunction<Set<AttributeRole>, Set<AttributeRole>, Set<AttributeRole>>()
+    {
+        @Override
+        public Set<AttributeRole> apply(final Set<AttributeRole> oldValue,
+                                        final Set<AttributeRole> newValue)
+        {
+            final EnumSet<AttributeRole> union = EnumSet.copyOf(oldValue);
+            return union.addAll(newValue) ? union : oldValue;
+        }
+    };
 }



Mime
View raw message