sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1755610 [2/7] - in /sis/branches/JDK7: ./ 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, 09 Aug 2016 14:48:05 GMT
Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] Tue Aug  9 14:48:04 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/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -75,7 +75,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> {
@@ -194,9 +194,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);

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java [UTF-8] Tue Aug  9 14:48:04 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();

Copied: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java (from r1755594, sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java?p2=sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java&p1=sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java&r1=1755594&r2=1755610&rev=1755610&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -144,7 +144,7 @@ abstract class PropertyView<V> extends F
      * @return a collection containing the given element if non-null, or an empty collection otherwise.
      */
     static <V> List<V> singletonOrEmpty(final V element) {
-        return (element != null) ? Collections.singletonList(element) : Collections.emptyList();
+        return (element != null) ? Collections.singletonList(element) : Collections.<V>emptyList();
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] Tue Aug  9 14:48:04 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/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java [UTF-8] Tue Aug  9 14:48:04 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/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AssociationRoleBuilder.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -21,7 +21,6 @@ import org.apache.sis.feature.DefaultAss
 
 // Branch-dependent imports
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.feature.FeatureAssociationRole;
 
 
@@ -53,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 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,6 +76,7 @@ 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();
@@ -78,6 +84,16 @@ public final class AssociationRoleBuilde
     }
 
     /**
+     * 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();
+    }
+
+    /**
      * Appends a text inside the value returned by {@link #toString()}, before the closing bracket.
      */
     @Override
@@ -137,6 +153,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 +208,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/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeRole.java [UTF-8] Tue Aug  9 14:48:04 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;
+        }
+    };
 }

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeTypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeTypeBuilder.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeTypeBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/AttributeTypeBuilder.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -17,43 +17,69 @@
 package org.apache.sis.feature.builder;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.lang.reflect.Array;
 import org.opengis.util.GenericName;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.feature.DefaultAttributeType;
 import org.apache.sis.feature.FeatureOperations;
 import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.internal.util.SetOfUnknownSize;
+import org.apache.sis.internal.util.AbstractIterator;
 import org.apache.sis.internal.feature.AttributeConvention;
 import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.Classes;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.UnconvertibleObjectException;
 
 // Branch-dependent imports
 import java.util.Objects;
 import org.opengis.feature.AttributeType;
