sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1758409 [2/9] - in /sis/branches/JDK6: ./ 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/s...
Date Tue, 30 Aug 2016 15:16:03 GMT
Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Tue Aug 30 15:16:01 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;
@@ -41,6 +42,7 @@ import org.apache.sis.internal.util.Unmo
 // Branch-dependent imports
 import org.apache.sis.internal.jdk7.Objects;
 import org.apache.sis.internal.jdk8.JDK8;
+import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.PropertyType;
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.Feature;
@@ -97,7 +99,7 @@ import org.opengis.feature.PropertyNotFo
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  *
  * @see DefaultAttributeType
@@ -166,6 +168,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)
      */
@@ -237,11 +240,11 @@ public class DefaultFeatureType extends
      *   </tr>
      * </table>
      *
-     * @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
      */
@@ -263,13 +266,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<PropertyType> sourceProperties = new ArrayList<PropertyType>(properties.length);
+        for (int i=0; i<properties.length; i++) {
+            final PropertyType 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, PropertyType[].class)); break;
+            case 1:  this.properties = Collections.singletonList(sourceProperties.get(0)); break;
+            default: this.properties = UnmodifiableArrayList.wrap(sourceProperties.toArray(new PropertyType[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 PropertyType 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);
     }
 
     /**
@@ -284,14 +314,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;
     }
 
     /**
@@ -299,15 +334,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<PropertyType> properties) {
         final int capacity = Containers.hashMapCapacity(properties.size());
         byName       = new LinkedHashMap<String,PropertyType>(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 PropertyType[byName.size()]));
         /*
          * Now check if the feature is simple/complex or dense/sparse. We perform this check after we finished
@@ -316,7 +354,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,PropertyType> entry : byName.entrySet()) {
             final int minimumOccurs, maximumOccurs;
             final PropertyType property = entry.getValue();
@@ -332,7 +370,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);
@@ -353,7 +391,7 @@ public class DefaultFeatureType extends
         for (final PropertyType 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);
@@ -401,23 +439,38 @@ 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 FeatureType source) {
+    private void scanPropertiesFrom(final FeatureType source, final Collection<? extends PropertyType> sourceProperties) {
         for (final FeatureType parent : source.getSuperTypes()) {
             if (assignableTo.add(parent.getName())) {
-                scanPropertiesFrom(parent);
+                scanPropertiesFrom(parent, parent.getProperties(false));
             }
         }
         int index = -1;
-        for (final PropertyType property : source.getProperties(false)) {
-            ArgumentChecks.ensureNonNullElement("properties", ++index, property);
-            final String name = toString(property.getName(), source, "properties", index);
+        final Iterator<? extends PropertyType> it = sourceProperties.iterator();
+        while (it.hasNext()) {
+            final PropertyType property = it.next();
+            final String name = toString(property.getName(), source, "properties", ++index);
             final PropertyType 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));
                 }
@@ -430,12 +483,14 @@ public class DefaultFeatureType extends
      * This method is for information purpose when producing an error message - its implementation does
      * not need to be efficient.
      */
-    private static GenericName ownerOf(final FeatureType type, final PropertyType property) {
-        if (type.getProperties(false).contains(property)) {
+    private static GenericName ownerOf(final FeatureType type, final Collection<? extends PropertyType> properties,
+            final PropertyType toSearch)
+    {
+        if (properties.contains(toSearch)) {
             return type.getName();
         }
         for (final FeatureType superType : type.getSuperTypes()) {
-            final GenericName owner = ownerOf(superType, property);
+            final GenericName owner = ownerOf(superType, superType.getProperties(false), toSearch);
             if (owner != null) {
                 return owner;
             }
@@ -454,8 +509,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 FeatureType feature, final Map<FeatureType,Boolean> previous) {
@@ -467,27 +522,33 @@ public class DefaultFeatureType extends
          */
         if (feature instanceof DefaultFeatureType) {
             final DefaultFeatureType dt = (DefaultFeatureType) feature;
-            return dt.isResolved = resolve(feature, previous, dt.isResolved);
+            return dt.isResolved = resolve(feature, dt.properties, previous, dt.isResolved);
         } else {
-            return resolve(feature, previous, feature.isSimple());
+            return resolve(feature, feature.getProperties(false), previous, feature.isSimple());
         }
     }
 
     /**
      * 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 FeatureType feature, Map<FeatureType,Boolean> previous, boolean resolved) {
+    private boolean resolve(final FeatureType feature, final Collection<? extends PropertyType> toUpdate,
+            Map<FeatureType,Boolean> previous, boolean resolved)
+    {
         if (!resolved) {
             resolved = true;
             for (final FeatureType type : feature.getSuperTypes()) {
                 resolved &= resolve(type, previous);
             }
-            for (final PropertyType property : feature.getProperties(false)) {
+            for (final PropertyType property : toUpdate) {
                 if (property instanceof FeatureAssociationRole) {
                     if (property instanceof DefaultAssociationRole) {
                         if (!((DefaultAssociationRole) property).resolve(this)) {
@@ -600,13 +661,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 FeatureType 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)) {
@@ -669,9 +730,31 @@ public class DefaultFeatureType extends
                 }
                 final FeatureType f0 = p0.getValueType();
                 final FeatureType f1 = p1.getValueType();
-                if (f0 != f1) {
-                    if (!f0.isAssignableFrom(f1)) {
-                        return false;
+                if (f0 != f1 && !f0.isAssignableFrom(f1)) {
+                    return false;
+                }
+            }
+            if (base instanceof Operation) {
+                if (!(other instanceof Operation)) {
+                    return false;
+                }
+                final Operation p0 = (Operation) base;
+                final Operation p1 = (Operation) other;
+                if (!Objects.equals(p0.getParameters(), p1.getParameters())) {
+                    return false;
+                }
+                final IdentifiedType r0 = p0.getResult();
+                final IdentifiedType r1 = p1.getResult();
+                if (r0 != r1) {
+                    if (r0 instanceof FeatureType) {
+                        if (!(r1 instanceof FeatureType) || !((FeatureType) r0).isAssignableFrom((FeatureType) r1)) {
+                            return false;
+                        }
+                    }
+                    if (r0 instanceof PropertyType) {
+                        if (!(r1 instanceof PropertyType) || !isAssignableIgnoreName((PropertyType) r0, (PropertyType) r1)) {
+                            return false;
+                        }
                     }
                 }
             }
@@ -692,7 +775,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.
      */
     @Override
     @SuppressWarnings("ReturnOfCollectionOrArrayField")
@@ -706,28 +789,22 @@ public class DefaultFeatureType extends
      * inherited from the {@linkplain #getSuperTypes() super-types} only if {@code includeSuperTypes}
      * is {@code true}.
      *
-     * <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<PropertyType> getProperties(final boolean includeSuperTypes) {
+    public Collection<PropertyType> getProperties(final boolean includeSuperTypes) {
         return includeSuperTypes ? allProperties : properties;
     }
 
     /**
      * Returns the attribute, operation or association role for the given name.
      *
-     * @param  name The name of the property to search.
-     * @return The property for the given name, or {@code null} if none.
-     * @throws PropertyNotFoundException 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 PropertyNotFoundException if the given argument is not a property name of this feature.
      *
      * @see AbstractFeature#getProperty(String)
      */
@@ -756,7 +833,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 FeatureInstantiationException if this feature type {@linkplain #isAbstract() is abstract}.
      */
     @Override
@@ -799,7 +876,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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] Tue Aug 30 15:16:01 2016
@@ -82,8 +82,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 PropertyNotFoundException if the given argument is not a property name of this feature.
      */
@@ -98,9 +98,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 PropertyNotFoundException 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 PropertyNotFoundException if the given argument is not a property name of this feature.
      */
     @Override
     public Property getProperty(final String name) throws PropertyNotFoundException {
@@ -131,7 +131,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.
      */
@@ -174,9 +174,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 PropertyNotFoundException 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 PropertyNotFoundException if the given argument is not an attribute or association name of this feature.
      */
     @Override
     public Object getPropertyValue(final String name) throws PropertyNotFoundException {
@@ -205,10 +205,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 {
@@ -268,7 +268,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.
      */
@@ -295,7 +295,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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] Tue Aug 30 15:16:01 2016
@@ -39,6 +39,7 @@ import org.apache.sis.util.resources.Voc
 import org.apache.sis.referencing.IdentifiedObjects;
 
 // Branch-dependent imports
+import org.apache.sis.internal.jdk8.UncheckedIOException;
 import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.Property;
 import org.opengis.feature.PropertyType;
@@ -75,7 +76,7 @@ import org.opengis.feature.Operation;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public class FeatureFormat extends TabularFormat<Object> {
@@ -178,9 +179,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);
@@ -231,11 +243,19 @@ header: for (int i=0; ; i++) {
                 }
             } else if (propertyType instanceof AttributeType<?>) {
                 value = ((AttributeType<?>) propertyType).getDefaultValue();
-            } else if (propertyType instanceof AbstractOperation) {
-                if (((AbstractOperation) propertyType).formatResultFormula(buffer)) {
-                    value = CharSequences.trimWhitespaces(buffer).toString();
-                    buffer.setLength(0);
+            } else if (propertyType instanceof Operation) {
+                buffer.append(" = ");
+                try {
+                    if (propertyType instanceof AbstractOperation) {
+                        ((AbstractOperation) propertyType).formatResultFormula(buffer);
+                    } else {
+                        AbstractOperation.defaultFormula(((Operation) propertyType).getParameters(), 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.
@@ -295,7 +315,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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java [UTF-8] Tue Aug 30 15:16:01 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.maintenance.ScopeCode;
 import org.opengis.metadata.quality.ConformanceResult;
@@ -24,12 +26,18 @@ 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;
 
 // Branch-dependent imports
 import org.opengis.feature.Attribute;
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureAssociationRole;
+import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.InvalidPropertyValueException;
+import org.opengis.feature.Operation;
+import org.opengis.feature.PropertyType;
 
 
 /**
@@ -38,7 +46,7 @@ import org.opengis.feature.InvalidProper
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  */
 public final class Features extends Static {
@@ -107,6 +115,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 PropertyType property) {
+        if (property instanceof FeatureAssociationRole) {
+            // Tested first because this is the main interest for this method.
+            return DefaultAssociationRole.getValueTypeName((FeatureAssociationRole) property);
+        } else if (property instanceof AttributeType<?>) {
+            final DefaultNameFactory factory = DefaultFactories.forBuildin(NameFactory.class, DefaultNameFactory.class);
+            return factory.toTypeName(((AttributeType<?>) property).getValueClass());
+        } else if (property instanceof Operation) {
+            final IdentifiedType result = ((Operation) 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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] Tue Aug 30 15:16:01 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();

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java [UTF-8] Tue Aug 30 15:16:01 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;
 import org.opengis.feature.Feature;
 import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.Property;
@@ -158,15 +157,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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java [UTF-8] Tue Aug 30 15:16:01 2016
@@ -37,7 +37,7 @@ import org.apache.sis.util.resources.Err
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 final class NamedFeatureType implements FeatureType, Serializable {
@@ -52,6 +52,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) {
@@ -115,8 +123,18 @@ final class NamedFeatureType implements
      * This feature type is considered independent of all other feature types except itself.
      */
     @Override
-    public boolean isAssignableFrom(final FeatureType type) {
-        return (type instanceof NamedFeatureType);
+    public boolean isAssignableFrom(FeatureType type) {
+        if (type == this) {
+            return true;
+        }
+        if (type instanceof NamedFeatureType) {
+            type = ((NamedFeatureType) type).resolved;
+        }
+        if (type == null) {
+            return false;
+        }
+        final FeatureType resolved = this.resolved;
+        return (resolved != null) && resolved.isAssignableFrom(type);
     }
 
     /**

Copied: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java (from r1758397, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java?p2=sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java&p1=sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java&r1=1758397&r2=1758409&rev=1758409&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] Tue Aug 30 15:16:01 2016
@@ -27,7 +27,7 @@ import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
 
 // Branch-dependent imports
-import java.util.Objects;
+import org.apache.sis.internal.jdk7.Objects;
 import org.opengis.feature.Feature;
 import org.opengis.feature.Property;
 import org.opengis.feature.Operation;

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] Tue Aug 30 15:16:01 2016
@@ -43,7 +43,7 @@ import org.opengis.feature.PropertyNotFo
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  *
  * @see DenseFeature
@@ -105,7 +105,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);
@@ -117,10 +117,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 PropertyNotFoundException If the given argument is not a property name of this feature.
+     * @throws PropertyNotFoundException if the given argument is not a property name of this feature.
      */
     private int getIndex(final String name) throws PropertyNotFoundException {
         final Integer index = indices.get(name);
@@ -150,7 +150,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());
                 }
@@ -163,16 +163,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 PropertyNotFoundException 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 PropertyNotFoundException if the given argument is not a property name of this feature.
      */
     @Override
     public Property getProperty(final String name) throws PropertyNotFoundException {
@@ -202,7 +202,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.
      */
@@ -222,9 +222,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 PropertyNotFoundException 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 PropertyNotFoundException if the given argument is not an attribute or association name of this feature.
      */
     @Override
     public Object getPropertyValue(final String name) throws PropertyNotFoundException {
@@ -247,7 +247,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);
         }
@@ -256,10 +256,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 {
@@ -296,9 +296,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) {
@@ -320,9 +320,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();
     }
 
@@ -333,7 +331,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.
      */
@@ -345,7 +343,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()) {
@@ -366,7 +364,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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java [UTF-8] Tue Aug 30 15:16:01 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;
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.Feature;
 import org.opengis.feature.IdentifiedType;
@@ -428,21 +427,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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] Tue Aug 30 15:16:01 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)

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java [UTF-8] Tue Aug 30 15:16:01 2016
@@ -17,11 +17,11 @@
 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.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.feature.FeatureAssociationRole;
 
 
@@ -53,6 +53,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 FeatureAssociationRole 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.
      *
@@ -71,10 +77,26 @@ public final class AssociationRoleBuilde
      */
     AssociationRoleBuilder(final FeatureTypeBuilder owner, final FeatureAssociationRole template) {
         super(owner, template);
+        property      = template;
         minimumOccurs = template.getMinimumOccurs();
         maximumOccurs = template.getMaximumOccurs();
-        type          = template.getValueType();
-        typeName      = type.getName();
+        if (template instanceof DefaultAssociationRole && !((DefaultAssociationRole) 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();
     }
 
     /**
@@ -137,6 +159,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
@@ -164,14 +214,21 @@ 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.
+     *
+     * @return the association role.
      */
     @Override
-    final PropertyType create() {
-        if (type != null) {
-            return new DefaultAssociationRole(identification(), type, minimumOccurs, maximumOccurs);
-        } else {
-            return new DefaultAssociationRole(identification(), typeName, minimumOccurs, maximumOccurs);
+    public FeatureAssociationRole 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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java?rev=1758409&r1=1758408&r2=1758409&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java [UTF-8] Tue Aug 30 15:16:01 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