sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1755610 [3/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/builder/PropertyTypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/PropertyTypeBuilder.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/PropertyTypeBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/PropertyTypeBuilder.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -48,14 +48,17 @@ import org.opengis.feature.FeatureAssoci
 public abstract class PropertyTypeBuilder extends TypeBuilder {
     /**
      * The feature type builder instance that created this {@code PropertyTypeBuilder}.
+     * This is set at construction time and considered as immutable until it is set to {@code null}.
+     *
+     * @see #owner()
      */
-    final FeatureTypeBuilder owner;
+    private FeatureTypeBuilder owner;
 
     /**
      * The minimum number of property values.
      * The default value is 1, unless otherwise specified by {@link #setDefaultCardinality(int, int)}.
      *
-     * @see #setCardinality(int, int)
+     * @see #getMinimumOccurs()
      */
     int minimumOccurs;
 
@@ -63,15 +66,23 @@ public abstract class PropertyTypeBuilde
      * The maximum number of property values.
      * The default value is 1, unless otherwise specified by {@link #setDefaultCardinality(int, int)}.
      *
-     * @see #setCardinality(int, int)
+     * @see #getMaximumOccurs()
      */
     int maximumOccurs;
 
     /**
-     * The attribute or 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.
+     * Creates a new builder initialized to the values of the given builder.
+     * This constructor is for {@link AttributeTypeBuilder#setValueClass(Class)} implementation.
+     *
+     * @param builder  the builder from which to copy values.
      */
-    private transient PropertyType property;
+    PropertyTypeBuilder(final PropertyTypeBuilder builder) {
+        super(builder);
+        owner         = builder.owner;
+        minimumOccurs = builder.minimumOccurs;
+        maximumOccurs = builder.maximumOccurs;
+        // Do not copy the 'property' reference since the 'valueClass' is different.
+    }
 
     /**
      * Creates a new {@code PropertyType} builder initialized to the values of an existing property.
@@ -84,7 +95,87 @@ public abstract class PropertyTypeBuilde
         this.owner    = owner;
         minimumOccurs = owner.defaultMinimumOccurs;
         maximumOccurs = owner.defaultMaximumOccurs;
-        property      = template;
+    }
+
+    /**
+     * Returns the feature type builder instance that created this {@code PropertyTypeBuilder}.
+     */
+    final FeatureTypeBuilder owner() {
+        ensureAlive(owner);
+        return owner;
+    }
+
+    /**
+     * Returns the minimum number of property values.
+     * The returned value is greater than or equal to zero.
+     *
+     * @return the minimum number of property values.
+     *
+     * @see org.apache.sis.feature.DefaultAttributeType#getMinimumOccurs()
+     */
+    public int getMinimumOccurs() {
+        return minimumOccurs;
+    }
+
+    /**
+     * Sets the minimum number of property values. If the given number is greater than the
+     * {@linkplain #getMaximumOccurs() maximal number} of property values, than the maximum
+     * is also set to that value.
+     *
+     * @param  occurs the new minimum number of property values.
+     * @return {@code this} for allowing method calls chaining.
+     *
+     * @see #getMinimumOccurs()
+     */
+    public PropertyTypeBuilder setMinimumOccurs(final int occurs) {
+        if (occurs != minimumOccurs) {
+            if (occurs < 0) {
+                throw new IllegalArgumentException(errors().getString(Errors.Keys.NegativeArgument_2, "occurs", occurs));
+            }
+            minimumOccurs = occurs;
+            if (occurs > maximumOccurs) {
+                maximumOccurs = occurs;
+            }
+            clearCache();
+        }
+        return this;
+    }
+
+    /**
+     * Returns the maximum number of property values.
+     * 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 property values, or {@link Integer#MAX_VALUE} if none.
+     *
+     * @see org.apache.sis.feature.DefaultAttributeType#getMaximumOccurs()
+     */
+    public final int getMaximumOccurs() {
+        return maximumOccurs;
+    }
+
+    /**
+     * Sets the maximum number of property values. If the given number is less than the
+     * {@linkplain #getMinimumOccurs() minimal number} of property values, than the minimum
+     * is also set to that value.
+     *
+     * @param  occurs the new maximum number of property values.
+     * @return {@code this} for allowing method calls chaining.
+     *
+     * @see #getMaximumOccurs()
+     */
+    public PropertyTypeBuilder setMaximumOccurs(final int occurs) {
+        if (occurs != maximumOccurs) {
+            if (occurs < 0) {
+                throw new IllegalArgumentException(errors().getString(Errors.Keys.NegativeArgument_2, "occurs", occurs));
+            }
+            maximumOccurs = occurs;
+            if (occurs < minimumOccurs) {
+                minimumOccurs = occurs;
+            }
+            clearCache();
+        }
+        return this;
     }
 
     /**
@@ -97,7 +188,10 @@ public abstract class PropertyTypeBuilde
      * @param  minimumOccurs  new minimum number of property values.
      * @param  maximumOccurs  new maximum number of property values.
      * @return {@code this} for allowing method calls chaining.
+     *
+     * @deprecated Replaced by {@link #setMinimumOccurs(int)} and {@link #setMaximumOccurs(int)}.
      */
+    @Deprecated
     @SuppressWarnings("unchecked")
     public PropertyTypeBuilder setCardinality(final int minimumOccurs, final int maximumOccurs) {
         if (this.minimumOccurs != minimumOccurs || this.maximumOccurs != maximumOccurs) {
@@ -123,6 +217,7 @@ public abstract class PropertyTypeBuilde
      */
     @Override
     final GenericName name(final String scope, final String localPart) {
+        ensureAlive(owner);
         return owner.name(scope, localPart);
     }
 
@@ -131,24 +226,42 @@ public abstract class PropertyTypeBuilde
      * clears that cache. This method must be invoked every time that a setter method is invoked.
      */
     @Override
-    final void clearCache() {
-        property = null;
+    void clearCache() {
+        ensureAlive(owner);
         owner.clearCache();
     }
 
     /**
-     * Returns the property type from the current setting.
-     * This method may return an existing property if it was already created.
+     * Builds the property 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 PropertyType} instance is returned
+     * (see {@link AttributeTypeBuilder#build()} for more information).
+     *
+     * @return the property type.
+     * @throws IllegalStateException if the builder contains inconsistent information.
      */
-    final PropertyType build() {
-        if (property == null) {
-            property = create();
-        }
-        return property;
+    @Override
+    public abstract PropertyType build() throws IllegalStateException;
+
+    /**
+     * Flags this builder as a disposed one. The builder should not be used anymore after this method call.
+     */
+    final void dispose() {
+        owner = null;
     }
 
     /**
-     * Creates a new property type from the current setting.
+     * Removes this property from the {@code FeatureTypeBuilder}.
+     * After this method has been invoked, this {@code PropertyTypeBuilder} instance
+     * is no longer in the list returned by {@link FeatureTypeBuilder#properties()}
+     * and attempts to invoke any setter method on {@code this} will cause an
+     * {@link IllegalStateException} to be thrown.
      */
-    abstract PropertyType create();
+    @Override
+    public void remove() {
+        if (owner != null) {
+            owner.replace(this, null);
+            dispose();
+        }
+    }
 }

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/RemoveOnlyList.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/RemoveOnlyList.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/RemoveOnlyList.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/RemoveOnlyList.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -17,7 +17,6 @@
 package org.apache.sis.feature.builder;
 
 import java.util.AbstractList;
-import java.util.Iterator;
 import java.util.List;
 
 
@@ -30,7 +29,7 @@ import java.util.List;
  * @version 0.8
  * @module
  */
-final class RemoveOnlyList<E> extends AbstractList<E> {
+final class RemoveOnlyList<E extends TypeBuilder> extends AbstractList<E> {
     /**
      * The original list to wrap.
      */
@@ -43,9 +42,31 @@ final class RemoveOnlyList<E> extends Ab
         this.elements = elements;
     }
 
-    @Override public void        clear()           {       elements.clear();}
-    @Override public int         size()            {return elements.size();}
-    @Override public E           get(int index)    {return elements.get(index);}
-    @Override public E           remove(int index) {return elements.get(index);}
-    @Override public Iterator<E> iterator()        {return elements.iterator();}
+    /**
+     * Returns the number of elements in this list.
+     */
+    @Override
+    public int size() {
+        return elements.size();
+    }
+
+    /**
+     * Returns the element at the given index.
+     */
+    @Override
+    public E get(int index) {
+        return elements.get(index);
+    }
+
+    /**
+     * Removes the element at the given index. The removed element is flagged as not usable anymore.
+     */
+    @Override
+    public E remove(int index) {
+        final E element = elements.get(index);
+        if (element != null) {
+            element.remove();
+        }
+        return element;
+    }
 }

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -18,7 +18,9 @@ package org.apache.sis.feature.builder;
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
+import org.opengis.util.ScopedName;
 import org.opengis.util.GenericName;
 import org.apache.sis.feature.AbstractIdentifiedType;
 import org.apache.sis.util.resources.Vocabulary;
@@ -34,8 +36,8 @@ import org.opengis.feature.IdentifiedTyp
 
 
 /**
- * Properties common to all kind of types (feature, association, characteristics).
- * Those properties are:
+ * Information common to all kind of types (feature, association, characteristics).
+ * Those information are:
  *
  * <ul>
  *   <li>the name        — a unique name which can be defined within a scope (or namespace).</li>
@@ -44,6 +46,7 @@ import org.opengis.feature.IdentifiedTyp
  *   <li>the description — information beyond that required for concise definition of the element.</li>
  * </ul>
  *
+ * <div class="section">Default namespace</div>
  * In many cases, the names of all {@code AttributeType}s and {@code AssociationRole}s to create
  * within a {@code FeatureType} share the same namespace.
  * For making name creations more convenient, a default namespace can be
@@ -61,12 +64,22 @@ public abstract class TypeBuilder implem
      * The feature name, definition, designation and description.
      * The name is mandatory; all other information are optional.
      */
-    private final Map<String,Object> identification = new HashMap<>(4);
+    private final Map<String,Object> identification;
+
+    /**
+     * Creates a new builder initialized to the values of the given builder.
+     *
+     * @param builder  the builder from which to copy information.
+     */
+    TypeBuilder(final TypeBuilder builder) {
+        identification = new HashMap<>(builder.identification);
+    }
 
     /**
      * Creates a new builder initialized to the values of an existing type.
      */
     TypeBuilder(final IdentifiedType template, final Locale locale) {
+        identification = new HashMap<>(4);
         putIfNonNull(Errors.LOCALE_KEY, locale);
         if (template != null) {
             putIfNonNull(AbstractIdentifiedType.NAME_KEY,        template.getName());
@@ -137,6 +150,7 @@ public abstract class TypeBuilder implem
      * @return the name of the {@code IdentifiedType} to create (may be a default name or {@code null}).
      *
      * @see AbstractIdentifiedType#getName()
+     * @see #setName(GenericName)
      */
     public GenericName getName() {
         return (GenericName) identification().get(AbstractIdentifiedType.NAME_KEY);
@@ -170,6 +184,7 @@ public abstract class TypeBuilder implem
      * @return {@code this} for allowing method calls chaining.
      *
      * @see #getName()
+     * @see #setName(String)
      * @see AbstractIdentifiedType#NAME_KEY
      */
     public TypeBuilder setName(final GenericName name) {
@@ -193,6 +208,7 @@ public abstract class TypeBuilder implem
      * @return {@code this} for allowing method calls chaining.
      *
      * @see #getName()
+     * @see #setName(String, String)
      */
     public TypeBuilder setName(final String localPart) {
         ensureNonEmpty("localPart", localPart);
@@ -214,6 +230,7 @@ public abstract class TypeBuilder implem
      * @return {@code this} for allowing method calls chaining.
      *
      * @see #getName()
+     * @see #setName(String)
      */
     public TypeBuilder setName(String scope, final String localPart) {
         ensureNonEmpty("localPart", localPart);
@@ -309,6 +326,46 @@ public abstract class TypeBuilder implem
     }
 
     /**
+     * Returns the element of the given name in the given list. The given name does not need to contains
+     * all elements of a {@link ScopedName}; it can be only the tip (for example {@code "myName"} instead
+     * of {@code "myScope:myName"}) provided that ignoring the name head does not create ambiguity.
+     *
+     * @param  types  the collection where to search for an element of the given name.
+     * @param  name   name of the element to search.
+     * @return element of the given name, or {@code null} if none were found.
+     * @throws IllegalArgumentException if the given name is ambiguous.
+     */
+    final <E extends TypeBuilder> E forName(final List<E> types, final String name) {
+        E best      = null;                     // Best type found so far.
+        E ambiguity = null;                     // If two types are found at the same depth, the other type.
+        int depth   = Integer.MAX_VALUE;        // Number of path elements that we had to ignore in the GenericName.
+        for (final E type : types) {
+            GenericName candidate = type.getName();
+            for (int d=0; candidate != null; d++) {
+                if (name.equals(candidate.toString())) {
+                    if (d < depth) {
+                        best      = type;
+                        ambiguity = null;
+                        depth     = d;
+                        break;
+                    }
+                    if (d == depth) {
+                        ambiguity = type;
+                        break;
+                    }
+                }
+                if (!(candidate instanceof ScopedName)) break;
+                candidate = ((ScopedName) candidate).tail();
+            }
+        }
+        if (ambiguity != null) {
+            throw new IllegalArgumentException(errors().getString(
+                    Errors.Keys.AmbiguousName_3, best.getName(), ambiguity.getName(), name));
+        }
+        return best;
+    }
+
+    /**
      * Returns the locale used for formatting error messages, or {@code null} if unspecified.
      * If unspecified, the system default locale will be used.
      *
@@ -341,6 +398,17 @@ public abstract class TypeBuilder implem
     }
 
     /**
+     * Ensures that this instance is still alive.
+     *
+     * @param owner  the owner of this instance. A value of null means that this instance should not be used any more.
+     */
+    final void ensureAlive(final TypeBuilder owner) {
+        if (owner == null) {
+            throw new IllegalStateException(errors().getString(Errors.Keys.DisposedInstanceOf_1, getClass()));
+        }
+    }
+
+    /**
      * Same as {@link org.apache.sis.util.ArgumentChecks#ensureNonEmpty(String, CharSequence)},
      * but uses the current locale in case of error.
      *
@@ -372,7 +440,7 @@ public abstract class TypeBuilder implem
 
     /**
      * Partial implementation of {@link #toString()}. This method assumes that the class name
-     * has already been written in the buffer.
+     * has already be written in the buffer.
      */
     final StringBuilder toString(final StringBuilder buffer) {
         toStringInternal(buffer.append("[“").append(getDisplayName()).append('”'));
@@ -384,4 +452,21 @@ public abstract class TypeBuilder implem
      */
     void toStringInternal(StringBuilder buffer) {
     }
+
+    /**
+     * Invoked when a type builder has been removed from its parent.
+     * Subclasses should override this method in a way that flag the builder as not usable anymore.
+     */
+    void remove() {
+    }
+
+    /**
+     * Builds the feature or property 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 IdentifiedType} instance is returned.
+     *
+     * @return the feature or property type.
+     * @throws IllegalStateException if the builder contains inconsistent information.
+     */
+    public abstract IdentifiedType build() throws IllegalStateException;
 }

Modified: sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/package-info.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/package-info.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/builder/package-info.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -21,12 +21,13 @@
  * their attributes and associations.
  *
  * <p>The starting point is {@link org.apache.sis.feature.builder.FeatureTypeBuilder}.
- * Example:</p>
+ * The following example creates a feature type for a capital, as a special kind of city,
+ * named "Utopia" by default:</p>
  *
  * {@preformat java
  *     // Create a feature type for a city, which contains a name and a population.
  *     FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("City");
- *     builder.addAttribute(String.class).setName("name");
+ *     builder.addAttribute(String.class).setName("name").setDefaultValue("Utopia");
  *     builder.addAttribute(Integer.class).setName("population");
  *     FeatureType city = builder.build();
  *
@@ -36,6 +37,19 @@
  *     FeatureType capital = builder.build();
  * }
  *
+ * A call to {@code System.out.println(capital)} prints the following table:
+ *
+ * {@preformat text
+ *   Capital ⇾ City
+ *   ┌────────────┬─────────┬─────────────┬───────────────┐
+ *   │ Name       │ Type    │ Cardinality │ Default value │
+ *   ├────────────┼─────────┼─────────────┼───────────────┤
+ *   │ name       │ String  │ [1 … 1]     │ Utopia        │
+ *   │ population │ Integer │ [1 … 1]     │               │
+ *   │ parliament │ String  │ [1 … 1]     │               │
+ *   └────────────┴─────────┴─────────────┴───────────────┘
+ * }
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.8

Modified: sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -34,6 +34,7 @@ import static org.apache.sis.test.TestUt
 
 // Branch-dependent imports
 import org.opengis.feature.PropertyType;
+import org.opengis.feature.AttributeType;
 
 
 /**
@@ -41,7 +42,7 @@ import org.opengis.feature.PropertyType;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 @DependsOn(DefaultAttributeTypeTest.class)
@@ -250,11 +251,11 @@ public final strictfp class DefaultFeatu
         final Map<String,Object> identification = new HashMap<>();
         final DefaultAttributeType<String>  city       = DefaultAttributeTypeTest.city(identification);
         final DefaultAttributeType<Integer> population = DefaultAttributeTypeTest.population(identification);
-        testComplex(city, population, 0, 0); // Simple
+        testComplex(city, population, 0, 0);    // Simple
         testComplex(city, population, 0, 1);
         testComplex(city, population, 0, 2);
         testComplex(city, population, 1, 2);
-        testComplex(city, population, 1, 1); // Simple
+        testComplex(city, population, 1, 1);    // Simple
     }
 
     /**
@@ -308,8 +309,8 @@ public final strictfp class DefaultFeatu
 
         final Map<String,String> identification = singletonMap(DefaultAttributeType.NAME_KEY, "City");
         try {
-            new DefaultFeatureType(identification, false, null, city, population, cityId);
-            fail("Duplicated attribute names shall not be allowed.");
+            final Object t = new DefaultFeatureType(identification, false, null, city, population, cityId);
+            fail("Duplicated attribute names shall not be allowed:\n" + t);
         } catch (IllegalArgumentException e) {
             final String message = e.getMessage();
             assertTrue(message, message.contains("name"));      // Property name.
@@ -361,6 +362,33 @@ public final strictfp class DefaultFeatu
     }
 
     /**
+     * Tests inclusion of a property of kind operation.
+     */
+    @Test
+    public void testOperationProperty() {
+        final Map<String,String> featureName = singletonMap(DefaultFeatureType.NAME_KEY, "Identified city");
+        final Map<String,String> identifierName = singletonMap(DefaultAttributeType.NAME_KEY, "identifier");
+        final DefaultFeatureType[] parent = {city()};
+        final DefaultFeatureType city = new DefaultFeatureType(featureName, false,
+                parent, new LinkOperation(identifierName, parent[0].getProperty("city")));
+        assertPropertiesEquals(city, true, "city", "population", "identifier");
+        /*
+         * Try to add an operation that depends on a non-existent property.
+         * Such construction shall not be allowed.
+         */
+        final PropertyType parliament = new LinkOperation(identifierName, DefaultAttributeTypeTest.parliament());
+        try {
+            final DefaultFeatureType illegal = new DefaultFeatureType(featureName, false, parent, parliament);
+            fail("Should not have been allowed to create this feature:\n" + illegal);
+        } catch (IllegalArgumentException e) {
+            final String message = e.getLocalizedMessage();
+            assertTrue(message, message.contains("identifier"));
+            assertTrue(message, message.contains("parliament"));
+            assertTrue(message, message.contains("Identified city"));
+        }
+    }
+
+    /**
      * Tests a feature type which inherit from an other feature type, but without property overriding.
      *
      * <p>Current implementation performs its tests on the {@link #capital()} feature.</p>
@@ -416,7 +444,7 @@ public final strictfp class DefaultFeatu
         assertPropertiesEquals(metroCapital, false, "country");
         assertPropertiesEquals(metroCapital, true, "city", "population", "region", "isGlobal", "parliament", "country");
         assertEquals("property(“region”).valueClass", CharSequence.class,
-                ((DefaultAttributeType) metroCapital.getProperty("region")).getValueClass());
+                ((AttributeType<?>) metroCapital.getProperty("region")).getValueClass());
 
         // Check based only on name.
         assertTrue ("maybeAssignableFrom", DefaultFeatureType.maybeAssignableFrom(capital, metroCapital));
@@ -460,7 +488,7 @@ public final strictfp class DefaultFeatu
         assertPropertiesEquals(worldMetropolis, false, "region", "temperature");
         assertPropertiesEquals(worldMetropolis, true, "city", "population", "region", "isGlobal", "universities", "temperature");
         assertEquals("property(“region”).valueClass", InternationalString.class,
-                ((DefaultAttributeType) worldMetropolis.getProperty("region")).getValueClass());
+                ((AttributeType<?>) worldMetropolis.getProperty("region")).getValueClass());
 
         // Check based only on name.
         assertTrue ("maybeAssignableFrom", DefaultFeatureType.maybeAssignableFrom(metropolis, worldMetropolis));
@@ -472,6 +500,22 @@ public final strictfp class DefaultFeatu
     }
 
     /**
+     * Tests the ommission of a property that duplicate a property already declared in the parent.
+     * This is a little bit different than {@link #testPropertyOverride()} since the duplicated property
+     * should be completely omitted.
+     */
+    @Test
+    @DependsOnMethod("testPropertyOverride")
+    public void testPropertyDuplication() {
+        DefaultFeatureType city = city();
+        city = new DefaultFeatureType(singletonMap(DefaultFeatureType.NAME_KEY, "New-City"),
+                false, new DefaultFeatureType[] {city()}, city.getProperty("city"));
+
+        assertPropertiesEquals(city, false);
+        assertPropertiesEquals(city, true, "city", "population");
+    }
+
+    /**
      * Tests {@link DefaultFeatureType#equals(Object)}.
      */
     @Test

Modified: sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureFormatTest.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -32,7 +32,7 @@ import static org.apache.sis.test.Assert
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 @DependsOn({
@@ -48,7 +48,7 @@ public final strictfp class FeatureForma
         final DefaultFeatureType feature = DefaultFeatureTypeTest.worldMetropolis();
         final FeatureFormat format = new FeatureFormat(Locale.US, null);
         final String text = format.format(feature);
-        assertMultilinesEquals("World metropolis\n" +
+        assertMultilinesEquals("World metropolis ⇾ Metropolis, University city\n" +
                 "┌──────────────┬─────────────────────┬─────────────┬───────────────┬────────────────────────────┐\n" +
                 "│ Name         │ Type                │ Cardinality │ Default value │ Characteristics            │\n" +
                 "├──────────────┼─────────────────────┼─────────────┼───────────────┼────────────────────────────┤\n" +

Modified: sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.feature;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import org.opengis.metadata.quality.DataQuality;
@@ -31,6 +32,11 @@ import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
 
+// Branch-dependent imports
+import org.opengis.feature.Attribute;
+import org.opengis.feature.AttributeType;
+import org.opengis.feature.Property;
+
 
 /**
  * Tests common to {@link DenseFeatureTest} and {@link SparseFeatureTest}.
@@ -38,7 +44,7 @@ import static org.apache.sis.test.Assert
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Marc le Bihan
  * @since   0.5
- * @version 0.6
+ * @version 0.8
  * @module
  */
 public abstract strictfp class FeatureTestCase extends TestCase {
@@ -70,29 +76,50 @@ public abstract strictfp class FeatureTe
     abstract AbstractFeature cloneFeature() throws CloneNotSupportedException;
 
     /**
+     * Asserts that {@link AbstractFeature#getProperty(String)} returns the given instance.
+     * This assertion is verified after a call to {@link AbstractFeature#setProperty(Property)}
+     * and should be true for all Apache SIS concrete implementations. But it is not guaranteed
+     * to be true for non-SIS implementations, for example built on top of {@code AbstractFeature}
+     * without overriding {@code setProperty(Property)}.
+     * Consequently, this assertion needs to be relaxed by {@link AbstractFeatureTest}.
+     *
+     * @param name      the property name to check.
+     * @param expected  the expected property instance.
+     * @param modified  {@code true} if {@code expected} has been modified <strong>after</strong> it has been set
+     *                  to the {@link #feature} instance. Not all feature implementations can see such changes.
+     * @return {@code true} if the property is the expected instance, or {@code false} if it is another instance.
+     */
+    boolean assertSameProperty(final String name, final Property expected, final boolean modified) {
+        assertSame(name, expected, feature.getProperty(name));
+        return true;
+    }
+
+    /**
      * Returns the attribute value of the current {@link #feature} for the given name.
      */
     private Object getAttributeValue(final String name) {
         final Object value = feature.getPropertyValue(name);
         if (getValuesFromProperty) {
-            final AbstractAttribute<?> property = (AbstractAttribute<?>) feature.getProperty(name);
-
-            // The AttributeType shall be the same than the one provided by FeatureType for the given name.
+            /*
+             * Verifies consistency with the Attribute instance:
+             *   - The AttributeType shall be the same than the one provided by FeatureType for the given name.
+             *   - Attribute value shall be the same than the one we got at the beginning of this method.
+             *   - Attribute values (as a collection) is either empty or contains the same value.
+             */
+            final Attribute<?> property = (Attribute<?>) feature.getProperty(name);
             assertSame(name, feature.getType().getProperty(name), property.getType());
-
-            // Attribute value shall be the same than the one provided by FeatureType convenience method.
             assertSame(name, value, property.getValue());
-
-            // Collection view shall contains the same value, or be empty.
             final Collection<?> values = property.getValues();
             if (value != null) {
                 assertSame(name, value, TestUtilities.getSingleton(values));
             } else {
                 assertTrue(name, values.isEmpty());
             }
-
-            // Invoking getProperty(name) twice shall return the same Property instance.
-            assertSame(name, property, feature.getProperty(name));
+            /*
+             * Invoking getProperty(name) twice should return the same Property instance at least with
+             * Apache SIS Feature implementations. Other implementations may relax this requirement.
+             */
+            assertSameProperty(name, property, false);
         }
         return value;
     }
@@ -102,9 +129,9 @@ public abstract strictfp class FeatureTe
      * First, this method verifies that the previous value is equals to the given one.
      * Then, this method set the attribute to the given value and check if the result.
      *
-     * @param name     The name of the attribute to set.
-     * @param oldValue The expected old value (may be {@code null}).
-     * @param newValue The new value to set.
+     * @param name      the name of the attribute to set.
+     * @param oldValue  the expected old value (may be {@code null}).
+     * @param newValue  the new value to set.
      */
     private void setAttributeValue(final String name, final Object oldValue, final Object newValue) {
         assertEquals(name, oldValue, getAttributeValue(name));
@@ -125,7 +152,7 @@ public abstract strictfp class FeatureTe
     @Test
     public void testGetProperty() {
         final DefaultFeatureType type = new DefaultFeatureType(
-                Collections.singletonMap(DefaultFeatureType.NAME_KEY, "My shapefile"), false, (DefaultFeatureType[]) null,
+                Collections.singletonMap(DefaultFeatureType.NAME_KEY, "My shapefile"), false, null,
                 DefaultAttributeTypeTest.attribute("COMMUNE"),
                 DefaultAttributeTypeTest.attribute("REF_INSEE"),
                 DefaultAttributeTypeTest.attribute("CODE_POSTAL"));
@@ -146,14 +173,31 @@ public abstract strictfp class FeatureTe
 
     /**
      * Tests the {@link AbstractFeature#getPropertyValue(String)} method on a simple feature without super-types.
-     * This method also tests that attempts to set a value of the wrong type throw an exception and leave the
-     * previous value unchanged, that the feature is cloneable and that serialization works.
+     * This method:
+     *
+     * <ul>
+     *   <li>Verifies setting attribute values.</li>
+     *   <li>Verifies that attempts to set an attribute value of the wrong type throw an exception
+     *       and leave the previous value unchanged.</li>
+     *   <li>Verifies feature clone.</li>
+     *   <li>Verifies serialization.</li>
+     * </ul>
      */
     @Test
     @DependsOnMethod("testGetProperty")
-    public void testSimpleValues() {
+    public final void testSimpleValues() {
         feature = createFeature(DefaultFeatureTypeTest.city());
         setAttributeValue("city", "Utopia", "Atlantide");
+        /*
+         * At this point we have the following "City" feature:
+         *   ┌────────────┬─────────┬─────────────┬───────────┐
+         *   │ Name       │ Type    │ Cardinality │ Value     │
+         *   ├────────────┼─────────┼─────────────┼───────────┤
+         *   │ city       │ String  │ [1 … 1]     │ Atlantide │
+         *   │ population │ Integer │ [1 … 1]     │           │
+         *   └────────────┴─────────┴─────────────┴───────────┘
+         * Verify that attempt to set an illegal value fail.
+         */
         try {
             feature.setPropertyValue("city", 2000);
             fail("Shall not be allowed to set a value of the wrong type.");
@@ -167,9 +211,9 @@ public abstract strictfp class FeatureTe
          * Before we set the population attribute, the feature should be considered invalid.
          * After we set it, the feature should be valid since all mandatory attributes are set.
          */
-        assertQualityReports("population");
+        verifyQualityReports("population");
         setAttributeValue("population", null, 1000);
-        assertQualityReports();
+        verifyQualityReports();
         /*
          * Opportunist tests using the existing instance.
          */
@@ -242,15 +286,15 @@ public abstract strictfp class FeatureTe
          * Before we set the 'isGlobal' attribute, the feature should be considered invalid.
          * After we set it, the feature should be valid since all mandatory attributes are set.
          */
-        assertQualityReports("isGlobal", "temperature");
+        verifyQualityReports("isGlobal", "temperature");
         setAttributeValue("isGlobal", null, Boolean.TRUE);
-        assertQualityReports("temperature");
+        verifyQualityReports("temperature");
         /*
          * Opportunist tests using the existing instance.
          */
         testSerialization();
         try {
-            testClone("population", 8405837, 8405838); // A birth...
+            testClone("population", 8405837, 8405838);          // A birth...
         } catch (CloneNotSupportedException e) {
             throw new AssertionError(e);
         }
@@ -265,7 +309,7 @@ public abstract strictfp class FeatureTe
         feature = createFeature(DefaultFeatureTypeTest.city());
         final AbstractAttribute<String> wrong = SingletonAttributeTest.parliament();
         final CustomAttribute<String> city = new CustomAttribute<>(Features.cast(
-                (DefaultAttributeType<?>) feature.getType().getProperty("city"), String.class));
+                (AttributeType<?>) feature.getType().getProperty("city"), String.class));
 
         feature.setProperty(city);
         setAttributeValue("city", "Utopia", "Atlantide");
@@ -277,33 +321,57 @@ public abstract strictfp class FeatureTe
             assertTrue(message, message.contains("parliament"));
             assertTrue(message, message.contains("City"));
         }
-        assertSame(city, feature.getProperty("city"));
-        /*
-         * The quality report is expected to contains a custom element.
-         */
-        int numOccurrences = 0;
-        final DataQuality quality = assertQualityReports("population");
-        for (final Element report : quality.getReports()) {
-            final String identifier = report.getMeasureIdentification().toString();
-            if (identifier.equals("city")) {
-                numOccurrences++;
-                final Result result = TestUtilities.getSingleton(report.getResults());
-                assertInstanceOf("result", QuantitativeResult.class, result);
-                assertEquals("quality.report.result.errorStatistic",
-                        CustomAttribute.ADDITIONAL_QUALITY_INFO,
-                        String.valueOf(((QuantitativeResult) result).getErrorStatistic()));
+        if (assertSameProperty("city", city, true)) {
+            /*
+             * The quality report is expected to contains a custom element.
+             */
+            int numOccurrences = 0;
+            final DataQuality quality = verifyQualityReports("population");
+            for (final Element report : quality.getReports()) {
+                final String identifier = report.getMeasureIdentification().toString();
+                if (identifier.equals("city")) {
+                    numOccurrences++;
+                    final Result result = TestUtilities.getSingleton(report.getResults());
+                    assertInstanceOf("result", QuantitativeResult.class, result);
+                    assertEquals("quality.report.result.errorStatistic",
+                            CustomAttribute.ADDITIONAL_QUALITY_INFO,
+                            String.valueOf(((QuantitativeResult) result).getErrorStatistic()));
+                }
             }
+            assertEquals("Number of reports.", 1, numOccurrences);
         }
-        assertEquals("Number of reports.", 1, numOccurrences);
+    }
+
+    /**
+     * Tests addition of values in a multi-valued property.
+     */
+    @Test
+    @DependsOnMethod("testSimpleProperties")
+    public void testAddToCollection() {
+        feature = createFeature(new DefaultFeatureType(
+                Collections.singletonMap(DefaultFeatureType.NAME_KEY, "City"),
+                false, null, DefaultAttributeTypeTest.universities()));
+        /*
+         * The value below is an instance of Collection<String>. But as of Java 8, the <String> parameterized type
+         * can not be verified at runtime. The best check we can have is Collection<?>, which does not allow addition
+         * of new values.
+         */
+        Collection<?> values = (Collection<?>) feature.getPropertyValue("universities");
+        assertTrue("isEmpty", values.isEmpty());
+        // Can not perform values.add("something") here.
+
+        feature.setPropertyValue("universities", Arrays.asList("UCAR", "Marie-Curie"));
+        values = (Collection<?>) feature.getPropertyValue("universities");
+        assertArrayEquals(new String[] {"UCAR", "Marie-Curie"}, values.toArray());
     }
 
     /**
      * Asserts that {@link AbstractFeature#quality()} reports no anomaly, or only anomalies for the given properties.
      *
-     * @param  anomalousProperties The property for which we expect a report.
-     * @return The data quality report.
+     * @param  anomalousProperties  the property for which we expect a report.
+     * @return the data quality report.
      */
-    private DataQuality assertQualityReports(final String... anomalousProperties) {
+    private DataQuality verifyQualityReports(final String... anomalousProperties) {
         int anomalyIndex  = 0;
         final DataQuality quality = feature.quality();
         for (final Element report : quality.getReports()) {
@@ -327,9 +395,9 @@ public abstract strictfp class FeatureTe
      * Tests the {@link AbstractFeature#clone()} method on the current {@link #feature} instance.
      * This method is invoked from other test methods using the existing feature instance in an opportunist way.
      *
-     * @param  property The name of a property to change.
-     * @param  oldValue The old value of the given property.
-     * @param  newValue The new value of the given property.
+     * @param  property  the name of a property to change.
+     * @param  oldValue  the old value of the given property.
+     * @param  newValue  the new value of the given property.
      * @throws CloneNotSupportedException Should never happen.
      */
     private void testClone(final String property, final Object oldValue, final Object newValue)

Modified: sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -23,6 +23,7 @@ import org.apache.sis.internal.feature.A
 import org.apache.sis.feature.DefaultFeatureTypeTest;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -44,34 +45,13 @@ import org.opengis.feature.PropertyType;
  * @version 0.8
  * @module
  */
+@DependsOn(AttributeTypeBuilderTest.class)
 public final strictfp class FeatureTypeBuilderTest extends TestCase {
     /**
-     * Tests a {@code FeatureTypeBuilder.Property} with the minimum number of parameters.
+     * Tests with the minimum number of parameters (no property and no super type).
      */
     @Test
-    public void testEmptyProperty() {
-        final AttributeTypeBuilder<String> builder = new FeatureTypeBuilder().addAttribute(String.class);
-        assertEquals("default name", "string", builder.getName().toString());
-
-        builder.setName("myScope", "myName");
-        final AttributeType<?> att = (AttributeType<?>) builder.build();
-
-        assertEquals("name", "myScope:myName",   att.getName().toString());
-        assertEquals("valueClass", String.class, att.getValueClass());
-        assertNull  ("defaultValue",             att.getDefaultValue());
-        assertNull  ("definition",               att.getDefinition());
-        assertNull  ("description",              att.getDescription());
-        assertNull  ("designation",              att.getDesignation());
-        assertEquals("minimumOccurs", 1,         att.getMinimumOccurs());
-        assertEquals("maximumOccurs", 1,         att.getMaximumOccurs());
-    }
-
-    /**
-     * Tests a {@code FeatureTypeBuilder} with the minimum number of parameters (no property and no super type).
-     */
-    @Test
-    @DependsOnMethod("testEmptyProperty")
-    public void testEmptyFeature() {
+    public void testInitialization() {
         final FeatureTypeBuilder builder = new FeatureTypeBuilder();
         try {
             builder.build();
@@ -80,14 +60,7 @@ public final strictfp class FeatureTypeB
             final String message = ex.getMessage();
             assertTrue(message, message.contains("name"));
         }
-        testEmptyFeature(builder);
-    }
-
-    /**
-     * Implementation of {@link #testEmptyFeature()} using the given pre-existing builder.
-     */
-    private static void testEmptyFeature(final FeatureTypeBuilder builder) {
-        builder.setName("scope", "test");
+        assertSame(builder, builder.setName("scope", "test"));
         final FeatureType type = builder.build();
 
         assertEquals("name", "scope:test",   type.getName().toString());
@@ -97,52 +70,17 @@ public final strictfp class FeatureTypeB
     }
 
     /**
-     * Test creation of a single property.
-     */
-    @Test
-    @DependsOnMethod("testEmptyProperty")
-    public void testPropertyBuild() {
-        final AttributeTypeBuilder<String> builder = new FeatureTypeBuilder().addAttribute(String.class);
-        builder.setName        ("myScope", "myName");
-        builder.setDefinition  ("test definition");
-        builder.setDesignation ("test designation");
-        builder.setDescription ("test description");
-        builder.setDefaultValue("test default value.");
-        builder.setCardinality(10, 60);
-        builder.setMaximalLength(80);
-        final AttributeType<?> att = (AttributeType<?>) builder.build();
-
-        assertEquals("name",          "myScope:myName",      att.getName().toString());
-        assertEquals("definition",    "test definition",     att.getDefinition().toString());
-        assertEquals("description",   "test description",    att.getDescription().toString());
-        assertEquals("designation",   "test designation",    att.getDesignation().toString());
-        assertEquals("valueClass",    String.class,          att.getValueClass());
-        assertEquals("defaultValue",  "test default value.", att.getDefaultValue());
-        assertEquals("minimumOccurs", 10,                    att.getMinimumOccurs());
-        assertEquals("maximumOccurs", 60,                    att.getMaximumOccurs());
-        assertTrue  ("characterizedByMaximalLength", AttributeConvention.characterizedByMaximalLength(att));
-        assertEquals("maximalLengthCharacteristic", Integer.valueOf(80),
-                AttributeConvention.getMaximalLengthCharacteristic(att.newInstance()));
-    }
-
-    /**
      * Tests {@link FeatureTypeBuilder#addAttribute(Class)}.
      */
     @Test
-    @DependsOnMethod("testEmptyFeature")
-    public void testAddProperties() {
-        testAddProperties(new FeatureTypeBuilder());
-    }
-
-    /**
-     * Implementation of {@link #testAddProperties()} using the given pre-existing builder.
-     */
-    private static void testAddProperties(final FeatureTypeBuilder builder) {
-        builder.setName("myScope", "myName");
-        builder.setDefinition ("test definition");
-        builder.setDesignation("test designation");
-        builder.setDescription("test description");
-        builder.setAbstract(true);
+    @DependsOnMethod("testInitialization")
+    public void testAddAttribute() {
+        final FeatureTypeBuilder builder = new FeatureTypeBuilder();
+        assertSame(builder, builder.setName("myScope", "myName"));
+        assertSame(builder, builder.setDefinition ("test definition"));
+        assertSame(builder, builder.setDesignation("test designation"));
+        assertSame(builder, builder.setDescription("test description"));
+        assertSame(builder, builder.setAbstract(true));
         builder.addAttribute(String .class).setName("name");
         builder.addAttribute(Integer.class).setName("age");
         builder.addAttribute(Point  .class).setName("location").setCRS(HardCodedCRS.WGS84);
@@ -194,14 +132,15 @@ public final strictfp class FeatureTypeB
     }
 
     /**
-     * Tests {@link FeatureTypeBuilder#addIdentifier(Class)}.
+     * Tests {@link FeatureTypeBuilder#addAttribute(Class)} where one property is an identifier
+     * and another property is the geometry.
      */
     @Test
-    @DependsOnMethod("testAddProperties")
-    public void testAddIdentifier() {
+    @DependsOnMethod("testAddAttribute")
+    public void testAddIdentifierAndGeometry() {
         final FeatureTypeBuilder builder = new FeatureTypeBuilder();
-        builder.setName("scope", "test");
-        builder.setIdentifierDelimiters("-", "pref.", null);
+        assertSame(builder, builder.setName("scope", "test"));
+        assertSame(builder, builder.setIdentifierDelimiters("-", "pref.", null));
         builder.addAttribute(String.class).setName("name")
                 .addRole(AttributeRole.IDENTIFIER_COMPONENT);
         builder.addAttribute(Geometry.class).setName("shape")
@@ -228,6 +167,54 @@ public final strictfp class FeatureTypeB
     }
 
     /**
+     * Tests {@link FeatureTypeBuilder#addAttribute(Class)} where one attribute is an identifier that already has
+     * the {@code "@identifier"} name. This is called "anonymous" because identifiers with an explicit name in the
+     * data file should use that name instead in the feature type.
+     */
+    @Test
+    @DependsOnMethod("testAddIdentifierAndGeometry")
+    public void testAddAnonymousIdentifier() {
+        final FeatureTypeBuilder builder = new FeatureTypeBuilder();
+        assertSame(builder, builder.setName("City"));
+        builder.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY).addRole(AttributeRole.IDENTIFIER_COMPONENT);
+        builder.addAttribute(Integer.class).setName("population");
+        final FeatureType type = builder.build();
+        final Iterator<? extends PropertyType> it = type.getProperties(true).iterator();
+        final PropertyType a0 = it.next();
+        final PropertyType a1 = it.next();
+        assertFalse("properties count", it.hasNext());
+        assertEquals("name", AttributeConvention.IDENTIFIER_PROPERTY, a0.getName());
+        assertEquals("type", String.class,  ((AttributeType<?>) a0).getValueClass());
+        assertEquals("name", "population", a1.getName().toString());
+        assertEquals("type", Integer.class, ((AttributeType<?>) a1).getValueClass());
+    }
+
+    /**
+     * Tests {@link FeatureTypeBuilder#addAttribute(Class)} where one attribute is a geometry that already has
+     * the {@code "@geometry"} name. This is called "anonymous" because geometries with an explicit name in the
+     * data file should use that name instead in the feature type.
+     */
+    @Test
+    @DependsOnMethod("testAddIdentifierAndGeometry")
+    public void testAddAnonymousGeometry() {
+        final FeatureTypeBuilder builder = new FeatureTypeBuilder();
+        assertSame(builder, builder.setName("City"));
+        builder.addAttribute(Point.class).setName(AttributeConvention.GEOMETRY_PROPERTY).addRole(AttributeRole.DEFAULT_GEOMETRY);
+        builder.addAttribute(Integer.class).setName("population");
+        final FeatureType type = builder.build();
+        final Iterator<? extends PropertyType> it = type.getProperties(true).iterator();
+        final PropertyType a0 = it.next();
+        final PropertyType a1 = it.next();
+        final PropertyType a2 = it.next();
+        assertFalse("properties count", it.hasNext());
+        assertEquals("name", AttributeConvention.ENVELOPE_PROPERTY, a0.getName());
+        assertEquals("name", AttributeConvention.GEOMETRY_PROPERTY, a1.getName());
+        assertEquals("type", Point.class,   ((AttributeType<?>) a1).getValueClass());
+        assertEquals("name", "population", a2.getName().toString());
+        assertEquals("type", Integer.class, ((AttributeType<?>) a2).getValueClass());
+    }
+
+    /**
      * Tests creation of a builder from an existing feature type.
      * This method also acts as a test of {@code FeatureTypeBuilder} getter methods.
      */
@@ -237,5 +224,68 @@ public final strictfp class FeatureTypeB
         assertEquals("name",       "Capital", builder.getName().toString());
         assertEquals("superTypes", "City",    TestUtilities.getSingleton(builder.getSuperTypes()).getName().toString());
         assertFalse ("isAbstract",            builder.isAbstract());
+
+        // The list of properties does not include super-type properties.
+        final AttributeTypeBuilder<?> a0 = (AttributeTypeBuilder<?>) TestUtilities.getSingleton(builder.properties());
+        assertEquals("name",  "parliament",  a0.getName().toString());
+        assertEquals("type",  String.class,  a0.getValueClass());
+        assertTrue  ("roles",                a0.roles().isEmpty());
+    }
+
+    /**
+     * Tests creation of a builder from an existing feature type with some attributes having {@link AttributeRole}s.
+     */
+    @Test
+    @DependsOnMethod("testCreateFromTemplate")
+    public void testCreateFromTemplateWithRoles() {
+        FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("City");
+        builder.addAttribute(String  .class).setName("name").roles().add(AttributeRole.IDENTIFIER_COMPONENT);
+        builder.addAttribute(Integer .class).setName("population");
+        builder.addAttribute(Geometry.class).setName("area").roles().add(AttributeRole.DEFAULT_GEOMETRY);
+
+        final FeatureType type = builder.build();
+        builder = new FeatureTypeBuilder(type);
+        assertEquals("name",       "City", builder.getName().toString());
+        assertEquals("superTypes", 0, builder.getSuperTypes().length);
+
+        final Iterator<PropertyTypeBuilder> it = builder.properties().iterator();
+        final AttributeTypeBuilder<?> a0 = (AttributeTypeBuilder<?>) it.next();
+        final AttributeTypeBuilder<?> a1 = (AttributeTypeBuilder<?>) it.next();
+        final AttributeTypeBuilder<?> a2 = (AttributeTypeBuilder<?>) it.next();
+        assertFalse("properties count", it.hasNext());
+        assertEquals("name", "name",       a0.getName().toString());
+        assertEquals("name", "population", a1.getName().toString());
+        assertEquals("name", "area",       a2.getName().toString());
+
+        assertEquals("type", String.class,   a0.getValueClass());
+        assertEquals("type", Integer.class,  a1.getValueClass());
+        assertEquals("type", Geometry.class, a2.getValueClass());
+
+        assertTrue  ("roles", a1.roles().isEmpty());
+        assertEquals("roles", AttributeRole.IDENTIFIER_COMPONENT, TestUtilities.getSingleton(a0.roles()));
+        assertEquals("roles", AttributeRole.DEFAULT_GEOMETRY,     TestUtilities.getSingleton(a2.roles()));
+    }
+
+    /**
+     * Verifies that {@code build()} method returns the previously created instance when possible.
+     * See {@link AttributeTypeBuilder#build()} javadoc for a rational.
+     */
+    @Test
+    @DependsOnMethod("testAddAttribute")
+    public void testBuildCache() {
+        final FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("City");
+        final AttributeType<String> name = builder.addAttribute(String.class).setName("name").build();
+        final FeatureType city = builder.build();
+        assertSame("Should return the existing AttributeType.", name, city.getProperty("name"));
+        assertSame("Should return the existing FeatureType.", city, builder.build());
+
+        assertSame("Should return the existing AttributeType since we didn't changed anything.",
+                   name, builder.getProperty("name").build());
+
+        assertNotSame("Should return a new AttributeType since we changed something.",
+                      name, builder.getProperty("name").setDescription("Name of the city").build());
+
+        assertNotSame("Should return a new FeatureType since we changed an attribute.",
+                      city, builder.build());
     }
 }

Modified: sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -27,7 +27,7 @@ import org.junit.BeforeClass;
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @since   0.5
- * @version 0.7
+ * @version 0.8
  * @module
  */
 @Suite.SuiteClasses({
@@ -40,6 +40,7 @@ import org.junit.BeforeClass;
     org.apache.sis.feature.MultiValuedAttributeTest.class,
     org.apache.sis.feature.DenseFeatureTest.class,
     org.apache.sis.feature.SparseFeatureTest.class,
+    org.apache.sis.feature.AbstractFeatureTest.class,
     org.apache.sis.feature.DefaultAssociationRoleTest.class,
     org.apache.sis.feature.SingletonAssociationTest.class,
     org.apache.sis.feature.AbstractOperationTest.class,
@@ -49,6 +50,8 @@ import org.junit.BeforeClass;
     org.apache.sis.feature.FeatureFormatTest.class,
     org.apache.sis.feature.FeaturesTest.class,
     org.apache.sis.internal.feature.AttributeConventionTest.class,
+    org.apache.sis.feature.builder.CharacteristicTypeBuilderTest.class,
+    org.apache.sis.feature.builder.AttributeTypeBuilderTest.class,
     org.apache.sis.feature.builder.FeatureTypeBuilderTest.class
 })
 public final strictfp class FeatureTestSuite extends TestSuite {

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -23,8 +23,6 @@ import org.apache.sis.xml.IdentifiedObje
 import org.apache.sis.util.Static;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.metadata.InvalidMetadataException;
-import org.apache.sis.internal.jaxb.PrimitiveTypeProperties;
 import org.apache.sis.internal.jaxb.Context;
 import org.apache.sis.internal.util.Utilities;
 
@@ -34,7 +32,7 @@ import org.apache.sis.internal.util.Util
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.7
+ * @version 0.8
  * @module
  */
 public final class MetadataUtilities extends Static {
@@ -67,28 +65,6 @@ public final class MetadataUtilities ext
     }
 
     /**
-     * Makes sure that the given inclusion is non-nil, then returns its value.
-     * If the given inclusion is {@code null}, then the default value is {@code true}.
-     *
-     * @param  value The {@link org.opengis.metadata.extent.GeographicBoundingBox#getInclusion()} value.
-     * @return The given value as a primitive type.
-     * @throws InvalidMetadataException if the given value is nil.
-     */
-    @SuppressWarnings("NumberEquality")
-    public static boolean getInclusion(final Boolean value) throws InvalidMetadataException {
-        if (value == null) {
-            return true;
-        }
-        final boolean p = value;
-        // (value == Boolean.FALSE) is an optimization for a common case avoiding PrimitiveTypeProperties check.
-        // DO NOT REPLACE BY 'equals' OR 'booleanValue()' - the exact reference value matter.
-        if (p || (value == Boolean.FALSE) || !(PrimitiveTypeProperties.property(value) instanceof NilReason)) {
-            return p;
-        }
-        throw new InvalidMetadataException(Errors.format(Errors.Keys.MissingValueForProperty_1, "inclusion"));
-    }
-
-    /**
      * Ensures that the given property value is positive. If the user gave a negative value or (in some case) zero,
      * then this method logs a warning if we are in process of (un)marshalling a XML document or throw an exception
      * otherwise.

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -18,11 +18,11 @@ package org.apache.sis.metadata;
 
 
 /**
- * Thrown when a metadata is in a invalid state, usually because a mandatory property is missing.
+ * Thrown when a metadata is in a invalid state or has illegal property values.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.8
  * @module
  */
 public class InvalidMetadataException extends IllegalStateException {
@@ -34,9 +34,33 @@ public class InvalidMetadataException ex
     /**
      * Creates a new exception with the specified detail message.
      *
-     * @param message The detail message.
+     * @param  message  the details message, or {@code null} if none.
      */
     public InvalidMetadataException(final String message) {
         super(message);
     }
+
+    /**
+     * Creates a new exception with the specified cause.
+     * The details message is copied from the cause.
+     *
+     * @param  cause  the cause, or {@code null} if none.
+     *
+     * @since 0.8
+     */
+    public InvalidMetadataException(final Throwable cause) {
+        super(cause.getLocalizedMessage(), cause);
+    }
+
+    /**
+     * Creates a new exception with the specified detail message and cause.
+     *
+     * @param  message  the details message, or {@code null} if none.
+     * @param  cause    the cause, or {@code null} if none.
+     *
+     * @since 0.8
+     */
+    public InvalidMetadataException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
 }

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -94,7 +94,7 @@ import org.apache.sis.xml.Namespaces;
  * @author  Touraïvane (IRD)
  * @author  Cédric Briançon (Geomatys)
  * @since   0.3
- * @version 0.5
+ * @version 0.8
  * @module
  */
 @XmlType(name = "MD_Metadata_Type", propOrder = {
@@ -1265,7 +1265,7 @@ public class DefaultMetadata extends ISO
      */
     @Override
     @XmlElement(name = "distributionInfo")
-    public Collection<? extends Distribution> getDistributionInfo() {
+    public Collection<Distribution> getDistributionInfo() {
         return distributionInfo = nonNullCollection(distributionInfo, Distribution.class);
     }
 

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/constraint/DefaultConstraints.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/constraint/DefaultConstraints.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/constraint/DefaultConstraints.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/constraint/DefaultConstraints.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -95,7 +95,7 @@ public class DefaultConstraints extends
     private Collection<Citation> references;
 
     /**
-     * Citation for the limitation of constraint.
+     * Information concerning the parties to whom the resource can or cannot be released.
      */
     private Releasability releasability;
 
@@ -277,7 +277,7 @@ public class DefaultConstraints extends
     /**
      * Returns information concerning the parties to whom the resource can or cannot be released.
      *
-     * @return Information concerning the parties to whom the resource, or {@code null} if none.
+     * @return Information concerning the parties to whom the resource can or cannot be released, or {@code null} if none.
      *
      * @since 0.5
      */
@@ -290,7 +290,7 @@ public class DefaultConstraints extends
     /**
      * Sets the information concerning the parties to whom the resource.
      *
-     * @param newValue The new information concerning the parties to whom the resource.
+     * @param newValue The new information concerning the parties to whom the resource can or cannot be released.
      *
      * @since 0.5
      */

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java?rev=1755610&r1=1755609&r2=1755610&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java [UTF-8] Tue Aug  9 14:48:04 2016
@@ -30,8 +30,10 @@ import org.apache.sis.util.ComparisonMod
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.jaxb.PrimitiveTypeProperties;
+import org.apache.sis.metadata.InvalidMetadataException;
+import org.apache.sis.xml.NilReason;
 
 import static java.lang.Double.doubleToLongBits;
 
@@ -100,7 +102,7 @@ import java.util.Objects;
  * @author  Touraïvane (IRD)
  * @author  Cédric Briançon (Geomatys)
  * @since   0.3
- * @version 0.4
+ * @version 0.8
  * @module
  *
  * @see org.apache.sis.geometry.GeneralEnvelope
@@ -162,12 +164,12 @@ public class DefaultGeographicBoundingBo
      * Java2D world, which is rather (<var>x</var><sub>min</sub>, <var>y</var><sub>min</sub>,
      * <var>x</var><sub>max</sub>, <var>y</var><sub>max</sub>).</p>
      *
-     * @param westBoundLongitude The minimal λ value.
-     * @param eastBoundLongitude The maximal λ value.
-     * @param southBoundLatitude The minimal φ value.
-     * @param northBoundLatitude The maximal φ value.
+     * @param  westBoundLongitude  the minimal λ value.
+     * @param  eastBoundLongitude  the maximal λ value.
+     * @param  southBoundLatitude  the minimal φ value.
+     * @param  northBoundLatitude  the maximal φ value.
      *
-     * @throws IllegalArgumentException If (<var>south bound</var> &gt; <var>north bound</var>).
+     * @throws IllegalArgumentException if (<var>south bound</var> &gt; <var>north bound</var>).
      *         Note that {@linkplain Double#NaN NaN} values are allowed.
      *
      * @see #setBounds(double, double, double, double)
@@ -192,7 +194,7 @@ public class DefaultGeographicBoundingBo
      * This is a <cite>shallow</cite> copy constructor, since the other metadata contained in the
      * given object are not recursively copied.
      *
-     * @param object The metadata to copy values from, or {@code null} if none.
+     * @param  object  the metadata to copy values from, or {@code null} if none.
      *
      * @see #castOrCopy(GeographicBoundingBox)
      */
@@ -227,8 +229,8 @@ public class DefaultGeographicBoundingBo
      *       metadata contained in the given object are not recursively copied.</li>
      * </ul>
      *
-     * @param  object The object to get as a SIS implementation, or {@code null} if none.
-     * @return A SIS implementation containing the values of the given object (may be the
+     * @param  object  the object to get as a SIS implementation, or {@code null} if none.
+     * @return a SIS implementation containing the values of the given object (may be the
      *         given object itself), or {@code null} if the argument was null.
      */
     public static DefaultGeographicBoundingBox castOrCopy(final GeographicBoundingBox object) {
@@ -239,13 +241,35 @@ public class DefaultGeographicBoundingBo
     }
 
     /**
+     * Makes sure that the given inclusion is non-nil, then returns its value.
+     * If the given inclusion is {@code null}, then the default value is {@code true}.
+     *
+     * @param  value  the {@link org.opengis.metadata.extent.GeographicBoundingBox#getInclusion()} value.
+     * @return the given value as a primitive type.
+     * @throws InvalidMetadataException if the given value is nil.
+     */
+    @SuppressWarnings("NumberEquality")
+    static boolean getInclusion(final Boolean value) throws InvalidMetadataException {
+        if (value == null) {
+            return true;
+        }
+        final boolean p = value;
+        // (value == Boolean.FALSE) is an optimization for a common case avoiding PrimitiveTypeProperties check.
+        // DO NOT REPLACE BY 'equals' OR 'booleanValue()' - the exact reference value matter.
+        if (p || (value == Boolean.FALSE) || !(PrimitiveTypeProperties.property(value) instanceof NilReason)) {
+            return p;
+        }
+        throw new InvalidMetadataException(Errors.format(Errors.Keys.MissingValueForProperty_1, "inclusion"));
+    }
+
+    /**
      * Returns the western-most coordinate of the limit of the dataset extent.
      * The value is expressed in longitude in decimal degrees (positive east).
      *
      * <p>Note that the returned value is greater than the {@linkplain #getEastBoundLongitude()
      * east bound longitude} if this box is spanning over the anti-meridian.</p>
      *
-     * @return The western-most longitude between -180° and +180° inclusive,
+     * @return the western-most longitude between -180° and +180° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -260,8 +284,8 @@ public class DefaultGeographicBoundingBo
      * The value is expressed in longitude in decimal degrees (positive east).
      * Values outside the [-180 … 180]° range are {@linkplain Longitude#normalize(double) normalized}.
      *
-     * @param newValue The western-most longitude between -180° and +180° inclusive,
-     *        or {@linkplain Double#NaN NaN} to undefine.
+     * @param  newValue  the western-most longitude between -180° and +180° inclusive,
+     *         or {@linkplain Double#NaN NaN} to undefine.
      */
     public void setWestBoundLongitude(double newValue) {
         checkWritePermission();
@@ -278,7 +302,7 @@ public class DefaultGeographicBoundingBo
      * <p>Note that the returned value is smaller than the {@linkplain #getWestBoundLongitude()
      * west bound longitude} if this box is spanning over the anti-meridian.</p>
      *
-     * @return The eastern-most longitude between -180° and +180° inclusive,
+     * @return the eastern-most longitude between -180° and +180° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -293,8 +317,8 @@ public class DefaultGeographicBoundingBo
      * The value is expressed in longitude in decimal degrees (positive east).
      * Values outside the [-180 … 180]° range are {@linkplain Longitude#normalize(double) normalized}.
      *
-     * @param newValue The eastern-most longitude between -180° and +180° inclusive,
-     *        or {@linkplain Double#NaN NaN} to undefine.
+     * @param  newValue  the eastern-most longitude between -180° and +180° inclusive,
+     *         or {@linkplain Double#NaN NaN} to undefine.
      */
     public void setEastBoundLongitude(double newValue) {
         checkWritePermission();
@@ -308,7 +332,7 @@ public class DefaultGeographicBoundingBo
      * Returns the southern-most coordinate of the limit of the dataset extent.
      * The value is expressed in latitude in decimal degrees (positive north).
      *
-     * @return The southern-most latitude between -90° and +90° inclusive,
+     * @return the southern-most latitude between -90° and +90° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -325,8 +349,8 @@ public class DefaultGeographicBoundingBo
      * If the result is greater than the {@linkplain #getNorthBoundLatitude() north bound latitude},
      * then the north bound is set to {@link Double#NaN}.
      *
-     * @param newValue The southern-most latitude between -90° and +90° inclusive,
-     *        or {@linkplain Double#NaN NaN} to undefine.
+     * @param  newValue  the southern-most latitude between -90° and +90° inclusive,
+     *         or {@linkplain Double#NaN NaN} to undefine.
      */
     public void setSouthBoundLatitude(final double newValue) {
         checkWritePermission();
@@ -340,7 +364,7 @@ public class DefaultGeographicBoundingBo
      * Returns the northern-most, coordinate of the limit of the dataset extent.
      * The value is expressed in latitude in decimal degrees (positive north).
      *
-     * @return The northern-most latitude between -90° and +90° inclusive,
+     * @return the northern-most latitude between -90° and +90° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -357,8 +381,8 @@ public class DefaultGeographicBoundingBo
      * If the result is smaller than the {@linkplain #getSouthBoundLatitude() south bound latitude},
      * then the south bound is set to {@link Double#NaN}.
      *
-     * @param newValue The northern-most latitude between -90° and +90° inclusive,
-     *        or {@linkplain Double#NaN NaN} to undefine.
+     * @param  newValue  the northern-most latitude between -90° and +90° inclusive,
+     *         or {@linkplain Double#NaN NaN} to undefine.
      */
     public void setNorthBoundLatitude(final double newValue) {
         checkWritePermission();
@@ -375,7 +399,7 @@ public class DefaultGeographicBoundingBo
      *
      * <p>This method should be invoked <strong>before</strong> {@link #normalize()}.</p>
      *
-     * @throws IllegalArgumentException If (<var>south bound</var> &gt; <var>north bound</var>).
+     * @throws IllegalArgumentException if (<var>south bound</var> &gt; <var>north bound</var>).
      *         Note that {@linkplain Double#NaN NaN} values are allowed.
      */
     private static void verifyBounds(final double southBoundLatitude, final double northBoundLatitude)
@@ -440,12 +464,12 @@ public class DefaultGeographicBoundingBo
      * Java2D world, which is rather (<var>x</var><sub>min</sub>, <var>y</var><sub>min</sub>,
      * <var>x</var><sub>max</sub>, <var>y</var><sub>max</sub>).</p>
      *
-     * @param westBoundLongitude The minimal λ value.
-     * @param eastBoundLongitude The maximal λ value.
-     * @param southBoundLatitude The minimal φ value.
-     * @param northBoundLatitude The maximal φ value.
+     * @param  westBoundLongitude  the minimal λ value.
+     * @param  eastBoundLongitude  the maximal λ value.
+     * @param  southBoundLatitude  the minimal φ value.
+     * @param  northBoundLatitude  the maximal φ value.
      *
-     * @throws IllegalArgumentException If (<var>south bound</var> &gt; <var>north bound</var>).
+     * @throws IllegalArgumentException if (<var>south bound</var> &gt; <var>north bound</var>).
      *         Note that {@linkplain Double#NaN NaN} values are allowed.
      */
     public void setBounds(final double westBoundLongitude,
@@ -477,7 +501,7 @@ public class DefaultGeographicBoundingBo
      *
      * <p><b>Note:</b> this method is available only if the referencing module is on the classpath.</p>
      *
-     * @param  envelope The envelope to use for setting this geographic bounding box.
+     * @param  envelope  the envelope to use for setting this geographic bounding box.
      * @throws UnsupportedOperationException if the referencing module is not on the classpath.
      * @throws TransformException if the envelope can not be transformed to a geographic extent.
      *
@@ -495,7 +519,7 @@ public class DefaultGeographicBoundingBo
     /**
      * Sets the bounding box to the same values than the specified box.
      *
-     * @param box The geographic bounding box to use for setting the values of this box.
+     * @param  box  the geographic bounding box to use for setting the values of this box.
      */
     public void setBounds(final GeographicBoundingBox box) {
         ArgumentChecks.ensureNonNull("box", box);
@@ -609,7 +633,7 @@ public class DefaultGeographicBoundingBo
      * structure. Consequently NaN values in {@code GeographicBoundingBox} means <cite>"information is unknown"</cite>
      * more often than <cite>"not yet calculated"</cite>.</div>
      *
-     * @param box The geographic bounding box to add to this box.
+     * @param  box  the geographic bounding box to add to this box.
      *
      * @see org.apache.sis.geometry.GeneralEnvelope#add(Envelope)
      */
@@ -624,8 +648,8 @@ public class DefaultGeographicBoundingBo
          * valid metadata object.  If the metadata object is invalid, it is better to get a
          * an exception than having a code doing silently some inappropriate work.
          */
-        final boolean i1 = MetadataUtilities.getInclusion(this.getInclusion());
-        final boolean i2 = MetadataUtilities.getInclusion(box. getInclusion());
+        final boolean i1 = getInclusion(this.getInclusion());
+        final boolean i2 = getInclusion(box. getInclusion());
         final int status = denormalize(λmin, λmax); // Must be after call to getInclusion().
         switch (status) {
             case -1: λmin -= Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
@@ -664,17 +688,15 @@ public class DefaultGeographicBoundingBo
      * box or the specified box has NaN bounds, then the corresponding bounds of the
      * intersection result will bet set to NaN.</p>
      *
-     * @param box The geographic bounding box to intersect with this box.
-     * @throws IllegalArgumentException If the inclusion status is not the same for both boxes.
+     * @param  box  the geographic bounding box to intersect with this box.
+     * @throws IllegalArgumentException if the inclusion status is not the same for both boxes.
      *
      * @see Extents#intersection(GeographicBoundingBox, GeographicBoundingBox)
      * @see org.apache.sis.geometry.GeneralEnvelope#intersect(Envelope)
      */
     public void intersect(final GeographicBoundingBox box) throws IllegalArgumentException {
         checkWritePermission();
-        if (MetadataUtilities.getInclusion(    getInclusion()) !=
-            MetadataUtilities.getInclusion(box.getInclusion()))
-        {
+        if (getInclusion(getInclusion()) != getInclusion(box.getInclusion())) {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatiblePropertyValue_1, "inclusion"));
         }
         double λmin = box.getWestBoundLongitude();
@@ -723,7 +745,7 @@ public class DefaultGeographicBoundingBo
     /**
      * Compares this geographic bounding box with the specified object for equality.
      *
-     * @param object The object to compare for equality.
+     * @param  object  the object to compare for equality.
      * @return {@code true} if the given object is equal to this box.
      */
     @Override



Mime
View raw message