-import org.opengis.feature.PropertyType;
 
 
 /**
- * Describes one attribute of the {@code FeatureType} to be built by the enclosing {@code FeatureTypeBuilder}.
- * A different instance of {@code AttributeTypeBuilder} exists for each feature attribute to describe.
- * Those instances are created by {@link FeatureTypeBuilder#addAttribute(Class)}.
+ * Describes one {@code AttributeType} which will be part of the feature type to be built by
+ * a {@code FeatureTypeBuilder}. An attribute can be for example a city name, a temperature
+ * (together with its units of measurement and uncertainty if desired) or a geometric shape.
+ * Attribute types contain the following information:
  *
- * @param <V> the class of property values.
+ * <ul>
+ *   <li>the name        — a unique name which can be defined within a scope (or namespace).</li>
+ *   <li>the definition  — a concise definition of the element.</li>
+ *   <li>the designation — a natural language designator for the element for user interfaces.</li>
+ *   <li>the description — information beyond that required for concise definition of the element.</li>
+ *   <li>the value class — often {@link String}, {@link Float} or {@link com.esri.core.geometry.Geometry}.
+ *       Must be specified at {@linkplain FeatureTypeBuilder#addAttribute(Class) construction time}.</li>
+ *   <li>a default value — to be used when an attribute instance does not provide an explicit value.</li>
+ *   <li>characteristics — for example the units of measurement for all attributes of the same type.</li>
+ *   <li>cardinality     — the minimum and maximum occurrences of attribute values.</li>
+ * </ul>
+ *
+ * @param <V> the class of attribute values.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
  *
- * @see org.apache.sis.feature.DefaultAttributeType
  * @see FeatureTypeBuilder#addAttribute(Class)
+ * @see org.apache.sis.feature.DefaultAttributeType
  */
 public final class AttributeTypeBuilder<V> extends PropertyTypeBuilder {
     /**
-     * The class of property values. Can not be changed after construction
+     * The class of attribute values. Can not be changed after construction
      * because this value determines the parameterized type {@code <V>}.
      */
     private final Class<V> valueClass;
 
     /**
-     * The default value for the property, or {@code null} if none.
+     * The default value for the attribute, or {@code null} if none.
      */
     private V defaultValue;
 
@@ -70,38 +96,74 @@ public final class AttributeTypeBuilder<
     /**
      * Builders for the characteristics associated to the attribute.
      */
-    private final List<CharacteristicTypeBuilder<?>> characteristics = new ArrayList<>();
+    final List<CharacteristicTypeBuilder<?>> characteristics;
+
+    /**
+     * The attribute type 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 AttributeType<V> property;
+
+    /**
+     * Creates a new builder initialized to the values of the given builder.
+     * This constructor is for {@link #setValueClass(Class)} implementation only.
+     *
+     * @throws UnconvertibleObjectException if the default value can not be converted to the given class.
+     */
+    private AttributeTypeBuilder(final AttributeTypeBuilder<?> builder, final Class<V> valueClass)
+            throws UnconvertibleObjectException
+    {
+        super(builder);
+        this.valueClass = valueClass;
+        defaultValue = ObjectConverters.convert(builder.defaultValue, valueClass);
+        isIdentifier = builder.isIdentifier;
+        characteristics = builder.characteristics;
+    }
 
     /**
      * Creates a new {@code AttributeType} builder for values of the given class.
      *
-     * @param owner      the builder of the {@code FeatureType} for which to add this property.
-     * @param valueClass the class of property values.
+     * @param owner      the builder of the {@code FeatureType} for which to add the attribute.
+     * @param valueClass the class of attribute values.
      */
     AttributeTypeBuilder(final FeatureTypeBuilder owner, final Class<V> valueClass) {
         super(owner, null);
         this.valueClass = valueClass;
+        characteristics = new ArrayList<>();
     }
 
     /**
      * Creates a new {@code AttributeType} builder initialized to the values of an existing attribute.
      *
-     * @param owner  the builder of the {@code FeatureType} for which to add this property.
+     * @param owner  the builder of the {@code FeatureType} for which to add the attribute.
      */
     AttributeTypeBuilder(final FeatureTypeBuilder owner, final AttributeType<V> template) {
         super(owner, template);
+        property      = template;
         minimumOccurs = template.getMinimumOccurs();
         maximumOccurs = template.getMaximumOccurs();
         valueClass    = template.getValueClass();
         defaultValue  = template.getDefaultValue();
-        for (final AttributeType<?> c : template.characteristics().values()) {
+        final Map<String, AttributeType<?>> tc = template.characteristics();
+        characteristics = new ArrayList<>(tc.size());
+        for (final AttributeType<?> c : tc.values()) {
             characteristics.add(new CharacteristicTypeBuilder<>(this, c));
         }
     }
 
     /**
+     * If the {@code AttributeType} 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();
+    }
+
+    /**
      * Returns a default name to use if the user did not specified a name. The first letter will be changed to
-     * lower case (unless the name looks like an acronym) for compliance with Java convention on property names.
+     * lower case (unless the name looks like an acronym) for compliance with Java convention on attribute names.
      */
     @Override
     final String getDefaultName() {
@@ -151,11 +213,91 @@ public final class AttributeTypeBuilder<
     }
 
     /**
-     * Sets the default value for the property.
+     * Sets the minimum number of attribute values. If the given number is greater than the
+     * {@linkplain #getMaximumOccurs() maximal number} of attribute values, than the maximum
+     * is also set to that value.
+     *
+     * @param  occurs the new minimum number of attribute values.
+     * @return {@code this} for allowing method calls chaining.
+     */
+    @Override
+    public AttributeTypeBuilder<V> setMinimumOccurs(final int occurs) {
+        super.setMinimumOccurs(occurs);
+        return this;
+    }
+
+    /**
+     * Sets the maximum number of attribute values. If the given number is less than the
+     * {@linkplain #getMinimumOccurs() minimal number} of attribute values, than the minimum
+     * is also set to that value.
      *
-     * @param  value  default property value, or {@code null} if none.
+     * @param  occurs the new maximum number of attribute values.
      * @return {@code this} for allowing method calls chaining.
      */
+    @Override
+    public AttributeTypeBuilder<V> setMaximumOccurs(final int occurs) {
+        super.setMaximumOccurs(occurs);
+        return this;
+    }
+
+    /**
+     * Returns the class of attribute values.
+     *
+     * @return the class of attribute values.
+     *
+     * @see #setValueClass(Class)
+     */
+    public Class<V> getValueClass() {
+        return valueClass;
+    }
+
+    /**
+     * Sets the class of attribute values. Callers <strong>must</strong> use the builder returned by this method
+     * instead of {@code this} builder after this method call, since the returned builder may be a new instance.
+     *
+     * @param  <N>   the compile-time value of the {@code type} argument.
+     * @param  type  the new class of attribute values.
+     * @return the attribute builder — <em>not necessarily this instance.</em>
+     * @throws UnconvertibleObjectException if the {@linkplain #getDefaultValue() default value}
+     *         can not be converted to the given {@code <N>} class.
+     *
+     * @see #getValueClass()
+     */
+    @SuppressWarnings("unchecked")
+    public <N> AttributeTypeBuilder<N> setValueClass(final Class<N> type) throws UnconvertibleObjectException {
+        final FeatureTypeBuilder owner = owner();
+        ensureNonNull("type", type);
+        if (type == valueClass) {
+            return (AttributeTypeBuilder<N>) this;
+        }
+        final AttributeTypeBuilder<N> newb = new AttributeTypeBuilder<>(this, type);
+        for (final CharacteristicTypeBuilder<?> c : characteristics) {
+            c.owner(newb);
+        }
+        owner.replace(this, newb);
+        dispose();
+        return newb;
+    }
+
+    /**
+     * Returns the default value for the attribute, or {@code null} if none.
+     *
+     * @return the default attribute value, or {@code null} if none.
+     *
+     * @see #setDefaultValue(Object)
+     */
+    public V getDefaultValue() {
+        return defaultValue;
+    }
+
+    /**
+     * Sets the default value for the attribute.
+     *
+     * @param  value  default attribute value, or {@code null} if none.
+     * @return {@code this} for allowing method calls chaining.
+     *
+     * @see #getDefaultValue()
+     */
     public AttributeTypeBuilder<V> setDefaultValue(final V value) {
         if (!Objects.equals(defaultValue, value)) {
             defaultValue = value;
@@ -165,14 +307,31 @@ public final class AttributeTypeBuilder<
     }
 
     /**
-     * Sets an enumeration of valid values for this attribute.
+     * Returns an enumeration of valid values for the attribute, or an empty array if none.
+     * This convenience method returns the value of the characteristic set by {@link #setValidValues(Object...)}.
+     *
+     * @return valid values for the attribute, or an empty array if none.
+     */
+    @SuppressWarnings("unchecked")
+    public V[] getValidValues() {
+        final Collection<?> c = CollectionsExt.nonNull((Collection<?>)
+                getCharacteristic(AttributeConvention.VALID_VALUES_CHARACTERISTIC));
+        final V[] values = (V[]) Array.newInstance(valueClass, c.size());
+        int index = 0;
+        for (final Object value : c) {
+            values[index++] = (V) value;        // ArrayStoreException if 'value' is not the expected type.
+        }
+        return values;
+    }
+
+    /**
+     * Sets an enumeration of valid values for the attribute.
      *
      * <p>This is a convenience method for {@link #addCharacteristic(Class)} with a value
      * of type {@link Set} and a conventional name.</p>
      *
      * @param  values valid values.
      * @return {@code this} for allowing method calls chaining.
-     * @throws UnsupportedOperationException if this property does not support characteristics.
      *
      * @see #characteristics()
      * @see AttributeConvention#VALID_VALUES_CHARACTERISTIC
@@ -184,6 +343,16 @@ public final class AttributeTypeBuilder<
     }
 
     /**
+     * Returns the maximal length that characterizes the {@link CharSequence} values of this attribute.
+     * This convenience method returns the value of the characteristic set by {@link #setMaximalLength(Integer)}.
+     *
+     * @return the maximal length of {@link CharSequence} attribute values, or {@code null}.
+     */
+    public Integer getMaximalLength() {
+        return (Integer) getCharacteristic(AttributeConvention.MAXIMAL_LENGTH_CHARACTERISTIC);
+    }
+
+    /**
      * Sets the maximal length that characterizes the {@link CharSequence} values of this attribute.
      * While this characteristic can be applied to any kind of attribute, it is meaningful only with
      * character sequences.
@@ -193,7 +362,6 @@ public final class AttributeTypeBuilder<
      *
      * @param  length  maximal length of {@link CharSequence} attribute values, or {@code null}.
      * @return {@code this} for allowing method calls chaining.
-     * @throws UnsupportedOperationException if this property does not support length characteristics.
      *
      * @see #characteristics()
      * @see AttributeConvention#MAXIMAL_LENGTH_CHARACTERISTIC
@@ -203,6 +371,16 @@ public final class AttributeTypeBuilder<
     }
 
     /**
+     * Returns the coordinate reference system associated to attribute values.
+     * This convenience method returns the value of the characteristic set by {@link #setCRS(CoordinateReferenceSystem)}.
+     *
+     * @return the coordinate reference system associated to attribute values, or {@code null}.
+     */
+    public CoordinateReferenceSystem getCRS() {
+        return (CoordinateReferenceSystem) getCharacteristic(AttributeConvention.CRS_CHARACTERISTIC);
+    }
+
+    /**
      * Sets the coordinate reference system that characterizes the values of this attribute.
      * While this characteristic can be applied to any kind of attribute, it is meaningful
      * only with georeferenced values like geometries or coverages.
@@ -212,7 +390,6 @@ public final class AttributeTypeBuilder<
      *
      * @param  crs  coordinate reference system associated to attribute values, or {@code null}.
      * @return {@code this} for allowing method calls chaining.
-     * @throws UnsupportedOperationException if this property does not support CRS characteristics.
      *
      * @see #characteristics()
      * @see AttributeConvention#CRS_CHARACTERISTIC
@@ -222,12 +399,25 @@ public final class AttributeTypeBuilder<
     }
 
     /**
+     * Implementation of all getter methods for characteristics.
+     */
+    private Object getCharacteristic(final GenericName name) {
+        for (final CharacteristicTypeBuilder<?> characteristic : characteristics) {
+            if (name.equals(characteristic.getName())) {
+                return characteristic.getDefaultValue();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Implementation of all setter methods for characteristics.
      */
     private <C> AttributeTypeBuilder<V> setCharacteristic(final GenericName name, final Class<C> type, final C value) {
         for (final CharacteristicTypeBuilder<?> characteristic : characteristics) {
             if (name.equals(characteristic.getName())) {
                 characteristic.set(value);
+                clearCache();
                 return this;
             }
         }
@@ -236,6 +426,22 @@ public final class AttributeTypeBuilder<
     }
 
     /**
+     * Returns the builder for the characteristic of the given name. The given name does not need to contains
+     * all elements of a {@link org.opengis.util.ScopedName}; it is okay to specify only the tip (for example
+     * {@code "myName"} instead of {@code "myScope:myName"}) provided that ignoring the name head does not
+     * create ambiguity.
+     *
+     * @param  name   name of the characteristic to search.
+     * @return characteristic of the given name, or {@code null} if none.
+     * @throws IllegalArgumentException if the given name is ambiguous.
+     *
+     * @see #characteristics()
+     */
+    public CharacteristicTypeBuilder<?> getCharacteristic(final String name) {
+        return forName(characteristics, name);
+    }
+
+    /**
      * Adds another attribute type that describes this attribute type.
      * See <cite>"Attribute characterization"</cite> in {@link DefaultAttributeType} Javadoc for more information.
      *
@@ -280,11 +486,100 @@ public final class AttributeTypeBuilder<
     }
 
     /**
-     * Flags this attribute as an input of one of the pre-defined operations managed by {@code FeatureTypeBuilder}.
+     * Returns a view of all characteristics added to the {@code AttributeType} to build.
+     * The returned list is <cite>live</cite>: changes in this builder are reflected in that list and conversely.
+     * However the returned list allows only {@linkplain List#remove(Object) remove} operations;
+     * new characteristics can be added only by calls to one of the {@code set/addCharacteristic(…)} methods.
      *
-     * @param role the role to add to this attribute (shall not be null).
+     * @return a live list over the characteristics declared to this builder.
+     *
+     * @see #getCharacteristic(String)
+     * @see #addCharacteristic(Class)
+     * @see #addCharacteristic(AttributeType)
+     * @see #setValidValues(Object...)
+     * @see #setCRS(CoordinateReferenceSystem)
      */
-    public void addRole(final AttributeRole role) {
+    public List<CharacteristicTypeBuilder<?>> characteristics() {
+        return new RemoveOnlyList<>(characteristics);
+    }
+
+    /**
+     * Returns the roles that the attribute play in the pre-defined operations managed by {@code AttributeTypeBuilder}.
+     * The set returned by this method is <cite>live</cite>: additions or removal on that set are reflected back on
+     * this builder, and conversely.
+     *
+     * @return the roles that the attribute play in the pre-defined operations managed by {@code AttributeTypeBuilder}.
+     */
+    public Set<AttributeRole> roles() {
+        return new SetOfUnknownSize<AttributeRole>() {
+            @Override public Iterator<AttributeRole> iterator() {return new RoleIter();}
+            @Override public boolean add(AttributeRole role)    {return addRole(role);}
+        };
+    }
+
+    /**
+     * The iterator returned by the {@link AttributeTypeBuilder#roles()} set.
+     */
+    private final class RoleIter extends AbstractIterator<AttributeRole> {
+        /**
+         * Index of the next {@code AttributeRole} to return.
+         */
+        private int index;
+
+        /**
+         * Prepares the next {@code AttributeRole} on which to iterate and returns
+         * {@code true} if such {@code AttributeRole} has been found.
+         */
+        @Override
+        @SuppressWarnings("fallthrough")
+        public boolean hasNext() {
+            if (next == null) {
+                switch (index) {
+                    case 0: {
+                        if (isIdentifier) {
+                            next = AttributeRole.IDENTIFIER_COMPONENT;
+                            break;
+                        }
+                        index++;        // Fall through for testing the case for next 'index' value.
+                    }
+                    case 1: {
+                        if (owner().defaultGeometry == AttributeTypeBuilder.this) {
+                            next = AttributeRole.DEFAULT_GEOMETRY;
+                            break;
+                        }
+                        index++;        // Fall through for testing the case for next 'index' value.
+                    }
+                    default: {
+                        return false;
+                    }
+                }
+                index++;
+            }
+            return true;
+        }
+
+        /**
+         * Removes the element returned by the last {@link #next()} method.
+         */
+        @Override
+        public void remove() {
+            switch (index) {
+                case 1: isIdentifier = false; break;
+                case 2: owner().defaultGeometry = null; break;
+                default: throw new IllegalStateException();
+            }
+        }
+    }
+
+    /**
+     * Flags this attribute as an input of one of the pre-defined operations managed by {@code AttributeTypeBuilder}.
+     * Invoking this method is equivalent to invoking <code>{@linkplain #roles()}.add(role)</code>.
+     *
+     * @param  role the role to add to the attribute (shall not be null).
+     * @return {@code true} if the given role has been added to the attribute.
+     */
+    public boolean addRole(final AttributeRole role) {
+        final FeatureTypeBuilder owner = owner();
         ensureNonNull("role", role);
         switch (role) {
             case IDENTIFIER_COMPONENT: {
@@ -292,6 +587,7 @@ public final class AttributeTypeBuilder<
                     isIdentifier = true;
                     owner.identifierCount++;
                     owner.clearCache();         // The change does not impact this attribute itself.
+                    return true;
                 }
                 break;
             }
@@ -306,10 +602,12 @@ public final class AttributeTypeBuilder<
                     }
                     owner.defaultGeometry = this;
                     owner.clearCache();         // The change does not impact this attribute itself.
+                    return true;
                 }
                 break;
             }
         }
+        return false;
     }
 
     /**
@@ -321,23 +619,6 @@ public final class AttributeTypeBuilder<
     }
 
     /**
-     * Returns a view of all characteristics added to the {@code AttributeType} to build.
-     * The returned list is <cite>live</cite>: changes in this builder are reflected in that list and conversely.
-     * However the returned list allows only {@linkplain List#remove(Object) remove} operations;
-     * new characteristics can be added only by calls to one of the {@code set/addCharacteristic(…)} methods.
-     *
-     * @return a live list over the characteristics declared to this builder.
-     *
-     * @see #addCharacteristic(Class)
-     * @see #addCharacteristic(AttributeType)
-     * @see #setValidValues(Object...)
-     * @see #setCRS(CoordinateReferenceSystem)
-     */
-    public List<CharacteristicTypeBuilder<?>> characteristics() {
-        return new RemoveOnlyList<>(characteristics);
-    }
-
-    /**
      * {@inheritDoc}
      */
     @Override
@@ -373,14 +654,52 @@ public final class AttributeTypeBuilder<
     }
 
     /**
-     * Creates a new property type from the current setting.
+     * {@inheritDoc}
+     */
+    @Override
+    public void remove() {
+        if (isIdentifier) {
+            isIdentifier = false;
+            owner().identifierCount--;      // Owner should never be null since we set 'isIdentifier' to false.
+        }
+        super.remove();
+    }
+
+    /**
+     * Builds the attribute type from the information specified to this builder.
+     * If a type has already been built and this builder state has not changed since the type creation,
+     * then the previously created {@code AttributeType} instance is returned.
+     *
+     * <div class="note"><b>Example:</b>
+     * the following lines of code add a "name" attribute to a "City" feature, then get the corresponding
+     * {@code AttributeType<String>} instance. If no setter method is invoked on the builder of the "name"
+     * attribute after those lines, then the {@code name} variable below will reference the same instance
+     * than the "name" attribute in the {@code city} type.
+     *
+     * {@preformat java
+     *   FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("City");
+     *   AttributeType<String> name = builder.addAttribute(String.class).setName("name").build();
+     *   FeatureType city = builder.build();
+     *
+     *   assert city.getProperty("name") == name : "AttributeType instance should be the same.";
+     * }
+     *
+     * Note that {@code city.getProperty("name")} returns {@code AttributeType<?>},
+     * i.e. the {@linkplain #getValueClass() value class} is lost at compile-time.
+     * By comparison, this {@code build()} method has a more accurate return type.
+     * </div>
+     *
+     * @return the attribute type.
      */
     @Override
-    final PropertyType create() {
-        final AttributeType<?>[] chrts = new AttributeType<?>[characteristics.size()];
-        for (int i=0; i<chrts.length; i++) {
-            chrts[i] = characteristics.get(i).build();
+    public AttributeType<V> build() {
+        if (property == null) {
+            final AttributeType<?>[] chrts = new AttributeType<?>[characteristics.size()];
+            for (int i=0; i<chrts.length; i++) {
+                chrts[i] = characteristics.get(i).build();
+            }
+            property = new DefaultAttributeType<>(identification(), valueClass, minimumOccurs, maximumOccurs, defaultValue, chrts);
         }
-        return new DefaultAttributeType<>(identification(), valueClass, minimumOccurs, maximumOccurs, defaultValue, chrts);
+        return property;
     }
 }

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/CharacteristicTypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/CharacteristicTypeBuilder.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/CharacteristicTypeBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/CharacteristicTypeBuilder.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -19,6 +19,8 @@ package org.apache.sis.feature.builder;
 import org.opengis.util.GenericName;
 import org.apache.sis.feature.DefaultAttributeType;
 import org.apache.sis.util.Classes;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.UnconvertibleObjectException;
 
 // Branch-dependent imports
 import java.util.Objects;
@@ -26,13 +28,14 @@ import org.opengis.feature.AttributeType
 
 
 /**
- * Describes one characteristic of an {@code AttributeType} to be built by the enclosing {@code FeatureTypeBuilder}.
- * A different instance of {@code CharacteristicTypeBuilder} exists for each characteristic to describe.
- * Those instances are created by:
+ * Describes one characteristic of the {@code AttributeType} will will be built by a {@code FeatureTypeBuilder}.
+ * Characteristics can describe additional information useful for interpreting an attribute value, like
+ * the units of measurement and uncertainties of a numerical value, or the coordinate reference system
+ * (CRS) of a geometry.
  *
- * <ul>
- *   <li>{@link AttributeTypeBuilder#addCharacteristic(Class)}</li>
- * </ul>
+ * <p>In many cases, all instances of the same {@code AttributeType} have the same characteristics.
+ * For example all values of the "temperature" attribute typically have the same units of measurement.
+ * Such common value can be specified as the characteristic {@linkplain #setDefaultValue(Object) default value}.</p>
  *
  * @param <V> the class of characteristic values.
  *
@@ -41,15 +44,18 @@ import org.opengis.feature.AttributeType
  * @since   0.8
  * @version 0.8
  * @module
+ *
+ * @see AttributeTypeBuilder#addCharacteristic(Class)
  */
 public final class CharacteristicTypeBuilder<V> extends TypeBuilder {
     /**
      * The attribute type builder instance that created this {@code CharacteristicTypeBuilder} builder.
+     * This is set at construction time and considered as immutable until it is set to {@code null}.
      */
-    private final AttributeTypeBuilder<?> owner;
+    private AttributeTypeBuilder<?> owner;
 
     /**
-     * The class of attribute values. Can not be changed after construction
+     * The class of characteristic values. Can not be changed after construction
      * because this value determines the parameterized type {@code <V>}.
      */
     private final Class<V> valueClass;
@@ -66,6 +72,22 @@ public final class CharacteristicTypeBui
     private transient AttributeType<V> characteristic;
 
     /**
+     * Creates a new builder initialized to the values of the given builder but a different type.
+     * This constructor is for {@link #setValueClass(Class)} implementation only.
+     *
+     * @throws UnconvertibleObjectException if the default value can not be converted to the given class.
+     */
+    private CharacteristicTypeBuilder(final CharacteristicTypeBuilder<?> builder, final Class<V> valueClass)
+            throws UnconvertibleObjectException
+    {
+        super(builder);
+        this.owner        = builder.owner;
+        this.valueClass   = valueClass;
+        this.defaultValue = ObjectConverters.convert(builder.defaultValue, valueClass);
+        // Do not copy the 'characteristic' reference since the 'valueClass' is different.
+    }
+
+    /**
      * Creates a new characteristic builder for values of the given class.
      *
      * @param owner      the builder of the {@code AttributeType} for which to add this property.
@@ -85,9 +107,9 @@ public final class CharacteristicTypeBui
     CharacteristicTypeBuilder(final AttributeTypeBuilder<?> owner, final AttributeType<V> template) {
         super(template, owner.getLocale());
         this.owner     = owner;
+        characteristic = template;
         valueClass     = template.getValueClass();
         defaultValue   = template.getDefaultValue();
-        characteristic = template;
     }
 
     /**
@@ -97,6 +119,7 @@ public final class CharacteristicTypeBui
     @Override
     final void clearCache() {
         characteristic = null;
+        ensureAlive(owner);
         owner.clearCache();
     }
 
@@ -156,14 +179,56 @@ public final class CharacteristicTypeBui
      */
     @Override
     final GenericName name(final String scope, final String localPart) {
+        ensureAlive(owner);
         return owner.name(scope, localPart);
     }
 
     /**
-     * Sets the default value with check of the value class.
+     * Returns the class of characteristic values.
+     *
+     * @return the class of characteristic values.
+     *
+     * @see #setValueClass(Class)
      */
-    final void set(final Object value) {
-        setDefaultValue(valueClass.cast(value));
+    public Class<V> getValueClass() {
+        return valueClass;
+    }
+
+    /**
+     * Sets the class of characteristic values. Callers <strong>must</strong> use the builder returned by this method
+     * instead of {@code this} builder after this method call, since the returned builder may be a new instance.
+     *
+     * @param  <N>   the compile-time value of the {@code type} argument.
+     * @param  type  the new class of characteristic values.
+     * @return the characteristic builder — <em>not necessarily this instance.</em>
+     * @throws UnconvertibleObjectException if the {@linkplain #getDefaultValue() default value}
+     *         can not be converted to the given {@code <N>} class.
+     *
+     * @see #getValueClass()
+     */
+    @SuppressWarnings("unchecked")
+    public <N> CharacteristicTypeBuilder<N> setValueClass(final Class<N> type) throws UnconvertibleObjectException {
+        ensureAlive(owner);
+        ensureNonNull("type", type);
+        if (type == valueClass) {
+            return (CharacteristicTypeBuilder<N>) this;
+        }
+        final CharacteristicTypeBuilder<N> newb = new CharacteristicTypeBuilder<>(this, type);
+        owner.characteristics.set(owner.characteristics.lastIndexOf(this), newb);
+        // Note: a negative lastIndexOf(old) would be a bug in our algorithm.
+        owner = null;
+        return newb;
+    }
+
+    /**
+     * Returns the default value for the characteristic, or {@code null} if none.
+     *
+     * @return the default characteristic value, or {@code null} if none.
+     *
+     * @see #setDefaultValue(Object)
+     */
+    public V getDefaultValue() {
+        return defaultValue;
     }
 
     /**
@@ -171,6 +236,8 @@ public final class CharacteristicTypeBui
      *
      * @param  value  characteristic default value, or {@code null} if none.
      * @return {@code this} for allowing method calls chaining.
+     *
+     * @see #getDefaultValue()
      */
     public CharacteristicTypeBuilder<V> setDefaultValue(final V value) {
         if (!Objects.equals(defaultValue, value)) {
@@ -181,6 +248,13 @@ public final class CharacteristicTypeBui
     }
 
     /**
+     * Sets the default value with check of the value class.
+     */
+    final void set(final Object value) {
+        setDefaultValue(valueClass.cast(value));
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -208,12 +282,40 @@ public final class CharacteristicTypeBui
     }
 
     /**
-     * Creates a new characteristic from the current setting.
+     * Builds the characteristic type from the information specified to this builder.
+     * If a type has already been built and this builder state has not changed since the type creation,
+     * then the previously created {@code AttributeType} instance is returned.
+     *
+     * @return the characteristic type.
      */
-    final AttributeType<V> build() {
+    @Override
+    public AttributeType<V> build() {
         if (characteristic == null) {
             characteristic = new DefaultAttributeType<>(identification(), valueClass, 0, 1, defaultValue);
         }
         return characteristic;
     }
+
+    /**
+     * Sets a new owner.
+     */
+    final void owner(final AttributeTypeBuilder<?> newb) {
+        owner = newb;
+    }
+
+    /**
+     * Removes this characteristics from the {@code AttributeTypeBuilder}.
+     * After this method has been invoked, this {@code CharacteristicTypeBuilder} instance
+     * is no longer in the list returned by {@link AttributeTypeBuilder#characteristics()}
+     * and attempts to invoke any setter method on {@code this} will cause an
+     * {@link IllegalStateException} to be thrown.
+     */
+    @Override
+    public void remove() {
+        if (owner != null) {
+            owner.characteristics.remove(owner.characteristics.lastIndexOf(this));
+            // Note: a negative lastIndexOf(old) would be a bug in our algorithm.
+            owner = null;
+        }
+    }
 }

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/FeatureTypeBuilder.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -19,9 +19,11 @@ package org.apache.sis.feature.builder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Locale;
+import java.util.Set;
 import org.opengis.util.GenericName;
 import org.opengis.util.NameFactory;
 import org.opengis.util.FactoryException;
@@ -41,6 +43,8 @@ import org.opengis.feature.Feature;
 import org.opengis.feature.FeatureType;
 import org.opengis.feature.PropertyType;
 import org.opengis.feature.FeatureAssociationRole;
+import org.opengis.feature.Operation;
+import org.apache.sis.internal.jdk8.JDK8;
 
 
 /**
@@ -178,16 +182,71 @@ public class FeatureTypeBuilder extends
             feature    = template;
             isAbstract = template.isAbstract();
             superTypes.addAll(template.getSuperTypes());
-            for (final PropertyType p : template.getProperties(false)) {
-                final PropertyTypeBuilder builder;
-                if (p instanceof AttributeType<?>) {
-                    builder = new AttributeTypeBuilder<>(this, (AttributeType<?>) p);
-                } else if (p instanceof FeatureAssociationRole) {
-                    builder = new AssociationRoleBuilder(this, (FeatureAssociationRole) p);
+            /*
+             * For each attribute and association, wrap those properties in a builder.
+             * For each operation, wrap them in pseudo-builder only if the operation
+             * is not one of the operations automatically generated by this builder.
+             */
+            final Map<String,Set<AttributeRole>> propertyRoles = new HashMap<>();
+            for (final PropertyType property : template.getProperties(false)) {
+                PropertyTypeBuilder builder;
+                if (property instanceof AttributeType<?>) {
+                    builder = new AttributeTypeBuilder<>(this, (AttributeType<?>) property);
+                } else if (property instanceof FeatureAssociationRole) {
+                    builder = new AssociationRoleBuilder(this, (FeatureAssociationRole) property);
+                } else {
+                    builder = null;     // Do not create OperationWrapper now - see below.
+                }
+                /*
+                 * If the property name is one of our (Apache SIS specific) conventional names, try to reconstitute
+                 * the attribute roles that caused FeatureTypeBuilder to produce such property. Those roles usually
+                 * need to be applied on the source properties used for calculating the current property. There is
+                 * usually at most one role for each source property, but we nevertheless allow an arbitrary amount.
+                 */
+                final AttributeRole role;
+                final GenericName name = property.getName();
+                if (AttributeConvention.IDENTIFIER_PROPERTY.equals(name)) {
+                    role = AttributeRole.IDENTIFIER_COMPONENT;
+                } else if (AttributeConvention.GEOMETRY_PROPERTY.equals(name)) {
+                    role = AttributeRole.DEFAULT_GEOMETRY;
+                } else if (AttributeConvention.ENVELOPE_PROPERTY.equals(name)) {
+                    // If "@envelope" is an operation, skip it completely.
+                    // It will be recreated if a default geometry exists.
+                    role = null;
                 } else {
-                    continue;           // Skip unknown types.
+                    if (builder == null) {
+                        // For all unknown operation, wrap as-is.
+                        builder = new OperationWrapper(this, property);
+                    }
+                    role = null;
+                }
+                if (role != null) {
+                    final Set<AttributeRole> rc = Collections.singleton(role);
+                    if (property instanceof AbstractOperation) {
+                        for (final String dependency : ((AbstractOperation) property).getDependencies()) {
+                            JDK8.merge(propertyRoles, dependency, rc, AttributeRole.merge);
+                        }
+                    } else {
+                        JDK8.merge(propertyRoles, name.toString(), rc, AttributeRole.merge);
+                    }
+                }
+                if (builder != null) {
+                    properties.add(builder);
+                }
+            }
+            /*
+             * At this point we finished to collect information about the attribute roles.
+             * Now assign those roles to the attribute builders. Note that some roles may
+             * be ignored if we didn't found a suitable builder. The roles inference done
+             * in this constructor is only a "best effort".
+             */
+            if (!propertyRoles.isEmpty()) {
+                for (final Map.Entry<String,Set<AttributeRole>> entry : propertyRoles.entrySet()) {
+                    final PropertyTypeBuilder property = forName(properties, entry.getKey());
+                    if (property instanceof AttributeTypeBuilder<?>) {
+                        ((AttributeTypeBuilder<?>) property).roles().addAll(entry.getValue());
+                    }
                 }
-                properties.add(builder);
             }
         }
     }
@@ -365,7 +424,8 @@ public class FeatureTypeBuilder extends
      * @param  maximumOccurs  new default maximum number of property values.
      * @return {@code this} for allowing method calls chaining.
      *
-     * @see AttributeTypeBuilder#setCardinality(int, int)
+     * @see PropertyTypeBuilder#setMinimumOccurs(int)
+     * @see PropertyTypeBuilder#setMaximumOccurs(int)
      */
     public FeatureTypeBuilder setDefaultCardinality(final int minimumOccurs, final int maximumOccurs) {
         if (minimumOccurs < 0 || maximumOccurs < minimumOccurs) {
@@ -413,6 +473,7 @@ public class FeatureTypeBuilder extends
      *
      * @return a live list over the properties declared to this builder.
      *
+     * @see #getProperty(String)
      * @see #addAttribute(Class)
      * @see #addAttribute(AttributeType)
      * @see #addAssociation(FeatureType)
@@ -424,6 +485,21 @@ public class FeatureTypeBuilder extends
     }
 
     /**
+     * Returns the builder for the property of the given name. The given name does not need to contains all elements
+     * of a {@link org.opengis.util.ScopedName}; it is okay to specify only the tip (for example {@code "myName"}
+     * instead of {@code "myScope:myName"}) provided that ignoring the name head does not create ambiguity.
+     *
+     * @param  name   name of the property to search.
+     * @return property of the given name, or {@code null} if none.
+     * @throws IllegalArgumentException if the given name is ambiguous.
+     *
+     * @see #addProperty(PropertyType)
+     */
+    public PropertyTypeBuilder getProperty(final String name) {
+        return forName(properties, name);
+    }
+
+    /**
      * Creates a new {@code AttributeType} builder for values of the given class.
      * The default attribute name is the name of the given type, but callers should invoke one
      * of the {@code AttributeTypeBuilder.setName(…)} methods on the returned instance with a better name.
@@ -525,6 +601,37 @@ public class FeatureTypeBuilder extends
     }
 
     /**
+     * Adds the given property in the feature type properties.
+     * The given property shall be an instance of one of the following types:
+     * <ul>
+     *   <li>{@link AttributeType}, in which case this method delegate to {@link #addAttribute(AttributeType)}.</li>
+     *   <li>{@link FeatureAssociationRole}, in which case this method delegate to {@link #addAssociation(FeatureAssociationRole)}.</li>
+     *   <li>{@link Operation}, in which case the given operation object will be added verbatim in the {@code FeatureType};
+     *       this builder does not create new operations.</li>
+     * </ul>
+     *
+     * @param  template  the property to add to the feature type.
+     * @return a builder initialized to the given builder.
+     *         In the {@code Operation} case, the builder is a read-only accessor on the operation properties.
+     *
+     * @see #properties()
+     * @see #getProperty(String)
+     */
+    public PropertyTypeBuilder addProperty(final PropertyType template) {
+        ensureNonNull("template", template);
+        if (template instanceof AttributeType<?>) {
+            return addAttribute((AttributeType<?>) template);
+        } else if (template instanceof FeatureAssociationRole) {
+            return addAssociation((FeatureAssociationRole) template);
+        } else {
+            final PropertyTypeBuilder property = new OperationWrapper(this, template);
+            properties.add(property);
+            clearCache();
+            return property;
+        }
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -556,10 +663,13 @@ public class FeatureTypeBuilder extends
      * One of the {@code setName(…)} methods must have been invoked before this {@code build()} method (mandatory).
      * All other methods are optional, but some calls to a {@code add} method are usually needed.
      *
-     * @return the new feature type.
-     * @throws IllegalStateException if the feature type contains incompatible
-     *         {@linkplain AttributeTypeBuilder#setCRS CRS characteristics}.
+     * <p>If a feature type has already been built and this builder state has not changed since the
+     * feature type creation, then the previously created {@code FeatureType} instance is returned.</p>
+     *
+     * @return the feature type.
+     * @throws IllegalStateException if the builder contains inconsistent information.
      */
+    @Override
     public FeatureType build() throws IllegalStateException {
         if (feature == null) {
             /*
@@ -580,9 +690,10 @@ public class FeatureTypeBuilder extends
                 identifierTypes = new PropertyType[identifierCount];
             }
             if (defaultGeometry != null) {
-                envelopeIndex = numSynthetic;
-                geometryIndex = numSynthetic + 1;
-                numSynthetic += 2;
+                envelopeIndex = numSynthetic++;
+                if (!AttributeConvention.GEOMETRY_PROPERTY.equals(defaultGeometry.getName())) {
+                    geometryIndex = numSynthetic++;
+                }
             }
             final PropertyType[] propertyTypes = new PropertyType[numSynthetic + numSpecified];
             int propertyCursor = numSynthetic;
@@ -604,18 +715,12 @@ public class FeatureTypeBuilder extends
                  * It may happen that the property created by the user is already named "@geometry",
                  * in which case we will avoid to duplicate the property.
                  */
-                if (builder == defaultGeometry) {
+                if (builder == defaultGeometry && geometryIndex >= 0) {
                     if (propertyTypes[geometryIndex] != null) {
                         // Assuming that there is no bug in our implementation, this error could happen if the user
                         // has modified this FeatureTypeBuilder in another thread during this build() execution.
                         throw new CorruptedObjectException();
                     }
-                    if (AttributeConvention.GEOMETRY_PROPERTY.equals(instance.getName())) {
-                        System.arraycopy(propertyTypes, geometryIndex, propertyTypes, geometryIndex-1, (numSynthetic - geometryIndex) + i);
-                        geometryIndex = -1;
-                        numSynthetic--;
-                        continue;           // Skip the increment of propertyCursor.
-                    }
                     propertyTypes[geometryIndex] = FeatureOperations.link(name(AttributeConvention.GEOMETRY_PROPERTY), instance);
                 }
                 propertyCursor++;
@@ -641,7 +746,11 @@ public class FeatureTypeBuilder extends
                     // has modified this FeatureTypeBuilder in another thread during this build() execution.
                     throw new CorruptedObjectException();
                 }
-                if (identifierCursor == 1 && AttributeConvention.IDENTIFIER_PROPERTY.equals(identifierTypes[0].getName())) {
+                if (AttributeConvention.IDENTIFIER_PROPERTY.equals(identifierTypes[0].getName())) {
+                    if (identifierCursor > 1) {
+                        throw new IllegalStateException(Errors.format(Errors.Keys.PropertyAlreadyExists_2,
+                                getDisplayName(), AttributeConvention.IDENTIFIER_PROPERTY));
+                    }
                     System.arraycopy(propertyTypes, 1, propertyTypes, 0, --propertyCursor);
                 } else {
                     propertyTypes[0] = FeatureOperations.compound(name(AttributeConvention.IDENTIFIER_PROPERTY),
@@ -658,11 +767,31 @@ public class FeatureTypeBuilder extends
     /**
      * Helper method for creating identification info of synthetic attributes.
      */
-    static Map<String,?> name(final GenericName name) {
+    private static Map<String,?> name(final GenericName name) {
         return Collections.singletonMap(AbstractOperation.NAME_KEY, name);
     }
 
     /**
+     * Replaces the given builder instance by a new instance, or delete the old instance.
+     * This builder should contain exactly one instance of the given {@code old} builder.
+     *
+     * @param old  the instance to replace.
+     * @param replacement  the replacement, or {@code null} for deleting the old instance.
+     */
+    final void replace(final PropertyTypeBuilder old, final PropertyTypeBuilder replacement) {
+        final int index = properties.lastIndexOf(old);
+        if (index < 0 || (replacement != null ? properties.set(index, replacement) : properties.remove(index)) != old) {
+            // Assuming that there is no bug in our algorithm, this exception should never happen
+            // unless builder state has been changed in another thread before this method completed.
+            throw new CorruptedObjectException();
+        }
+        if (old == defaultGeometry) {
+            defaultGeometry = (AttributeTypeBuilder<?>) replacement;
+        }
+        clearCache();
+    }
+
+    /**
      * Formats a string representation of this builder for debugging purpose.
      */
     @Override



Mime
View raw message