sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1595039 [1/3] - in /sis/branches/JDK6: ./ application/sis-javafx/ core/ core/sis-build-helper/src/main/ant/ core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/ core/sis-feature/ core/sis-feature/src/main/java/org/apache/sis...
Date Thu, 15 May 2014 20:48:32 GMT
Author: desruisseaux
Date: Thu May 15 20:48:31 2014
New Revision: 1595039

URL: http://svn.apache.org/r1595039
Log:
Merge from the JDK7 branch. The main work is in the org.apache.sis.feature package.
The work is not yet completed; this merge is a milestone.

Added:
    sis/branches/JDK6/application/sis-javafx/
      - copied from r1595037, sis/branches/JDK7/application/sis-javafx/
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java
      - copied, changed from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java
      - copied, changed from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/FieldType.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Property.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/Property.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyType.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyType.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/Validator.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java
      - copied, changed from r1595037, sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultOperationTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java
      - copied, changed from r1595037, sis/branches/JDK7/core/sis-feature/src/test/java/org/apache/sis/feature/PropertySingletonTest.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/formulas.html
      - copied unchanged from r1595037, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/formulas.html
Removed:
    sis/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java
Modified:
    sis/branches/JDK6/   (props changed)
    sis/branches/JDK6/core/pom.xml
    sis/branches/JDK6/core/sis-build-helper/src/main/ant/prepare-release.xml
    sis/branches/JDK6/core/sis-feature/pom.xml
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java   (contents, props changed)
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/package-info.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAttributeTypeTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java
    sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
    sis/branches/JDK6/core/sis-metadata/pom.xml
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/constraint/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/lineage/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/DefaultExtentTest.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/lineage/DefaultProcessStepTest.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/quality/AbstractPositionalAccuracyTest.java
    sis/branches/JDK6/core/sis-referencing/pom.xml
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java
    sis/branches/JDK6/core/sis-utility/pom.xml
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/measure/UnitsMap.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/collection/DerivedIterator.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/collection/package-info.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/CheckedArrayListTest.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/measure/UnitsTest.java
    sis/branches/JDK6/pom.xml
    sis/branches/JDK6/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/AttributeNames.java
    sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java

Propchange: sis/branches/JDK6/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK8:r1591679-1595029
  Merged /sis/branches/JDK7:r1591681-1595037

Modified: sis/branches/JDK6/core/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/pom.xml?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/pom.xml (original)
+++ sis/branches/JDK6/core/pom.xml Thu May 15 20:48:31 2014
@@ -148,6 +148,23 @@
 
 
   <!-- ===========================================================
+           Dependencies
+       =========================================================== -->
+  <dependencies>
+    <dependency>
+      <groupId>org.opengis</groupId>
+      <artifactId>geoapi-pending</artifactId>
+    </dependency>
+
+    <!-- Test dependencies -->
+    <dependency>
+      <groupId>org.opengis</groupId>
+      <artifactId>geoapi-conformance</artifactId>
+    </dependency>
+  </dependencies>
+
+
+  <!-- ===========================================================
            Sub-modules included in the build in dependency order
        =========================================================== -->
   <modules>

Modified: sis/branches/JDK6/core/sis-build-helper/src/main/ant/prepare-release.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-build-helper/src/main/ant/prepare-release.xml?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-build-helper/src/main/ant/prepare-release.xml (original)
+++ sis/branches/JDK6/core/sis-build-helper/src/main/ant/prepare-release.xml Thu May 15 20:48:31 2014
@@ -44,7 +44,6 @@
     <!-- Replace URL to trunk by URL to the branch on Subversion. -->
     <replace dir="${user.dir}" failOnNoReplacements="true">
       <include name="**/pom.xml"/>
-      <include name="core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java"/>
       <replacefilter token="svn.apache.org/repos/asf/sis/trunk"
                      value="svn.apache.org/repos/asf/sis/branches/${sis.version}"/>
       <replacefilter token="svn.apache.org/viewvc/sis/trunk"
@@ -60,7 +59,6 @@
     <!-- Replace URL to branch by URL to the branch on Subversion. -->
     <replace dir="${user.dir}" failOnNoReplacements="true">
       <include name="**/pom.xml"/>
-      <include name="core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java"/>
       <replacefilter token="svn.apache.org/repos/asf/sis/branches/${sis.version}"
                      value="svn.apache.org/repos/asf/sis/tags/${sis.version}"/>
       <replacefilter token="svn.apache.org/viewvc/sis/branches/${sis.version}"

Modified: sis/branches/JDK6/core/sis-feature/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/pom.xml?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/pom.xml (original)
+++ sis/branches/JDK6/core/sis-feature/pom.xml Thu May 15 20:48:31 2014
@@ -86,12 +86,18 @@ Representations of geographic features.
        =========================================================== -->
   <dependencies>
     <dependency>
-      <groupId>org.opengis</groupId>
-      <artifactId>geoapi-pending</artifactId>
+      <groupId>org.apache.sis.core</groupId>
+      <artifactId>sis-utility</artifactId>
+      <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.apache.sis.core</groupId>
-      <artifactId>sis-utility</artifactId>
+      <artifactId>sis-metadata</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sis.core</groupId>
+      <artifactId>sis-referencing</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
@@ -101,10 +107,6 @@ Representations of geographic features.
 
     <!-- Test dependencies -->
     <dependency>
-      <groupId>org.opengis</groupId>
-      <artifactId>geoapi-conformance</artifactId>
-    </dependency>
-    <dependency>
       <groupId>org.apache.sis.core</groupId>
       <artifactId>sis-utility</artifactId>
       <version>${project.version}</version>

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] Thu May 15 20:48:31 2014
@@ -119,12 +119,12 @@ public class AbstractIdentifiedType impl
     /**
      * Constructs a type from the given properties. Keys are strings from the table below.
      * The map given in argument shall contain an entry at least for the {@value #NAME_KEY}.
-     * Other properties listed in the table below are optional.
+     * Other entries listed in the table below are optional.
      *
      * <table class="sis">
-     *   <caption>Recognized properties</caption>
+     *   <caption>Recognized map entries</caption>
      *   <tr>
-     *     <th>Property name</th>
+     *     <th>Map key</th>
      *     <th>Value type</th>
      *     <th>Returned by</th>
      *   </tr>
@@ -165,35 +165,26 @@ public class AbstractIdentifiedType impl
      * is used only on a <cite>best effort</cite> basis. The locale is discarded after successful construction
      * since localizations are applied by the {@link InternationalString#toString(Locale)} method.</p>
      *
-     * @param  properties The name and other properties to be given to this identified type.
+     * @param  identification The name and other information to be given to this identified type.
      * @throws IllegalArgumentException if a property has an invalid value.
      */
-    protected AbstractIdentifiedType(final Map<String,?> properties) throws IllegalArgumentException {
-        ensureNonNull("properties", properties);
-        Object value = properties.get(NAME_KEY);
+    protected AbstractIdentifiedType(final Map<String,?> identification) throws IllegalArgumentException {
+        ensureNonNull("identification", identification);
+        Object value = identification.get(NAME_KEY);
         if (value == null) {
-            throw new IllegalArgumentException(Errors.getResources(properties)
+            throw new IllegalArgumentException(Errors.getResources(identification)
                     .getString(Errors.Keys.MissingValueForProperty_1, NAME_KEY));
         } else if (value instanceof String) {
             name = DefaultFactories.NAMES.createLocalName(null, (String) value);
         } else if (value instanceof GenericName) {
             name = (GenericName) value;
         } else {
-            throw illegalPropertyType(properties, NAME_KEY, value);
+            throw new IllegalArgumentException(Errors.getResources(identification).getString(
+                    Errors.Keys.IllegalPropertyClass_2, NAME_KEY, value.getClass()));
         }
-        definition  = Types.toInternationalString(properties, DEFINITION_KEY );
-        designation = Types.toInternationalString(properties, DESIGNATION_KEY);
-        description = Types.toInternationalString(properties, DESCRIPTION_KEY);
-    }
-
-    /**
-     * Returns the exception to be thrown when a property is of illegal type.
-     */
-    private static IllegalArgumentException illegalPropertyType(
-            final Map<String,?> properties, final String key, final Object value)
-    {
-        return new IllegalArgumentException(Errors.getResources(properties)
-                .getString(Errors.Keys.IllegalPropertyClass_2, key, value.getClass()));
+        definition  = Types.toInternationalString(identification, DEFINITION_KEY );
+        designation = Types.toInternationalString(identification, DESIGNATION_KEY);
+        description = Types.toInternationalString(identification, DESCRIPTION_KEY);
     }
 
     /**
@@ -222,7 +213,7 @@ public class AbstractIdentifiedType impl
 
     /**
      * Returns a natural language designator for the element.
-     * This can be used as an alternative to the {@linkplain #getName()} in user interfaces.
+     * This can be used as an alternative to the {@linkplain #getName() name} in user interfaces.
      *
      * @return Natural language designator for the element.
      */
@@ -270,7 +261,7 @@ public class AbstractIdentifiedType impl
      */
     @Override
     public boolean equals(final Object obj) {
-        if (obj != null && getClass() != obj.getClass()) {
+        if (obj != null && getClass() == obj.getClass()) {
             final AbstractIdentifiedType that = (AbstractIdentifiedType) obj;
             return Objects.equals(name,        that.name) &&
                    Objects.equals(definition,  that.definition) &&

Copied: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java (from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java?p2=sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java&p1=sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java&r1=1595037&r2=1595039&rev=1595039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociation.java [UTF-8] Thu May 15 20:48:31 2014
@@ -24,7 +24,7 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.resources.Errors;
 
 // Related to JDK7
-import java.util.Objects;
+import org.apache.sis.internal.jdk7.Objects;
 
 
 /**

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java [UTF-8] Thu May 15 20:48:31 2014
@@ -17,7 +17,11 @@
 package org.apache.sis.feature;
 
 import java.io.Serializable;
+import org.opengis.util.GenericName;
+import org.opengis.metadata.quality.DataQuality;
+import org.opengis.metadata.maintenance.ScopeCode;
 import org.apache.sis.util.Debug;
+import org.apache.sis.util.Classes;
 import org.apache.sis.util.ArgumentChecks;
 
 // Related to JDK7
@@ -25,9 +29,18 @@ import org.apache.sis.internal.jdk7.Obje
 
 
 /**
- * Holds the value of an attribute in a feature. The value can be an arbitrary Java object.
- * Constraints like the base Java class and domain of values are specified by the
- * {@linkplain DefaultAttributeType attribute type} associated to each attribute instance.
+ * An instance of an {@linkplain DefaultAttributeType attribute type} containing the value of an attribute in a feature.
+ * {@code Attribute} holds two main information:
+ *
+ * <ul>
+ *   <li>A reference to an {@linkplain DefaultAttributeType attribute type}
+ *       which define the base Java type and domain of valid values.</li>
+ *   <li>A value.</li>
+ * </ul>
+ *
+ * {@section Usage in multi-thread environment}
+ * {@code DefaultAttribute} are <strong>not</strong> thread-safe.
+ * Synchronization, if needed, shall be done externally by the caller.
  *
  * @param <T> The type of attribute values.
  *
@@ -36,8 +49,10 @@ import org.apache.sis.internal.jdk7.Obje
  * @since   0.5
  * @version 0.5
  * @module
+ *
+ * @see DefaultAttributeType
  */
-final class DefaultAttribute<T> implements Serializable {
+public class DefaultAttribute<T> extends Property implements Cloneable, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -54,20 +69,47 @@ final class DefaultAttribute<T> implemen
     private T value;
 
     /**
-     * Creates a new attribute of the given type.
-     * The value is initialized to the {@linkplain DefaultAttributeType#getDefaultValue() default value}.
+     * Creates a new attribute of the given type initialized to the
+     * {@linkplain DefaultAttributeType#getDefaultValue() default value}.
      *
      * @param type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
      */
     public DefaultAttribute(final DefaultAttributeType<T> type) {
         ArgumentChecks.ensureNonNull("type", type);
-        this.type = type;
-        value = type.getDefaultValue();
+        this.type  = type;
+        this.value = type.getDefaultValue();
+    }
+
+    /**
+     * Creates a new attribute of the given type initialized to the given value.
+     * Note that a {@code null} value may not the same as the default value.
+     *
+     * @param type  Information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     * @param value The initial value (may be null {@code null}).
+     */
+    public DefaultAttribute(final DefaultAttributeType<T> type, final Object value) {
+        ArgumentChecks.ensureNonNull("type", type);
+        this.type  = type;
+        this.value = type.getValueClass().cast(value);
+    }
+
+    /**
+     * Returns the name of this attribute as defined by its {@linkplain #getType() type}.
+     * This convenience method delegates to {@link DefaultAttributeType#getName()}.
+     *
+     * @return The attribute name specified by its type.
+     */
+    @Override
+    public GenericName getName() {
+        return type.getName();
     }
 
     /**
      * Returns information about the attribute (base Java class, domain of values, <i>etc.</i>).
      *
+     * <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed
+     * to {@code org.opengis.feature.AttributeType}. This change is pending GeoAPI revision.</div>
+     *
      * @return Information about the attribute.
      */
     public DefaultAttributeType<T> getType() {
@@ -78,6 +120,8 @@ final class DefaultAttribute<T> implemen
      * Returns the attribute value.
      *
      * @return The attribute value (may be {@code null}).
+     *
+     * @see DefaultFeature#getPropertyValue(String)
      */
     public T getValue() {
         return value;
@@ -86,17 +130,80 @@ final class DefaultAttribute<T> implemen
     /**
      * Sets the attribute value.
      *
-     * <div class="warning">Current implementation does not yet performed any validation.
-     * However future Apache SIS version is likely to check argument validity here.</div>
+     * {@section Validation}
+     * The amount of validation performed by this method is implementation dependent.
+     * Usually, only the most basic constraints are verified. This is so for performance reasons
+     * and also because some rules may be temporarily broken while constructing a feature.
+     * A more exhaustive verification can be performed by invoking the {@link #validate()} method.
      *
-     * @param  value The new value.
-     * @throws IllegalArgumentException If the given value is outside the attribute domain.
+     * @param value The new value.
+     *
+     * @see DefaultFeature#setPropertyValue(String, Object)
      */
-    public void setValue(final T value) throws IllegalArgumentException {
+    public void setValue(final T value) {
         this.value = value;
     }
 
     /**
+     * Verifies if the current attribute value mets the constraints defined by the attribute type.
+     * This method returns {@linkplain org.apache.sis.metadata.iso.quality.DefaultDataQuality#getReports()
+     * reports} for all constraint violations found, if any.
+     *
+     * <div class="note"><b>Example:</b> given an attribute named “population” with [1 … 1] cardinality,
+     * if no value has been assigned to that attribute, then this {@code validate()} method will return
+     * the following data quality report:
+     *
+     * {@preformat text
+     *   Data quality
+     *     ├─Scope
+     *     │   └─Level………………………………………………… Attribute
+     *     └─Report
+     *         ├─Measure identification
+     *         │   └─Code………………………………………… population
+     *         ├─Evaluation method type…… Direct internal
+     *         └─Result
+     *             ├─Explanation……………………… Missing value for “population” property.
+     *             └─Pass………………………………………… false
+     * }
+     * </div>
+     *
+     * This attribute is valid if this method does not report any
+     * {@linkplain org.apache.sis.metadata.iso.quality.DefaultConformanceResult conformance result} having a
+     * {@linkplain org.apache.sis.metadata.iso.quality.DefaultConformanceResult#pass() pass} value of {@code false}.
+     *
+     * @return Reports on all constraint violations found.
+     *
+     * @see DefaultFeature#validate()
+     */
+    /*
+     * API NOTE: this method is final for now because if we allowed users to override it, users would
+     * expect their method to be invoked by DefaultFeature.validate(). But this is not yet the case.
+     */
+    public final DataQuality validate() {
+        final Validator v = new Validator(ScopeCode.ATTRIBUTE);
+        v.validate(type, value);
+        return v.quality;
+    }
+
+    /**
+     * Returns a shallow copy of this attribute.
+     * The attribute {@linkplain #getValue() value} is <strong>not</strong> cloned.
+     *
+     * @return A clone of this attribute.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public DefaultAttribute<T> clone() {
+        final DefaultAttribute<T> clone;
+        try {
+            clone = (DefaultAttribute<T>) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(e); // Should never happen since we are cloneable.
+        }
+        return clone;
+    }
+
+    /**
      * Returns a hash code value for this attribute type.
      *
      * @return A hash code value.
@@ -116,7 +223,7 @@ final class DefaultAttribute<T> implemen
         if (obj == this) {
             return true;
         }
-        if (obj.getClass() == getClass()) {
+        if (obj != null && obj.getClass() == getClass()) {
             final DefaultAttribute<?> that = (DefaultAttribute<?>) obj;
             return type.equals(that.type) &&
                    Objects.equals(value, that.value);
@@ -133,6 +240,10 @@ final class DefaultAttribute<T> implemen
     @Debug
     @Override
     public String toString() {
-        return type.toString("Attribute").append(" = ").append(value).toString();
+        final StringBuilder buffer = type.toString("Attribute", Classes.getShortName(type.getValueClass()));
+        if (value != null) {
+            buffer.append(" = ").append(value);
+        }
+        return buffer.toString();
     }
 }

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] Thu May 15 20:48:31 2014
@@ -18,11 +18,10 @@ package org.apache.sis.feature;
 
 import java.util.Map;
 import org.opengis.util.GenericName;
+import org.opengis.util.InternationalString;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.Classes;
-import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Numerics;
-import org.apache.sis.measure.NumberRange;
 
 import static org.apache.sis.util.ArgumentChecks.*;
 
@@ -42,8 +41,29 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * <div class="warning"><b>Warning:</b>
  * This class is expected to implement a GeoAPI {@code AttributeType} interface in a future version.
- * When such interface will be available, most references to {@code DefaultAttributeType} in the API
- * will be replaced by references to the {@code AttributeType} interface.</div>
+ * When such interface will be available, most references to {@code DefaultAttributeType} in current
+ * API will be replaced by references to the {@code AttributeType} interface.</div>
+ *
+ * {@section Value type}
+ * Attributes can be used for both spatial and non-spatial properties.
+ * Some examples are:
+ *
+ * <table class="sis">
+ *   <caption>Attribute value type examples</caption>
+ *   <tr><th>Attribute name</th>      <th>Value type</th></tr>
+ *   <tr><td>Building shape</td>      <td>{@link org.opengis.geometry.Geometry}</td></tr>
+ *   <tr><td>Building owner</td>      <td>{@link org.opengis.metadata.citation.ResponsibleParty}</td></tr>
+ *   <tr><td>Horizontal accuracy</td> <td>{@link org.opengis.metadata.quality.PositionalAccuracy}</td></tr>
+ * </table>
+ *
+ * {@section Immutability and thread safety}
+ * Instances of this class are immutable if all properties ({@link GenericName} and {@link InternationalString}
+ * instances) and all arguments (e.g. {@code defaultValue}) given to the constructor are also immutable.
+ * Such immutable instances can be shared by many objects and passed between threads without synchronization.
+ *
+ * <p>In particular, the {@link #getDefaultValue()} method does <strong>not</strong> clone the returned value.
+ * This means that the same {@code defaultValue} instance may be shared by many {@link DefaultAttribute} instances.
+ * Consequently the default value should be immutable for avoiding unexpected behavior.</p>
  *
  * @param <T> The type of attribute values.
  *
@@ -52,8 +72,10 @@ import org.apache.sis.internal.jdk7.Obje
  * @since   0.5
  * @version 0.5
  * @module
+ *
+ * @see DefaultAttribute
  */
-public class DefaultAttributeType<T> extends AbstractIdentifiedType {
+public class DefaultAttributeType<T> extends FieldType {
     /**
      * For cross-version compatibility.
      */
@@ -74,68 +96,54 @@ public class DefaultAttributeType<T> ext
     private final T defaultValue;
 
     /**
-     * The minimum/maximum number of occurrences of the property within its containing entity.
-     */
-    private final NumberRange<Integer> cardinality;
-
-    /**
-     * Constructs an attribute type from the given properties. The properties map is given unchanged to
+     * Constructs an attribute type from the given properties. The identification map is given unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
-     * The following table is a reminder of main (not all) properties:
+     * The following table is a reminder of main (not all) recognized map entries:
      *
      * <table class="sis">
-     *   <caption>Recognized properties (non exhaustive list)</caption>
+     *   <caption>Recognized map entries (non exhaustive list)</caption>
      *   <tr>
-     *     <th>Property name</th>
+     *     <th>Map key</th>
      *     <th>Value type</th>
      *     <th>Returned by</th>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#NAME_KEY}</td>
-     *     <td>{@link org.opengis.util.GenericName} or {@link String}</td>
+     *     <td>{@link GenericName} or {@link String}</td>
      *     <td>{@link #getName()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#DEFINITION_KEY}</td>
-     *     <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
      *     <td>{@link #getDefinition()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#DESIGNATION_KEY}</td>
-     *     <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
      *     <td>{@link #getDesignation()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#DESCRIPTION_KEY}</td>
-     *     <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
      *     <td>{@link #getDescription()}</td>
      *   </tr>
      * </table>
      *
-     * @param properties    The name and other properties to be given to this attribute type.
-     * @param valueClass    The type of attribute values.
-     * @param defaultValue  The default value for the attribute, or {@code null} if none.
-     * @param cardinality   The minimum and maximum number of occurrences of the property within its containing entity,
-     *                      or {@code null} if there is no restriction.
+     * @param identification The name and other information to be given to this attribute type.
+     * @param valueClass     The type of attribute values.
+     * @param minimumOccurs  The minimum number of occurrences of the attribute within its containing entity.
+     * @param maximumOccurs  The maximum number of occurrences of the attribute within its containing entity,
+     *                       or {@link Integer#MAX_VALUE} if there is no restriction.
+     * @param defaultValue   The default value for the attribute, or {@code null} if none.
      */
-    public DefaultAttributeType(final Map<String,?> properties, final Class<T> valueClass, final T defaultValue,
-            NumberRange<Integer> cardinality)
+    public DefaultAttributeType(final Map<String,?> identification, final Class<T> valueClass,
+            final int minimumOccurs, final int maximumOccurs, final T defaultValue)
     {
-        super(properties);
+        super(identification, minimumOccurs, maximumOccurs);
         ensureNonNull("valueClass",   valueClass);
         ensureCanCast("defaultValue", valueClass, defaultValue);
-        if (cardinality == null) {
-            cardinality = NumberRange.createLeftBounded(0, true);
-        } else {
-            final Integer minValue = cardinality.getMinValue();
-            if (minValue == null || minValue < 0) {
-                throw new IllegalArgumentException(Errors.format(
-                        Errors.Keys.IllegalArgumentValue_2, "cardinality", cardinality));
-            }
-        }
         this.valueClass   = valueClass;
         this.defaultValue = Numerics.cached(defaultValue);
-        this.cardinality  = cardinality;
     }
 
     /**
@@ -147,16 +155,6 @@ public class DefaultAttributeType<T> ext
         return valueClass;
     }
 
-    /**
-     * Returns the default value for the attribute.
-     * This value is used when an attribute is created and no value for it is specified.
-     *
-     * @return The default value for the attribute, or {@code null} if none.
-     */
-    public T getDefaultValue() {
-        return defaultValue;
-    }
-
     /*
      * ISO 19109 properties omitted for now:
      *
@@ -170,14 +168,37 @@ public class DefaultAttributeType<T> ext
      */
 
     /**
-     * Returns the minimum and maximum number of occurrences of the property within its containing entity.
-     * The bounds are always integer values greater than or equal to zero. The upper bounds may be {@code null}
-     * if there is no maximum number of occurrences.
+     * 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.
+     */
+    @Override
+    public final int getMinimumOccurs() {
+        return super.getMinimumOccurs();
+    }
+
+    /**
+     * Returns the maximum number of occurrences of the property within its containing entity.
+     * 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 minimum and 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 NumberRange<Integer> getCardinality() {
-        return cardinality;
+    @Override
+    public final int getMaximumOccurs() {
+        return super.getMaximumOccurs();
+    }
+
+    /**
+     * Returns the default value for the attribute.
+     * This value is used when an attribute is created and no value for it is specified.
+     *
+     * @return The default value for the attribute, or {@code null} if none.
+     */
+    public T getDefaultValue() {
+        return defaultValue;
     }
 
     /**
@@ -187,7 +208,7 @@ public class DefaultAttributeType<T> ext
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + valueClass.hashCode() + Objects.hashCode(defaultValue) + 31*cardinality.hashCode();
+        return super.hashCode() + valueClass.hashCode() + Objects.hashCode(defaultValue);
     }
 
     /**
@@ -203,8 +224,7 @@ public class DefaultAttributeType<T> ext
         if (super.equals(obj)) {
             final DefaultAttributeType<?> that = (DefaultAttributeType<?>) obj;
             return valueClass == that.valueClass &&
-                   Objects.equals(defaultValue, that.defaultValue) &&
-                   cardinality.equals(that.cardinality);
+                   Objects.equals(defaultValue, that.defaultValue);
         }
         return false;
     }
@@ -218,22 +238,6 @@ public class DefaultAttributeType<T> ext
     @Debug
     @Override
     public String toString() {
-        return toString("AttributeType").toString();
-    }
-
-    /**
-     * Implementation of {@link #toString()} to be shared by {@link DefaultAttribute#toString()}.
-     */
-    final StringBuilder toString(final String typeName) {
-        final StringBuilder buffer = new StringBuilder(40).append(typeName).append('[');
-        final GenericName name = super.getName();
-        if (name != null) {
-            buffer.append('“');
-        }
-        buffer.append(name);
-        if (name != null) {
-            buffer.append("” : ");
-        }
-        return buffer.append(Classes.getShortName(valueClass)).append(']');
+        return toString("AttributeType", Classes.getShortName(valueClass)).toString();
     }
 }

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java [UTF-8] Thu May 15 20:48:31 2014
@@ -20,6 +20,8 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.ConcurrentModificationException;
 import java.io.Serializable;
+import org.opengis.metadata.quality.DataQuality;
+import org.opengis.metadata.maintenance.ScopeCode;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
@@ -27,11 +29,28 @@ import org.apache.sis.util.collection.Co
 
 // Related to JDK7
 import org.apache.sis.internal.jdk7.JDK7;
-import org.apache.sis.internal.jdk7.Objects;
 
 
 /**
- * An instance of {@linkplain DefaultFeatureType feature type} containing values for a real-world phenomena.
+ * An instance of a {@linkplain DefaultFeatureType feature type} containing values for a real-world phenomena.
+ * Each feature instance can provide values for the following properties:
+ *
+ * <ul>
+ *   <li>{@linkplain DefaultAttribute   Attributes}</li>
+ *   <li>{@linkplain DefaultAssociation Associations to other features}</li>
+ *   <li>{@linkplain DefaultOperation   Operations}</li>
+ * </ul>
+ *
+ * {@section Simple features}
+ * A feature is said “simple” if it complies to the following conditions:
+ * <ul>
+ *   <li>the feature allows only attributes and operations (no associations),</li>
+ *   <li>the cardinality of all attributes is constrained to [1 … 1].</li>
+ * </ul>
+ *
+ * {@section Usage in multi-thread environment}
+ * {@code DefaultFeature} are <strong>not</strong> thread-safe.
+ * Synchronization, if needed, shall be done externally by the caller.
  *
  * @author  Travis L. Pinney
  * @author  Johann Sorel (Geomatys)
@@ -39,6 +58,8 @@ import org.apache.sis.internal.jdk7.Obje
  * @since   0.5
  * @version 0.5
  * @module
+ *
+ * @see DefaultFeatureType
  */
 public class DefaultFeature implements Serializable {
     /**
@@ -53,24 +74,43 @@ public class DefaultFeature implements S
 
     /**
      * The properties (attributes, operations, feature associations) of this feature.
+     *
+     * Conceptually, values in this map are {@link Property} instances. However at first we will store
+     * only the property <em>values</em>, and build the full {@code Property} objects only if they are
+     * requested. The intend is to reduce the amount of allocated objects as much as possible, because
+     * typical SIS applications may create a very large amount of features.
+     *
+     * @see #propertiesAreInstantiated
      */
-    private final Map<String, DefaultAttribute<?>> properties;
+    private final Map<String, Object> properties;
 
     /**
-     * Creates a new features.
+     * {@code true} if the values in the {@link #properties} map are {@link Property} instances,
+     * or {@code false} if the map contains only the "raw" property values.
+     *
+     * <p>This field is initially {@code false}, and will be set to {@code true} only if at least one
+     * {@code Property} instance has been requested. In such case, all property values will have been
+     * wrapped into their appropriate {@code Property} instance.</p>
+     */
+    private boolean propertiesAreInstantiated;
+
+    /**
+     * Creates a new feature of the given type.
      *
      * @param type Information about the feature (name, characteristics, <i>etc.</i>).
      */
     public DefaultFeature(final DefaultFeatureType type) {
         ArgumentChecks.ensureNonNull("type", type);
         this.type = type;
-        properties = new HashMap<String, DefaultAttribute<?>>(
-                Math.min(16, Containers.hashMapCapacity(type.getCharacteristics().size())));
+        properties = new HashMap<String,Object>(Math.min(16, Containers.hashMapCapacity(type.getInstanceSize())));
     }
 
     /**
      * Returns information about the feature (name, characteristics, <i>etc.</i>).
      *
+     * <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed
+     * to {@code org.opengis.feature.FeatureType}. This change is pending GeoAPI revision.</div>
+     *
      * @return Information about the feature.
      */
     public DefaultFeatureType getType() {
@@ -78,54 +118,246 @@ public class DefaultFeature implements S
     }
 
     /**
-     * Returns the value of the attribute of the given name.
+     * Returns the type for the property of the given name.
+     *
+     * @param  name The property name.
+     * @return The type for the property of the given name (never {@code null}).
+     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     */
+    private PropertyType getPropertyType(final String name) throws IllegalArgumentException {
+        final PropertyType pt = type.getProperty(name);
+        if (pt != null) {
+            return pt;
+        }
+        throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyNotFound_2, type.getName(), name));
+    }
+
+    /**
+     * Returns the property (attribute, operation or association) of the given name.
+     *
+     * <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed
+     * to {@code org.opengis.feature.Property}. This change is pending GeoAPI revision.</div>
+     *
+     * @param  name The property name.
+     * @return The property of the given name.
+     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     */
+    public Object getProperty(final String name) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("name", name);
+        /*
+         * Wraps values in Property objects for all entries in the properties map,
+         * if not already done. This operation is execute at most once per feature.
+         */
+        if (!propertiesAreInstantiated) {
+            propertiesAreInstantiated = true;
+            if (!properties.isEmpty()) { // The map is typically empty when this method is first invoked.
+                for (final Map.Entry<String, Object> entry : properties.entrySet()) {
+                    final String key      = entry.getKey();
+                    final Object value    = entry.getValue();
+                    final PropertyType pt = getPropertyType(key);
+                    final Property property;
+                    if (pt instanceof DefaultAttributeType<?>) {
+                        property = new DefaultAttribute((DefaultAttributeType<?>) pt, value);
+                    } else {
+                        throw new UnsupportedOperationException(); // TODO
+                    }
+                    if (entry.setValue(property) != value) {
+                        throw new ConcurrentModificationException(key);
+                    }
+                }
+            }
+        }
+        return getPropertyInstance(name);
+    }
+
+    /**
+     * Implementation of {@link #getProperty(String)} invoked when we know that the {@link #properties}
+     * map contains {@code Property} instances (as opposed to their value).
+     */
+    private Property getPropertyInstance(final String name) throws IllegalArgumentException {
+        Property property = (Property) properties.get(name);
+        if (property == null) {
+            final PropertyType pt = getPropertyType(name);
+            if (pt instanceof DefaultAttributeType<?>) {
+                property = new DefaultAttribute((DefaultAttributeType<?>) pt);
+            } else {
+                throw new UnsupportedOperationException(); // TODO
+            }
+            replace(name, null, property);
+        }
+        return property;
+    }
+
+    /**
+     * Returns the value for the property of the given name.
+     * This convenience method is equivalent to the following code,
+     * except that this method is potentially more efficient:
+     *
+     * {@preformat java
+     *     return getProperty(name).getValue();
+     * }
+     *
+     * @param  name The property name.
+     * @return The value for the given property, or {@code null} if none.
+     * @throws IllegalArgumentException If the given argument is not a property name of this feature.
+     *
+     * @see DefaultAttribute#getValue()
+     */
+    public Object getPropertyValue(final String name) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("name", name);
+        final Object element = properties.get(name);
+        if (element != null) {
+            if (!propertiesAreInstantiated) {
+                return element;
+            } else {
+                return ((DefaultAttribute<?>) element).getValue();
+            }
+        } else if (properties.containsKey(name)) {
+            return null; // Null has been explicitely set.
+        } else {
+            final PropertyType pt = getPropertyType(name);
+            if (pt instanceof DefaultAttributeType<?>) {
+                return ((DefaultAttributeType<?>) pt).getDefaultValue();
+            } else {
+                throw new UnsupportedOperationException(); // TODO
+            }
+        }
+    }
+
+    /**
+     * Sets the value for the property of the given name.
+     *
+     * {@section Validation}
+     * The amount of validation performed by this method is implementation dependent.
+     * Usually, only the most basic constraints are verified. This is so for performance reasons
+     * and also because some rules may be temporarily broken while constructing a feature.
+     * A more exhaustive verification can be performed by invoking the {@link #validate()} method.
      *
-     * @param  name The attribute name.
-     * @return The value for the given attribute, or {@code null} if none.
+     * @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 an other reason.
+     *
+     * @see DefaultAttribute#setValue(Object)
      */
-    public Object getAttributeValue(final String name) {
-        final DefaultAttribute<?> attribute = properties.get(name);
-        if (attribute == null) {
-            final DefaultAttributeType<?> at = type.getProperty(name);
-            if (at == null) {
-                throw new IllegalArgumentException(propertyNotFound(name));
+    public void setPropertyValue(final String name, final Object value) throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("name", name);
+        if (!propertiesAreInstantiated) {
+            final Object previous = properties.put(name, value);
+            /*
+             * Slight optimisation: if we replaced a previous value of the same class, then we can skip the
+             * checks for name and type validity since those checks has been done previously. But if we add
+             * a new value or a value of a different type, then we need to check the name and type validity.
+             */
+            if (previous == null || (value != null && previous.getClass() != value.getClass())) {
+                final PropertyType pt = type.getProperty(name);
+                if (!isValidAttributeValue(pt, value)) {
+                    replace(name, value, previous); // Restore the previous value.
+                    if (pt == null) {
+                        throw new IllegalArgumentException(Errors.format(
+                                Errors.Keys.PropertyNotFound_2, type.getName(), name));
+                    } else {
+                        throw new ClassCastException(Errors.format(
+                                Errors.Keys.IllegalPropertyClass_2, name, value.getClass()));
+                    }
+                }
+            }
+        } else {
+            final Property property = getPropertyInstance(name);
+            if (property instanceof DefaultAttribute<?>) {
+                setAttributeValue((DefaultAttribute<?>) property, value);
+            } else {
+                throw new UnsupportedOperationException();
             }
-            return at.getDefaultValue();
         }
-        return attribute.getValue();
     }
 
     /**
-     * Sets the value of the attribute of the given name.
+     * Sets a value in the {@link #properties} map.
      *
-     * @param name  The attribute name.
-     * @param value The new value for the given attribute (may be {@code null}).
+     * @param name     The name of the property to set.
+     * @param oldValue The old value, used for verification purpose.
+     * @param newValue The new value.
+     */
+    private void replace(final String name, final Object oldValue, final Object newValue) {
+        if (properties.put(name, newValue) != oldValue) {
+            throw new ConcurrentModificationException(name);
+        }
+    }
+
+    /**
+     * Returns {@code true} if the given type is an attribute type, and if the given value
+     * is valid for that attribute type.
+     */
+    private static boolean isValidAttributeValue(final PropertyType type, final Object value) {
+        if (!(type instanceof DefaultAttributeType<?>)) {
+            return false;
+        }
+        if (value == null) {
+            return true;
+        }
+        return ((DefaultAttributeType<?>) type).getValueClass().isInstance(value);
+    }
+
+    /**
+     * Sets the attribute value after verification of its type. This method is invoked only for checking
+     * that we are not violating the Java parameterized type contract. For a more exhaustive validation,
+     * use {@link Validator} instead.
      */
     @SuppressWarnings("unchecked")
-    public void setAttributeValue(final String name, final Object value) {
-        DefaultAttribute<?> attribute = properties.get(name);
-        if (attribute == null) {
-            final DefaultAttributeType<?> at = type.getProperty(name);
-            if (at == null) {
-                throw new IllegalArgumentException(propertyNotFound(name));
-            }
-            if (Objects.equals(value, at.getDefaultValue())) {
-                return; // Avoid creating the attribute if not necessary.
-            }
-            attribute = new DefaultAttribute(at);
-            if (properties.put(name, attribute) != null) {
-                throw new ConcurrentModificationException();
+    private static <T> void setAttributeValue(final DefaultAttribute<T> attribute, final Object value) {
+        if (value != null) {
+            final DefaultAttributeType<T> pt = attribute.getType();
+            if (!pt.getValueClass().isInstance(value)) {
+                throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
+                        pt.getName(), value.getClass()));
             }
         }
-        ArgumentChecks.ensureCanCast(name, attribute.getType().getValueClass(), value);
         ((DefaultAttribute) attribute).setValue(value);
     }
 
     /**
-     * Returns the error message for a property not found.
+     * Verifies if all current properties met the constraints defined by the feature type.
+     * This method returns {@linkplain org.apache.sis.metadata.iso.quality.DefaultDataQuality#getReports()
+     * reports} for all constraint violations found, if any.
+     *
+     * <div class="note"><b>Example:</b> given a feature with an attribute named “population”.
+     * If this attribute is mandatory ([1 … 1] cardinality) but no value has been assigned to it,
+     * then this {@code validate()} method will return the following data quality report:
+     *
+     * {@preformat text
+     *   Data quality
+     *     ├─Scope
+     *     │   └─Level………………………………………………… Feature
+     *     └─Report
+     *         ├─Measure identification
+     *         │   └─Code………………………………………… population
+     *         ├─Evaluation method type…… Direct internal
+     *         └─Result
+     *             ├─Explanation……………………… Missing value for “population” property.
+     *             └─Pass………………………………………… false
+     * }
+     * </div>
+     *
+     * This feature is valid if this method does not report any
+     * {@linkplain org.apache.sis.metadata.iso.quality.DefaultConformanceResult conformance result} having a
+     * {@linkplain org.apache.sis.metadata.iso.quality.DefaultConformanceResult#pass() pass} value of {@code false}.
+     *
+     * @return Reports on all constraint violations found.
+     *
+     * @see DefaultAttribute#validate()
+     * @see DefaultAssociation#validate()
      */
-    private String propertyNotFound(final String name) {
-        return Errors.format(Errors.Keys.PropertyNotFound_2, type.getName(), name);
+    /*
+     * API NOTE: this method is final for now because if we allowed users to override it, users would
+     * expect their method to be invoked by DefaultAssociation.validate(). But this is not yet the case.
+     */
+    public final DataQuality validate() {
+        final Validator v = new Validator(ScopeCode.FEATURE);
+        for (final Map.Entry<String, Object> entry : properties.entrySet()) {
+            v.validateAny(getPropertyType(entry.getKey()), entry.getValue());
+        }
+        return v.quality;
     }
 
     /**
@@ -148,7 +380,7 @@ public class DefaultFeature implements S
         if (obj == this) {
             return true;
         }
-        if (super.equals(obj)) {
+        if (obj != null && obj.getClass() == getClass()) {
             final DefaultFeature that = (DefaultFeature) obj;
             return type.equals(that.type) &&
                    properties.equals(that.properties);
@@ -167,8 +399,16 @@ public class DefaultFeature implements S
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         final String lineSeparator = JDK7.lineSeparator();
-        for (final DefaultAttribute<?> attribute : properties.values()) {
-            sb.append(attribute.getType().getName()).append(": ").append(attribute.getValue()).append(lineSeparator);
+        for (final Map.Entry<String,Object> entry : properties.entrySet()) {
+            final PropertyType pt;
+            Object element = entry.getValue();
+            if (propertiesAreInstantiated) {
+                pt = ((DefaultAttribute<?>) element).getType();
+                element = ((DefaultAttribute<?>) element).getValue();
+            } else {
+                pt = type.getProperty(entry.getKey());
+            }
+            sb.append(pt.getName()).append(": ").append(element).append(lineSeparator);
         }
         return sb.toString();
     }

Propchange: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java
------------------------------------------------------------------------------
  Merged /sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java:r1591681-1595037
  Merged /sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java:r1591679-1595029

Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1595039&r1=1595038&r2=1595039&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Thu May 15 20:48:31 2014
@@ -16,16 +16,20 @@
  */
 package org.apache.sis.feature;
 
-import java.util.Map;
-import java.util.Set;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.HashMap;
-import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.IdentityHashMap;
+import java.util.Collection;
 import java.util.Collections;
 import java.io.IOException;
 import java.io.ObjectInputStream;
-import java.lang.reflect.Field;
 import org.opengis.util.GenericName;
+import org.opengis.util.InternationalString;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.Containers;
@@ -46,11 +50,42 @@ import org.apache.sis.internal.util.Unmo
  * When such interface will be available, most references to {@code DefaultFeatureType} in the API
  * will be replaced by references to the {@code FeatureType} interface.</div>
  *
+ * {@section Naming}
+ * The feature type {@linkplain #getName() name} is mandatory and should be unique. Those names are the main
+ * criterion used for deciding if a feature type {@linkplain #isAssignableFrom is assignable from} another type.
+ * Names can be {@linkplain org.apache.sis.util.iso.DefaultScopedName scoped} for avoiding name collision.
+ *
+ * {@section Properties and inheritance}
+ * Each feature type can provide descriptions for the following {@link #getProperties(boolean) properties}:
+ *
+ * <ul>
+ *   <li>{@linkplain DefaultAttributeType    Attributes}</li>
+ *   <li>{@linkplain DefaultAssociationRole  Associations to other feature types}</li>
+ *   <li>{@linkplain DefaultOperation        Operations}</li>
+ * </ul>
+ *
+ * In addition, a feature type can inherit the properties of one or more other feature types.
+ * Properties defined in the sub-type can override properties of the same name defined in the
+ * {@link #getSuperTypes() super-types}, provided that values of the sub-type property are
+ * assignable to the super-type property.
+ *
+ * <div class="note"><b>Analogy:</b> compared to the Java language, the above rule is similar to overriding a method
+ * with a more specific return type (a.k.a. <cite>covariant return type</cite>). This is also similar to Java arrays,
+ * which are implicitly <cite>covariant</cite> (i.e. {@code String[]} can be casted to {@code CharSequence[]}, which
+ * is safe for read operations but not for write operations — the later may throw {@link ArrayStoreException}).</div>
+ *
+ * {@section Immutability and thread safety}
+ * Instances of this class are immutable if all properties ({@link GenericName} and {@link InternationalString}
+ * instances) and all arguments ({@link AttributeType} instances) given to the constructor are also immutable.
+ * Such immutable instances can be shared by many objects and passed between threads without synchronization.
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
  * @version 0.5
  * @module
+ *
+ * @see DefaultFeature
  */
 public class DefaultFeatureType extends AbstractIdentifiedType {
     /**
@@ -66,125 +101,251 @@ public class DefaultFeatureType extends 
     private final boolean isAbstract;
 
     /**
-     * The parents of this feature type, or an empty set if none.
+     * {@code true} if this feature type contains only attributes constrained to the [1 … 1] cardinality,
+     * or operations.
+     *
+     * @see #isSimple()
+     */
+    private transient boolean isSimple;
+
+    /**
+     * The direct parents of this feature type, or an empty set if none.
+     *
+     * @see #getSuperTypes()
      */
     private final Set<DefaultFeatureType> superTypes;
 
     /**
+     * The names of all parents of this feature type, including parents of parents. This is used
+     * for a more efficient implementation of {@link #isAssignableFrom(DefaultFeatureType)}.
+     *
+     * @see #isAssignableFrom(DefaultFeatureType)
+     */
+    private transient Set<GenericName> assignableTo;
+
+    /**
      * Any feature operation, any feature attribute type and any feature association role
      * that carries characteristics of a feature type.
+     *
+     * @see #getProperties(boolean)
      */
-    private final List<DefaultAttributeType<?>> characteristics;
+    private final List<PropertyType> properties;
 
     /**
-     * A lookup table for fetching properties by name.
+     * A lookup table for fetching properties by name, including the properties from super-types.
      * This map shall not be modified after construction.
      *
      * @see #getProperty(String)
      */
-    private final transient Map<String, DefaultAttributeType<?>> byName;
+    private transient Map<String, PropertyType> byName;
 
     /**
-     * Constructs a feature type from the given properties. The properties map is given unchanged to
+     * Indices of properties in an array of properties similar to {@link #properties},
+     * but excluding operations. This map includes the properties from the super-types.
+     *
+     * The size of this map may be smaller than the {@link #byName} size.
+     * This map shall not be modified after construction.
+     */
+    private transient Map<String, Integer> indices;
+
+    /**
+     * Constructs a feature type from the given properties. The identification map is given unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
-     * The following table is a reminder of main (not all) properties:
+     * The following table is a reminder of main (not all) recognized map entries:
      *
      * <table class="sis">
-     *   <caption>Recognized properties (non exhaustive list)</caption>
+     *   <caption>Recognized map entries (non exhaustive list)</caption>
      *   <tr>
-     *     <th>Property name</th>
+     *     <th>Map key</th>
      *     <th>Value type</th>
      *     <th>Returned by</th>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#NAME_KEY}</td>
-     *     <td>{@link org.opengis.util.GenericName} or {@link String}</td>
+     *     <td>{@link GenericName} or {@link String}</td>
      *     <td>{@link #getName()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#DEFINITION_KEY}</td>
-     *     <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
      *     <td>{@link #getDefinition()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#DESIGNATION_KEY}</td>
-     *     <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
      *     <td>{@link #getDesignation()}</td>
      *   </tr>
      *   <tr>
      *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#DESCRIPTION_KEY}</td>
-     *     <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
+     *     <td>{@link InternationalString} or {@link String}</td>
      *     <td>{@link #getDescription()}</td>
      *   </tr>
      * </table>
      *
-     * @param properties The name and other properties to be given to this feature type.
-     * @param isAbstract If {@code true}, the feature type acts as an abstract super-type.
-     * @param superTypes The parents of this feature type, or {@code null} or empty if none.
-     * @param characteristics Any feature operation, any feature attribute type and any feature
-     *        association role that carries characteristics of a feature type.
+     * <div class="warning"><b>Warning:</b> In a future SIS version, the type of array elements may be
+     * changed to {@code org.opengis.feature.FeatureType} and {@code org.opengis.feature.PropertyType}.
+     * This change is pending GeoAPI revision. In the meantime, make sure that the {@code properties}
+     * array contains only attribute types, association roles or operations, <strong>not</strong> other
+     * feature types since the later are not properties in the ISO sense.</div>
+     *
+     * @param identification The name and other information to be given to this feature type.
+     * @param isAbstract     If {@code true}, the feature type acts as an abstract super-type.
+     * @param superTypes     The parents of this feature type, or {@code null} or empty if none.
+     * @param properties     Any feature operation, any feature attribute type and any feature
+     *                       association role that carries characteristics of a feature type.
      */
-    public DefaultFeatureType(final Map<String,?> properties, final boolean isAbstract,
-            final DefaultFeatureType[] superTypes, final DefaultAttributeType<?>... characteristics)
+    public DefaultFeatureType(final Map<String,?> identification, final boolean isAbstract,
+            final DefaultFeatureType[] superTypes, final AbstractIdentifiedType... properties)
     {
-        super(properties);
-        ArgumentChecks.ensureNonNull("characteristics", characteristics);
+        super(identification);
+        ArgumentChecks.ensureNonNull("properties", properties);
         this.isAbstract = isAbstract;
         this.superTypes = (superTypes == null) ? Collections.<DefaultFeatureType>emptySet() :
                           CollectionsExt.<DefaultFeatureType>immutableSet(true, superTypes);
-        this.characteristics = (List) UnmodifiableArrayList.wrap(Arrays.copyOf(
-                characteristics, characteristics.length, DefaultAttributeType[].class));
-        byName = byName(this.characteristics);
+        switch (properties.length) {
+            case 0:  this.properties = Collections.emptyList(); break;
+            case 1:  this.properties = Collections.singletonList((PropertyType) properties[0]); break;
+            default: this.properties = UnmodifiableArrayList.wrap(Arrays.copyOf(properties, properties.length, PropertyType[].class)); break;
+        }
+        computeTransientFields();
+    }
+
+    /**
+     * Invoked on deserialization for restoring the {@link #byName} and other transient fields.
+     *
+     * @param  in The input stream from which to deserialize a feature type.
+     * @throws IOException If an I/O error occurred while reading or if the stream contains invalid data.
+     * @throws ClassNotFoundException If the class serialized on the stream is not on the classpath.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        computeTransientFields();
     }
 
     /**
-     * Creates a (<var>name</var>, <var>property</var>) map from the given list.
-     * This is used only for performance purpose when searching for attributes.
+     * Computes all transient fields ({@link #assignableTo}, {@link #byName}, {@link #indices}, {@link #isSimple}).
      *
      * <p>As a side effect, this method checks for missing or duplicated names.</p>
      *
-     * @param  characteristics The list to convert to a (<var>name</var>, <var>property</var>) map.
-     * @return The map of properties.
      * @throws IllegalArgumentException if two properties have the same name.
      */
-    private static Map<String, DefaultAttributeType<?>> byName(final List<DefaultAttributeType<?>> characteristics) {
-        final int length = characteristics.size();
-        final Map<String, DefaultAttributeType<?>> byName =
-                new HashMap<String, DefaultAttributeType<?>>(Containers.hashMapCapacity(length));
-        for (int i=0; i<length; i++) {
-            final DefaultAttributeType<?> c = characteristics.get(i);
-            ArgumentChecks.ensureNonNullElement("characteristics", i, c);
-            final GenericName name = c.getName();
-            if (name == null) {
-                throw new IllegalArgumentException(Errors.format(
-                        Errors.Keys.MissingValueForProperty_1, "characteristics[" + i + "].name"));
+    private void computeTransientFields() {
+        final int capacity = Containers.hashMapCapacity(properties.size());
+        isSimple     = true;
+        byName       = new LinkedHashMap<String,PropertyType>(capacity);
+        indices      = new HashMap<String,Integer>(capacity);
+        assignableTo = new HashSet<GenericName>(4);
+        assignableTo.add(getName());
+        scanPropertiesFrom(this);
+        byName       = CollectionsExt.unmodifiableOrCopy(byName);
+        indices      = CollectionsExt.unmodifiableOrCopy(indices);
+        assignableTo = CollectionsExt.unmodifiableOrCopy(assignableTo);
+    }
+
+    /**
+     * Computes the transient fields using the non-transient information in the given {@code source}.
+     * This method invokes itself recursively in order to use the information provided in super-types.
+     */
+    private void scanPropertiesFrom(final DefaultFeatureType source) {
+        /*
+         * Process all super-types before to process the given type. The intend is to have the
+         * super-types properties indexed before the sub-types ones in the 'indices' map.
+         */
+        for (final DefaultFeatureType parent : source.getSuperTypes()) {
+            if (assignableTo.add(parent.getName())) {
+                scanPropertiesFrom(parent);
             }
-            final String s = name.toString();
-            if (byName.put(s, c) != null) {
-                throw new IllegalArgumentException(Errors.format(Errors.Keys.DuplicatedIdentifier_1, s));
+        }
+        int index = -1;
+        Map<DefaultFeatureType,Boolean> done = null;
+        for (final PropertyType property : source.properties) {
+            ArgumentChecks.ensureNonNullElement("properties", ++index, property);
+            /*
+             * Fill the (name, property) map with opportunist verification of argument validity.
+             */
+            final String name = toString(property.getName(), source, index);
+            final PropertyType previous = byName.put(name, property);
+            if (previous != null) {
+                if (done == null) {
+                    done = new IdentityHashMap<DefaultFeatureType,Boolean>(4); // Guard against infinite recursivity.
+                }
+                if (!isAssignableIgnoreName(previous, property, done)) {
+                    final GenericName owner = ownerOf(previous);
+                    throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyAlreadyExists_2,
+                            (owner != null) ? owner : "?", name));
+                }
+                done.clear();
+            }
+            /*
+             * Fill the (name, indice) map. Values are indices that the property elements would have
+             * in a flat array. This block also opportunistically check if the FeatureType is "simple".
+             */
+            final int maximumOccurs;
+            if (property instanceof DefaultAttributeType<?>) { // TODO: check for AttributeType instead (after GeoAPI upgrade).
+                maximumOccurs = ((DefaultAttributeType<?>) property).getMaximumOccurs();
+                if (isSimple && ((DefaultAttributeType<?>) property).getMinimumOccurs() != maximumOccurs) {
+                    isSimple = false;
+                }
+            } else if (property instanceof FieldType) { // TODO: check for AssociationRole instead (after GeoAPI upgrade).
+                maximumOccurs = ((FieldType) property).getMaximumOccurs();
+                isSimple = false;
+            } else {
+                continue; // For feature operations, maximumOccurs is implicitly 0.
+            }
+            if (maximumOccurs != 0) {
+                isSimple &= (maximumOccurs == 1);
+                indices.put(name, indices.size());
+            }
+        }
+    }
+
+    /**
+     * Returns the name of the feature which defines the given property, or {@code null} if not found.
+     * This method is for information purpose when producing an error message - its implementation does
+     * not need to be efficient.
+     */
+    private GenericName ownerOf(final PropertyType property) {
+        if (properties.contains(property)) {
+            return getName();
+        }
+        for (final DefaultFeatureType type : superTypes) {
+            final GenericName owner = type.ownerOf(property);
+            if (owner != null) {
+                return owner;
             }
         }
-        return byName;
+        return null;
     }
 
     /**
-     * Invoked on deserialization for restoring the {@link #byName} map.
+     * Returns the string representation of the given name, making sure that the name is non-null
+     * and the string non-empty. This method is used for checking argument validity.
      *
-     * @param  in The input stream from which to deserialize a feature type.
-     * @throws IOException If an I/O error occurred while reading or if the stream contains invalid data.
-     * @throws ClassNotFoundException If the class serialized on the stream is not on the classpath.
-     */
-    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
-        in.defaultReadObject();
-        try {
-            final Field field = DefaultFeatureType.class.getDeclaredField("byName");
-            field.setAccessible(true);
-            field.set(this, byName(characteristics));
-        } catch (Exception e) { // ReflectiveOperationException on the JDK7 branch.
-            throw new AssertionError(e);
+     * @param name   The name for which to get the string representation.
+     * @param source The feature which contains the property (typically {@code this}).
+     * @param index  Index of the property having the given name.
+     */
+    private String toString(final GenericName name, final DefaultFeatureType source, final int index) {
+        short key = Errors.Keys.MissingValueForProperty_1;
+        if (name != null) {
+            final String s = name.toString();
+            if (!s.isEmpty()) {
+                return s;
+            }
+            key = Errors.Keys.EmptyProperty_1;
         }
+        final StringBuilder b = new StringBuilder(30);
+        if (source != this) {
+            b.append(source.getName()).append('.');
+        }
+        throw new IllegalArgumentException(Errors.format(key,
+                b.append("properties[").append(index).append("].name").toString()));
     }
 
+
+    // -------- END OF CONSTRUCTORS ------------------------------------------------------------------------------
+
+
     /**
      * Returns {@code true} if the feature type acts as an abstract super-type.
      *
@@ -195,26 +356,141 @@ public class DefaultFeatureType extends 
     }
 
     /**
-     * Returns the parents of this feature type.
+     * Returns {@code true} if this feature type contains only attributes constrained to the [1 … 1] cardinality,
+     * or operations. Such feature types can be handled as a {@link org.opengis.util.Record}s.
+     *
+     * @return {@code true} if this feature type contains only simple attributes or operations.
+     */
+    public boolean isSimple() {
+        return isSimple;
+    }
+
+    /**
+     * Returns {@code true} if this type may be the same or a super-type of the given type, using only
+     * the name as a criterion. This is a faster check than {@link #isAssignableFrom(DefaultFeatureType)}
+     */
+    final boolean maybeAssignableFrom(final DefaultFeatureType type) {
+        return type.assignableTo.contains(getName());
+    }
+
+    /**
+     * Returns {@code true} if this type is same or a super-type of the given type.
+     * The check is based mainly on the feature type {@linkplain #getName() name}, which should be unique.
+     * However as a safety, this method also checks that all properties in this feature type is assignable
+     * from a property of the same name in the given type.
+     *
+     * @param  type The type to be checked.
+     * @return {@code true} if instances of the given type can be assigned to association of this type.
+     */
+    public boolean isAssignableFrom(final DefaultFeatureType type) {
+        if (type == this) {
+            return true; // Optimization for a common case.
+        }
+        ArgumentChecks.ensureNonNull("type", type);
+        return maybeAssignableFrom(type) && isAssignableIgnoreName(type,
+                new IdentityHashMap<DefaultFeatureType,Boolean>(4));
+    }
+
+    /**
+     * Return {@code true} if all properties in this type are also properties in the given type.
+     * This method does not compare the names — this verification is presumed already done by the caller.
+     *
+     * @param type The type to check.
+     * @param done An initially empty map to be used for avoiding infinite recursivity.
+     */
+    private boolean isAssignableIgnoreName(final DefaultFeatureType type, final Map<DefaultFeatureType,Boolean> done) {
+        if (done.put(this, Boolean.TRUE) == null) {
+            /*
+             * Ensures that all properties defined in this feature type is also defined
+             * in the given property, and that the former is assignable from the later.
+             */
+            for (final Map.Entry<String, PropertyType> entry : byName.entrySet()) {
+                final PropertyType other = type.getProperty(entry.getKey());
+                if (other == null || !isAssignableIgnoreName(entry.getValue(), other, done)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns {@code true} if instances of the {@code other} type are assignable to the given {@code base} type.
+     * This method does not compare the names — this verification is presumed already done by the caller.
+     */
+    private static boolean isAssignableIgnoreName(final PropertyType base, final PropertyType other,
+            final Map<DefaultFeatureType,Boolean> done)
+    {
+        if (base != other) {
+            /*
+             * TODO: DefaultAttributeType and DefaultAssociationRole to be replaced by GeoAPI interfaces
+             *       (pending GeoAPI review).
+             */
+            if (base instanceof DefaultAttributeType<?>) {
+                if (!(other instanceof DefaultAttributeType<?>)) {
+                    return false;
+                }
+                final DefaultAttributeType<?> p0 = (DefaultAttributeType<?>) base;
+                final DefaultAttributeType<?> p1 = (DefaultAttributeType<?>) other;
+                if (!p0.getValueClass().isAssignableFrom(p1.getValueClass()) ||
+                     p0.getMinimumOccurs() > p1.getMinimumOccurs() ||
+                     p0.getMaximumOccurs() < p1.getMaximumOccurs())
+                {
+                    return false;
+                }
+            }
+            if (base instanceof DefaultAssociationRole) {
+                if (!(other instanceof DefaultAssociationRole)) {
+                    return false;
+                }
+                final DefaultAssociationRole p0 = (DefaultAssociationRole) base;
+                final DefaultAssociationRole p1 = (DefaultAssociationRole) other;
+                if (p0.getMinimumOccurs() > p1.getMinimumOccurs() ||
+                    p0.getMaximumOccurs() < p1.getMaximumOccurs())
+                {
+                    return false;
+                }
+                final DefaultFeatureType f0 = p0.getValueType();
+                final DefaultFeatureType f1 = p1.getValueType();
+                if (!f0.maybeAssignableFrom(f1) || !f0.isAssignableIgnoreName(f1, done)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the direct parents of this feature type.
+     *
+     * <div class="warning"><b>Warning:</b>
+     * The type of list elements will be changed to {@code FeatureType} if and when such interface
+     * will be defined in GeoAPI.</div>
      *
      * @return The parents of this feature type, or an empty set if none.
      */
-    public Set<DefaultFeatureType> superTypes() {
+    public Set<DefaultFeatureType> getSuperTypes() {
         return superTypes;
     }
 
     /**
-     * Returns any feature operation, any feature attribute type and any feature association role
-     * that carries characteristics of a feature type.
+     * Returns any feature operation, any feature attribute type and any feature association role that
+     * carries characteristics of a feature type. The returned collection will include the properties
+     * inherited from the {@link #getSuperTypes() super-types} only if {@code includeSuperTypes} is
+     * {@code true}.
      *
      * <div class="warning"><b>Warning:</b>
      * The type of list elements will be changed to {@code PropertyType} if and when such interface
      * will be defined in GeoAPI.</div>
      *
-     * @return Feature operation, attribute type and association role that carries characteristics of a feature type.
-     */
-    public List<DefaultAttributeType<?>> getCharacteristics() {
-        return characteristics;
+     * @param  includeSuperTypes {@code true} for including the properties inherited from the super-types,
+     *         or {@code false} for returning only the properties defined explicitely in this type.
+     * @return Feature operation, attribute type and association role that carries characteristics of this
+     *         feature type (not including parent types).
+     */
+    public Collection<AbstractIdentifiedType> getProperties(final boolean includeSuperTypes) {
+        // TODO: temporary cast to be removed after we upgraded GeoAPI.
+        return (Collection) (includeSuperTypes ? byName.values() : properties);
     }
 
     /**
@@ -223,18 +499,26 @@ public class DefaultFeatureType extends 
      * @param  name The name of the property to search.
      * @return The property for the given name, or {@code null} if none.
      */
-    final DefaultAttributeType<?> getProperty(final String name) {
+    final PropertyType getProperty(final String name) {
         return byName.get(name);
     }
 
     /**
+     * Returns the number of attributes and features (not operations) that instance will have
+     * if all attributes are handled as simple attributes (maximum occurrences of 1).
+     */
+    final int getInstanceSize() {
+        return indices.size();
+    }
+
+    /**
      * Returns a hash code value for this feature type.
      *
      * @return {@inheritDoc}
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + superTypes.hashCode() + 37*superTypes.hashCode();
+        return super.hashCode() + superTypes.hashCode() + 37*properties.hashCode();
     }
 
     /**
@@ -251,7 +535,7 @@ public class DefaultFeatureType extends 
             final DefaultFeatureType that = (DefaultFeatureType) obj;
             return isAbstract == that.isAbstract &&
                    superTypes.equals(that.superTypes) &&
-                   characteristics.equals(that.characteristics);
+                   properties.equals(that.properties);
         }
         return false;
     }

Copied: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java (from r1595037, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java?p2=sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java&p1=sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java&r1=1595037&r2=1595039&rev=1595039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java [UTF-8] Thu May 15 20:48:31 2014
@@ -25,7 +25,7 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.Debug;
 
 // Related to JDK7
-import java.util.Objects;
+import org.apache.sis.internal.jdk7.Objects;
 
 
 /**



Mime
View raw message