sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1634239 [1/3] - in /sis/trunk: ./ application/sis-javafx/ core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ core/sis-build-helper/src/test/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/test/j...
Date Sat, 25 Oct 2014 18:11:36 GMT
Author: desruisseaux
Date: Sat Oct 25 18:11:34 2014
New Revision: 1634239

URL: http://svn.apache.org/r1634239
Log:
Merge from the JDK6 branch (avoid usage of deprecated methods; skeleton classes for JDBC driver).

Added:
    sis/trunk/core/sis-build-helper/src/test/
      - copied from r1634173, sis/branches/JDK6/core/sis-build-helper/src/test/
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java   (with props)
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
      - copied, changed from r1634173, sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Standards.java
      - copied unchanged from r1634173, sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Standards.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
      - copied, changed from r1634173, sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/internal/
      - copied from r1634173, sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/internal/
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/Database.java
      - copied, changed from r1634173, sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/Database.java
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/package-info.java
      - copied unchanged from r1634173, sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/package-info.java
    sis/trunk/storage/sis-shapefile/src/test/java/org/apache/sis/internal/
      - copied from r1634173, sis/branches/JDK6/storage/sis-shapefile/src/test/java/org/apache/sis/internal/
Removed:
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/CmdLineDriver.java
    sis/trunk/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/CmdLineDriverTest.java
Modified:
    sis/trunk/   (props changed)
    sis/trunk/application/sis-javafx/pom.xml
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java
    sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
    sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
    sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
    sis/trunk/core/sis-metadata/src/main/resources/org/apache/sis/metadata/api-changes.properties
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataTestCase.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/PrunerTest.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DefaultMetadataTest.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ReferencingInMetadataTest.java
    sis/trunk/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Version.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/XMLComparator.java
    sis/trunk/ide-project/NetBeans/build.xml
    sis/trunk/ide-project/NetBeans/nbproject/project.properties
    sis/trunk/pom.xml
    sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
    sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
    sis/trunk/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
    sis/trunk/storage/sis-shapefile/pom.xml
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/CodePage.java
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/DataType.java
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/FieldDescriptor.java
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeTypeEnum.java
    sis/trunk/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/ShapeFileTest.java
    sis/trunk/storage/sis-shapefile/src/test/java/org/apache/sis/test/suite/ShapefileTestSuite.java
    sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/XMLStoreTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK8:r1631331-1634096
  Merged /sis/branches/JDK7:r1631333-1634098
  Merged /sis/branches/JDK6:r1631338-1634173

Modified: sis/trunk/application/sis-javafx/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-javafx/pom.xml?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/application/sis-javafx/pom.xml (original)
+++ sis/trunk/application/sis-javafx/pom.xml Sat Oct 25 18:11:34 2014
@@ -28,7 +28,7 @@
   <parent>
     <groupId>org.apache.sis</groupId>
     <artifactId>application</artifactId>
-    <version>0.5-jdk8-SNAPSHOT</version>
+    <version>0.5-jdk7-SNAPSHOT</version>
   </parent>
 
 

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -18,6 +18,7 @@ package org.apache.sis.util.resources;
 
 import java.io.File;
 import java.io.FilenameFilter;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.maven.model.Resource;
@@ -27,6 +28,9 @@ import org.apache.maven.project.MavenPro
 import org.codehaus.plexus.util.Scanner;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
+import static org.apache.sis.util.resources.IndexedResourceCompiler.JAVA_EXT;
+import static org.apache.sis.util.resources.IndexedResourceCompiler.PROPERTIES_EXT;
+
 
 /**
  * Compiles the international resources that are found in the module from which this mojo is invoked.
@@ -35,7 +39,7 @@ import org.sonatype.plexus.build.increme
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Olivier Nouguier (Geomatys)
  * @since   0.3 (derived from geotk-3.00)
- * @version 0.4
+ * @version 0.5
  * @module
  *
  * @goal compile-resources
@@ -96,6 +100,12 @@ public class ResourceCompilerMojo extend
     private File javaDirectoryFile;
 
     /**
+     * Constructs a new resource compiler MOJO.
+     */
+    public ResourceCompilerMojo() {
+    }
+
+    /**
      * Executes the mojo.
      *
      * @throws MojoExecutionException if the plugin execution failed.
@@ -146,8 +156,8 @@ public class ResourceCompilerMojo extend
     }
 
     /**
-     * Recursively scans the directories for a sub-package named "resources",
-     * then invokes the resource compiler for that directory.
+     * Recursively scans the directories and find all Java classes having a property files of the same name.
+     * Then invokes the resource compiler for those files.
      */
     private int processAllResourceDirectories(final File directory) throws ResourceCompilerException {
         int errors = 0;
@@ -155,14 +165,16 @@ public class ResourceCompilerMojo extend
         if (subdirs != null) { // Appears to be sometime null with auto-generated sub-directories.
             for (final File subdir : subdirs) {
                 if (subdir.isDirectory()) {
-                    if (subdir.getName().equals("resources")) {
-                        final File[] resourcesToProcess = subdir.listFiles(this);
-                        if (resourcesToProcess != null && resourcesToProcess.length != 0) {
+                    File[] resourcesToProcess = subdir.listFiles(this);
+                    int count = filterLanguages(resourcesToProcess);
+                    if (count != 0) {
+                        count = toJavaSourceFiles(resourcesToProcess, count);
+                        if (count != 0) {
+                            resourcesToProcess = Arrays.copyOf(resourcesToProcess, count);
                             errors += new Compiler(resourcesToProcess).run();
                         }
-                    } else {
-                        errors += processAllResourceDirectories(subdir);
                     }
+                    errors += processAllResourceDirectories(subdir);
                 }
             }
         }
@@ -170,22 +182,84 @@ public class ResourceCompilerMojo extend
     }
 
     /**
-     * Returns {@code true} if the given file is the source code for a resources bundle.
-     * This method returns {@code true} if the given file is a Java source file and if a
-     * properties file of the same name exists.
+     * Accepts all {@code "*.properties"} files.
      *
      * @param directory The directory.
      * @param name The file name.
      * @return {@code true} if the given file is a property file.
      */
     @Override
-    public final boolean accept(final File directory, String name) {
-        if (!name.endsWith(IndexedResourceCompiler.JAVA_EXT)) {
-            return false;
-        }
-        name = name.substring(0, name.length() - IndexedResourceCompiler.JAVA_EXT.length());
-        name += IndexedResourceCompiler.PROPERTIES_EXT;
-        return new File(directory, name).isFile();
+    public final boolean accept(final File directory, final String name) {
+        return name.endsWith(PROPERTIES_EXT);
+    }
+
+    /**
+     * Retains only the properties files which seems to be about internationalized resources.
+     * For example if the given array contains the following files:
+     * <ul>
+     *   <li>{@code "Errors.properties"}</li>
+     *   <li>{@code "Errors_en.properties"}</li>
+     *   <li>{@code "Errors_fr.properties"}</li>
+     *   <li>{@code "Messages.properties"}</li>
+     *   <li>{@code "Messages_en.properties"}</li>
+     *   <li>{@code "Messages_fr.properties"}</li>
+     *   <li>{@code "NotAnInternationalResource.properties"}</li>
+     * </ul>
+     *
+     * Then this method will retain the following files:
+     * <ul>
+     *   <li>{@code "Errors.properties"}</li>
+     *   <li>{@code "Messages.properties"}</li>
+     * </ul>
+     *
+     * @param  resourcesToProcess The files to filter. This array will be overwritten in-place.
+     * @return Number of valid elements in the {@code resourcesToProcess} after this method completion.
+     */
+    static int filterLanguages(final File[] resourcesToProcess) {
+        int count = 0;
+        if (resourcesToProcess != null) {
+            Arrays.sort(resourcesToProcess);
+            for (int i=0; i<resourcesToProcess.length;) {
+                final File file = resourcesToProcess[i];
+                String name = file.getName();
+                name = name.substring(0, name.length() - PROPERTIES_EXT.length()) + '_';
+                final int fileIndex = i;
+                while (++i < resourcesToProcess.length) {
+                    if (!resourcesToProcess[i].getName().startsWith(name)) {
+                        break;
+                    }
+                }
+                // Accepts the property file only if we found at least one language.
+                // Example: "Messages.properties" and "Messages_en.properties".
+                if (i - fileIndex >= 2) {
+                    resourcesToProcess[count++] = file;
+                }
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Converts the given property files into Java source file, provided that the later exists.
+     * The given array is overwritten in place.
+     *
+     * @param  resourcesToProcess The filtered resource files, as returned by {@link #filterLanguages(File[])}.
+     * @param  count Number of valid elements in {@code resourcesToProcess}.
+     * @return Number of valid elements after this method completion.
+     */
+    private static int toJavaSourceFiles(final File[] resourcesToProcess, final int count) {
+        int n = 0;
+        for (int i=0; i<count; i++) {
+            File file = resourcesToProcess[i];
+            String name = file.getName();
+            name = name.substring(0, name.length() - PROPERTIES_EXT.length());
+            name += JAVA_EXT;
+            file = new File(file.getParentFile(), name);
+            if (file.isFile()) {
+                resourcesToProcess[n++] = file;
+            }
+        }
+        return n;
     }
 
     /**

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -225,7 +225,7 @@ public abstract class AbstractAssociatio
     public String toString() {
         final String pt = DefaultAssociationRole.getTitleProperty(role);
         final Iterator<AbstractFeature> it = getValues().iterator();
-        return FieldType.toString("FeatureAssociation", role, role.getValueType().getName(), new Iterator<Object>() {
+        return FieldType.toString("FeatureAssociation", role, DefaultAssociationRole.getValueTypeName(role), new Iterator<Object>() {
             @Override public boolean hasNext() {
                 return it.hasNext();
             }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -207,9 +207,16 @@ public class AbstractIdentifiedType impl
      * <p>For {@linkplain DefaultFeatureType feature types}, the name is mandatory and shall be unique
      * in the unit processing the data (e.g. a {@link org.apache.sis.storage.DataStore} reading a file).</p>
      *
+     * <div class="note"><b>Note for subclasses:</b>
+     * this method is final because it is invoked (indirectly) by subclass constructors,
+     * and invoking a user-overrideable method at construction time is not recommended.
+     * Furthermore, this attribute is often used as the primary key for {@code IdentifiedType} instances
+     * and need some guarantees about its stability.
+     * </div>
+     *
      * @return The type name.
      */
-    public GenericName getName() {
+    public final GenericName getName() {
         return name;
     }
 

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -17,8 +17,12 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.Debug;
 
 import static org.apache.sis.util.ArgumentChecks.*;
@@ -59,7 +63,7 @@ public class DefaultAssociationRole exte
      *
      * @see #getValueType()
      */
-    private final DefaultFeatureType valueType;
+    private volatile FeatureType valueType;
 
     /**
      * The name of the property to use as a title for the associated feature, or an empty string if none.
@@ -71,7 +75,7 @@ public class DefaultAssociationRole exte
     private volatile transient String titleProperty;
 
     /**
-     * Constructs an association role from the given properties. The properties map is given unchanged to
+     * Constructs an association to the given feature type. The properties map is given unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      * The following table is a reminder of main (not all) recognized map entries:
      *
@@ -119,15 +123,204 @@ public class DefaultAssociationRole exte
     }
 
     /**
+     * Constructs an association to a feature type of the given name.
+     * This constructor can be used when creating a cyclic graph of {@link DefaultFeatureType} instances.
+     * In such cases, at least one association needs to be created while its {@code FeatureType} is not yet available.
+     *
+     * <div class="note"><b>Example:</b>
+     * The following establishes a bidirectional association between feature types <var>A</var> and <var>B</var>:
+     *
+     * {@preformat java
+     *   String    namespace = "My model";
+     *   GenericName nameOfA = Names.createTypeName(namespace, ":", "Feature type A");
+     *   GenericName nameOfB = Names.createTypeName(namespace, ":", "Feature type B");
+     *   FeatureType typeA = new DefaultFeatureType(nameOfA, false, null,
+     *       new DefaultAssociationRole(Names.createLocalName("Association to B"), nameOfB),
+     *       // More properties if desired.
+     *   );
+     *   FeatureType typeB = new DefaultFeatureType(nameOfB, false, null,
+     *       new DefaultAssociationRole(Names.createLocalName("Association to A"), featureA),
+     *       // More properties if desired.
+     *   );
+     * }
+     *
+     * After the above code completed, the {@linkplain #getValueType() value type} of "<cite>association to B</cite>"
+     * has been automatically set to the {@code typeB} instance.
+     * </div>
+     *
+     * Callers shall make sure that the feature types graph will not contain more than one feature of the given name.
+     * If more than one {@code FeatureType} instance of the given name is found at resolution time, the selected one
+     * is undetermined.
+     *
+     * @param identification The name and other information to be given to this association role.
+     * @param valueType      The name of the type of feature values.
+     * @param minimumOccurs  The minimum number of occurrences of the association within its containing entity.
+     * @param maximumOccurs  The maximum number of occurrences of the association within its containing entity,
+     *                       or {@link Integer#MAX_VALUE} if there is no restriction.
+     */
+    public DefaultAssociationRole(final Map<String,?> identification, final GenericName valueType,
+            final int minimumOccurs, final int maximumOccurs)
+    {
+        super(identification, minimumOccurs, maximumOccurs);
+        ensureNonNull("valueType", valueType);
+        this.valueType = new NamedFeatureType(valueType);
+    }
+
+    /**
+     * If the associated feature type is a placeholder for a {@code FeatureType} to be defined later,
+     * replaces the placeholder by the actual instance if available. Otherwise do nothing.
+     *
+     * This method is needed only in case of cyclic graph, e.g. feature <var>A</var> has an association
+     * to feature <var>B</var> which has an association back to <var>A</var>. It may also be <var>A</var>
+     * having an association to itself, <i>etc.</i>
+     *
+     * @param  creating The feature type in process of being constructed.
+     * @return {@code true} if this association references a resolved feature type after this method call.
+     */
+    final boolean resolve(final DefaultFeatureType creating) {
+        FeatureType type = valueType;
+        if (type instanceof NamedFeatureType) {
+            final GenericName name = type.getName();
+            if (name.equals(creating.getName())) {
+                type = creating; // This is the most common case.
+            } else {
+                /*
+                 * The feature that we need to resolve is not the one we just created. Maybe we can find
+                 * this desired feature in an association of the 'creating' feature, instead than beeing
+                 * the 'creating' feature itself. This is a little bit unusual, but not illegal.
+                 */
+                final List<DefaultFeatureType> deferred = new ArrayList<DefaultFeatureType>();
+                type = search(creating, name, deferred);
+                if (type == null) {
+                    /*
+                     * Did not found the desired FeatureType in the 'creating' instance.
+                     * Try harder, by searching recursively in associations of associations.
+                     */
+                    if (deferred.isEmpty() || (type = deepSearch(deferred, name)) == null) {
+                        return false;
+                    }
+                }
+            }
+            valueType = type;
+        }
+        return true;
+    }
+
+    /**
+     * Searches in the given {@code feature} for an associated feature type of the given name.
+     * This method does not search recursively in the associations of the associated features.
+     * Such recursive search will be performed by {@link #deepSearch(List, GenericName)} only
+     * if we do not find the desired feature in the most direct way.
+     *
+     * <p>Current implementation does not check that there is no duplicated names.
+     * See {@link #deepSearch(List, GenericName)} for a rational.</p>
+     *
+     * @param  feature The feature in which to search.
+     * @param  name The name of the feature to search.
+     * @param  deferred Where to store {@code FeatureType}s to be eventually used for a deep search.
+     * @return The feature of the given name, or {@code null} if none.
+     */
+    @SuppressWarnings("null")
+    private static DefaultFeatureType search(final DefaultFeatureType feature, final GenericName name,
+            final List<DefaultFeatureType> deferred)
+    {
+        /*
+         * Search only in associations declared in the given feature, not in inherited associations.
+         * The inherited associations will be checked in a separated loop below if we did not found
+         * the request feature type in explicitly declared associations.
+         */
+        for (final AbstractIdentifiedType property : feature.getProperties(false)) {
+            if (property instanceof DefaultAssociationRole) {
+                final FeatureType valueType;
+                valueType = ((DefaultAssociationRole) property).valueType;
+                if (valueType instanceof NamedFeatureType) {
+                    continue; // Skip unresolved feature types.
+                }
+                if (name.equals(valueType.getName())) {
+                    return (DefaultFeatureType) valueType;
+                }
+                deferred.add((DefaultFeatureType) valueType);
+            }
+        }
+        /*
+         * Search in inherited associations as a separated step, in order to include the overridden
+         * associations in the search. Overridden associations have the same association role name,
+         * but not necessarily the same feature type (may be a subtype). This is equivalent to
+         * "covariant return type" in the Java language.
+         */
+        for (DefaultFeatureType type : feature.getSuperTypes()) {
+            if (name.equals(type.getName())) {
+                return type;
+            }
+            type = search(type, name, deferred);
+            if (type != null) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Potentially invoked after {@link #search(FeatureType, GenericName, List)} for searching
+     * in associations of associations.
+     *
+     * <p>Current implementation does not check that there is no duplicated names. Even if we did so,
+     * a graph of feature types may have no duplicated names at this time but some duplicated names
+     * later. We rather put a warning in {@link #DefaultAssociationRole(Map, GenericName, int, int)}
+     * javadoc.</p>
+     *
+     * @param  feature The feature in which to search.
+     * @param  name The name of the feature to search.
+     * @param  done The feature types collected by {@link #search(FeatureType, GenericName, List)}.
+     * @return The feature of the given name, or {@code null} if none.
+     */
+    private static DefaultFeatureType deepSearch(final List<DefaultFeatureType> deferred, final GenericName name) {
+        final Map<FeatureType,Boolean> done = new IdentityHashMap<FeatureType,Boolean>(8);
+        for (int i=0; i<deferred.size();) {
+            DefaultFeatureType valueType = deferred.get(i++);
+            if (done.put(valueType, Boolean.TRUE) == null) {
+                deferred.subList(0, i).clear(); // Discard previous value for making more room.
+                valueType = search(valueType, name, deferred);
+                if (valueType != null) {
+                    return valueType;
+                }
+                i = 0;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the type of feature values.
      *
      * <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 The type of feature values.
+     * @throws IllegalStateException if the feature type has been specified
+     *         {@linkplain #DefaultAssociationRole(Map, GenericName, int, int) only by its name}
+     *         and not yet resolved.
      */
     public final DefaultFeatureType getValueType() {
-        return valueType;
+        /*
+         * This method shall be final for consistency with other methods in this classes
+         * which use the 'valueType' field directly. Furthermore, this method is invoked
+         * (indirectly) by DefaultFeatureType constructors.
+         */
+        final FeatureType type = valueType;
+        if (type instanceof NamedFeatureType) {
+            throw new IllegalStateException(Errors.format(Errors.Keys.UnresolvedFeatureName_1, getName()));
+        }
+        return (DefaultFeatureType) type;
+    }
+
+    /**
+     * Returns the name of the feature type. This information is always available
+     * even when the name has not yet been {@linkplain #resolve resolved}.
+     */
+    static GenericName getValueTypeName(final DefaultAssociationRole role) {
+        // Method is static for compatibility with branches on GeoAPI snapshots.
+        return role.valueType.getName();
     }
 
     /**
@@ -195,7 +388,12 @@ public class DefaultAssociationRole exte
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + valueType.hashCode();
+        /*
+         * Do not use the full 'valueType' object for computing hash code,
+         * because it may change before and after 'resolve' is invoked. In
+         * addition, this avoid infinite recursivity in case of cyclic graph.
+         */
+        return super.hashCode() + valueType.getName().hashCode();
     }
 
     /**

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -23,6 +23,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.IdentityHashMap;
 import java.util.Collection;
 import java.util.Collections;
 import java.io.IOException;
@@ -87,7 +88,7 @@ import org.apache.sis.internal.util.Unmo
  *
  * @see AbstractFeature
  */
-public class DefaultFeatureType extends AbstractIdentifiedType {
+public class DefaultFeatureType extends AbstractIdentifiedType implements FeatureType {
     /**
      * For cross-version compatibility.
      */
@@ -102,7 +103,7 @@ public class DefaultFeatureType extends 
 
     /**
      * {@code true} if this feature type contains only attributes constrained to the [1 … 1] cardinality,
-     * or operations.
+     * or operations. The feature type shall not contains associations.
      *
      * @see #isSimple()
      */
@@ -115,6 +116,23 @@ public class DefaultFeatureType extends 
     private transient boolean isSparse;
 
     /**
+     * {@code true} if we determined that this feature type does not have, directly or indirectly,
+     * any unresolved name (i.e. a {@link DefaultAssociationRole#valueType} specified only be the
+     * feature type name instead than its actual instance). A value of {@code true} means that all
+     * names have been resolved. However a value of {@code false} only means that we are not sure,
+     * and that {@link #resolve(FeatureType)} should check again.
+     *
+     * <div class="note"><b>Note:</b>
+     * Strictly speaking, this field should be declared {@code volatile} since the names could
+     * be resolved late after construction, after the {@code DefaultFeatureType} instance became
+     * used by different threads. However this is not the intended usage of deferred associations.
+     * Furthermore a wrong value ({@code false} when it should be {@code true}) should only cause
+     * more computation than needed, without changing the result.
+     * </div>
+     */
+    private transient boolean isResolved;
+
+    /**
      * The direct parents of this feature type, or an empty set if none.
      *
      * @see #getSuperTypes()
@@ -214,14 +232,24 @@ public class DefaultFeatureType extends 
         super(identification);
         ArgumentChecks.ensureNonNull("properties", properties);
         this.isAbstract = isAbstract;
-        this.superTypes = (superTypes == null) ? Collections.<DefaultFeatureType>emptySet() :
-                          CollectionsExt.<DefaultFeatureType>immutableSet(true, superTypes);
+        if (superTypes == null) {
+            this.superTypes = Collections.emptySet();
+        } else {
+            this.superTypes = CollectionsExt.immutableSet(true, superTypes);
+            for (final FeatureType type : this.superTypes) {
+                if (type instanceof NamedFeatureType) {
+                    // Hierarchy of feature types can not be cyclic.
+                    throw new IllegalArgumentException(Errors.format(Errors.Keys.UnresolvedFeatureName_1, type.getName()));
+                }
+            }
+        }
         switch (properties.length) {
             case 0:  this.properties = Collections.emptyList(); break;
             case 1:  this.properties = Collections.singletonList(properties[0]); break;
             default: this.properties = UnmodifiableArrayList.wrap(Arrays.copyOf(properties, properties.length, AbstractIdentifiedType[].class)); break;
         }
         computeTransientFields();
+        isResolved = resolve(this, null, isSimple);
     }
 
     /**
@@ -243,10 +271,11 @@ public class DefaultFeatureType extends 
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
         computeTransientFields();
+        isResolved = isSimple; // Conservative value. The 'resolve' method will compute a more accurate value if needed.
     }
 
     /**
-     * Computes all transient fields ({@link #assignableTo}, {@link #byName}, {@link #indices}, {@link #isSimple}).
+     * Computes transient fields ({@link #assignableTo}, {@link #byName}, {@link #indices}, {@link #isSimple}).
      *
      * <p>As a side effect, this method checks for missing or duplicated names.</p>
      *
@@ -257,9 +286,9 @@ public class DefaultFeatureType extends 
         byName       = new LinkedHashMap<String,AbstractIdentifiedType>(capacity);
         indices      = new LinkedHashMap<String,Integer>(capacity);
         assignableTo = new HashSet<GenericName>(4);
-        assignableTo.add(getName());
+        assignableTo.add(super.getName());
         scanPropertiesFrom(this);
-        byName        = compact(byName);
+        byName        = CollectionsExt.compact(byName);
         assignableTo  = CollectionsExt.unmodifiableOrCopy(assignableTo);
         allProperties = byName.values();
         if (byName instanceof HashMap<?,?>) {
@@ -294,7 +323,7 @@ public class DefaultFeatureType extends 
                 }
             }
         }
-        indices = compact(indices);
+        indices = CollectionsExt.compact(indices);
         /*
          * Rational for choosing whether the feature is sparse: By default, java.util.HashMap implementation creates
          * an internal array of length 16 (see HashMap.DEFAULT_INITIAL_CAPACITY).  In addition, the HashMap instance
@@ -309,25 +338,15 @@ public class DefaultFeatureType extends 
     }
 
     /**
-     * Returns a more compact representation of the given map. This method is similar to
-     * {@link CollectionsExt#unmodifiableOrCopy(Map)}, except that it does not wrap the
-     * map in an unmodifiable view. The intend is to avoid one level of indirection for
-     * performance and memory reasons (keeping in mind that we will have lot of features).
-     * This is okay if we guaranteed that the map does not escape outside this class.
-     */
-    private static <K,V> Map<K,V> compact(final Map<K,V> map) {
-        switch (map.size()) {
-            case 0:  return Collections.emptyMap();
-            case 1:  final Map.Entry<K,V> entry = map.entrySet().iterator().next();
-                     return Collections.singletonMap(entry.getKey(), entry.getValue());
-            default: return map;
-        }
-    }
-
-    /**
      * Fills the {@link #byName} map 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.
      * This method also performs an opportunist verification of argument validity.
+     *
+     * <p>{@code this} shall be the instance in process of being created, not any other instance
+     * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
+     *
+     * @param  source The feature from which to get properties.
+     * @throws IllegalArgumentException if two properties have the same name.
      */
     private void scanPropertiesFrom(final DefaultFeatureType source) {
         for (final DefaultFeatureType parent : source.getSuperTypes()) {
@@ -376,6 +395,8 @@ public class DefaultFeatureType extends 
      * 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.
      *
+     * <p>{@code this} shall be the instance in process of being created, not any other instance.</p>
+     *
      * @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.
@@ -397,6 +418,73 @@ public class DefaultFeatureType extends 
                 b.append("properties[").append(index).append("].name").toString()));
     }
 
+    /**
+     * If an associated feature type is a placeholder for a {@code FeatureType} to be defined later,
+     * replaces the placeholder by the actual instance if available. Otherwise do nothing.
+     *
+     * <p>This method is needed only in case of cyclic graph, e.g. feature <var>A</var> has an association
+     * to feature <var>B</var> which has an association back to <var>A</var>. It may also be <var>A</var>
+     * having an association to itself, <i>etc.</i></p>
+     *
+     * <p>{@code this} shall be the instance in process of being created, not other instance
+     * (i.e. recursive method invocations are performed on the same {@code this} instance).</p>
+     *
+     * @param  feature  The feature type for which to resolve the properties.
+     * @param  previous Previous results, for avoiding never ending loop.
+     * @return {@code true} if all names have been resolved.
+     */
+    private boolean resolve(final DefaultFeatureType feature, final Map<FeatureType,Boolean> previous) {
+        /*
+         * The isResolved field is used only as a cache for skipping completely the DefaultFeatureType instance if
+         * we have determined that there is no unresolved name.
+         */
+        return feature.isResolved = resolve(feature, previous, feature.isResolved);
+    }
+
+    /**
+     * Implementation of {@link #resolve(FeatureType, Map)}, also to be invoked from the constructor.
+     *
+     * @param  feature  The feature type for which to resolve the properties.
+     * @param  previous Previous results, for avoiding never ending loop. Initially {@code null}.
+     * @param  resolved {@code true} if we already know that all names are resolved.
+     * @return {@code true} if all names have been resolved.
+     */
+    private boolean resolve(final DefaultFeatureType feature, Map<FeatureType,Boolean> previous, boolean resolved) {
+        if (!resolved) {
+            resolved = true;
+            for (final DefaultFeatureType type : feature.getSuperTypes()) {
+                resolved &= resolve(type, previous);
+            }
+            for (final AbstractIdentifiedType property : feature.getProperties(false)) {
+                if (property instanceof DefaultAssociationRole) {
+                    if (!((DefaultAssociationRole) property).resolve(this)) {
+                        resolved = false;
+                        continue;
+                    }
+                    /*
+                     * Resolve recursively the associated features, with a check against infinite recursivity.
+                     * If we fall in a loop (for example A → B → C → A), conservatively returns 'false'. This
+                     * may not be the most accurate answer, but will not cause any more hurt than checking more
+                     * often than necessary.
+                     */
+                    final DefaultFeatureType valueType = ((DefaultAssociationRole) property).getValueType();
+                    if (valueType != this) {
+                        if (previous == null) {
+                            previous = new IdentityHashMap<FeatureType,Boolean>(8);
+                        }
+                        Boolean r = previous.put(valueType, Boolean.FALSE);
+                        if (r == null) {
+                            r = resolve(valueType, previous);
+                            previous.put(valueType, r);
+                        }
+                        resolved &= r;
+                    }
+                }
+            }
+        }
+        return resolved;
+    }
+
 
     // -------- END OF CONSTRUCTORS ------------------------------------------------------------------------------
 
@@ -421,7 +509,8 @@ public class DefaultFeatureType extends 
 
     /**
      * 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.
+     * or operations (no feature association).
+     * Such feature types can be handled as a {@linkplain org.apache.sis.util.iso.DefaultRecord records}.
      *
      * @return {@code true} if this feature type contains only simple attributes or operations.
      */
@@ -546,9 +635,15 @@ public class DefaultFeatureType extends 
      * The type of list elements will be changed to {@code FeatureType} if and when such interface
      * will be defined in GeoAPI.</div>
      *
+     * <div class="note"><b>Note for subclasses:</b>
+     * this method is final because it is invoked (indirectly) by constructors, and invoking a user-overrideable
+     * method at construction time is not recommended. Furthermore, many Apache SIS methods need guarantees about
+     * the stability of this collection.
+     * </div>
+     *
      * @return The parents of this feature type, or an empty set if none.
      */
-    public Set<DefaultFeatureType> getSuperTypes() {
+    public final Set<DefaultFeatureType> getSuperTypes() {
         return superTypes;
     }
 
@@ -562,12 +657,18 @@ public class DefaultFeatureType extends 
      * The type of list elements will be changed to {@code PropertyType} if and when such interface
      * will be defined in GeoAPI.</div>
      *
+     * <div class="note"><b>Note for subclasses:</b>
+     * this method is final because it is invoked (indirectly) by constructors, and invoking a user-overrideable
+     * method at construction time is not recommended. Furthermore, many Apache SIS methods need guarantees about
+     * the stability of this collection.
+     * </div>
+     *
      * @param  includeSuperTypes {@code true} for including the properties inherited from the super-types,
      *         or {@code false} for returning only the properties defined explicitely in this type.
      * @return Feature operation, attribute type and association role that carries characteristics of this
      *         feature type (not including parent types).
      */
-    public Collection<AbstractIdentifiedType> getProperties(final boolean includeSuperTypes) {
+    public final Collection<AbstractIdentifiedType> getProperties(final boolean includeSuperTypes) {
         return includeSuperTypes ? allProperties : properties;
     }
 

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -204,7 +204,7 @@ header: for (int i=0; ; i++) {
                 final DefaultAssociationRole pt = (DefaultAssociationRole) propertyType;
                 minimumOccurs = pt.getMinimumOccurs();
                 maximumOccurs = pt.getMaximumOccurs();
-                valueType     = toString(pt.getValueType().getName());
+                valueType     = toString(DefaultAssociationRole.getValueTypeName(pt));
                 valueClass    = AbstractFeature.class;
             } else if (propertyType instanceof DefaultOperation) {
                 final AbstractIdentifiedType resultType = ((DefaultOperation) propertyType).getResult();

Added: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java?rev=1634239&view=auto
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java (added)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.feature;
+
+import org.opengis.util.GenericName;
+
+
+/**
+ * Place-holder for an interface not available in GeoAPI 3.0.
+ * This place-holder will be removed after we upgrade to a later GeoAPI version.
+ *
+ * <p><strong>Do not put this type in public API</strong>. We need to prevent users from using
+ * this type in order to reduce compatibility breaks when we will upgrade the GeoAPI version.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+interface FeatureType {
+    GenericName getName();
+}

Propchange: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureType.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Copied: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java (from r1634173, sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java?p2=sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java&p1=sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java&r1=1634173&r2=1634239&rev=1634239&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -16,15 +16,8 @@
  */
 package org.apache.sis.feature;
 
-import java.util.Set;
-import java.util.Collection;
-import java.util.Collections;
 import java.io.Serializable;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.util.GenericName;
-import org.opengis.util.InternationalString;
-import org.apache.sis.util.resources.Errors;
 
 
 /**
@@ -63,59 +56,6 @@ final class NamedFeatureType implements 
         return name;
     }
 
-    /** Undefined. */ @Override public InternationalString getDefinition()  {return null;}
-    /** Undefined. */ @Override public InternationalString getDesignation() {return null;}
-    /** Undefined. */ @Override public InternationalString getDescription() {return null;}
-
-    /**
-     * Declares that this feature shall not be instantiated.
-     */
-    @Override
-    public boolean isAbstract() {
-        return true;
-    }
-
-    /**
-     * Conservatively assumes that the feature is not simple,
-     * since we do not know what the actual feature will be.
-     */
-    @Override
-    public boolean isSimple() {
-        return false;
-    }
-
-    /**
-     * Always throws {@link IllegalArgumentException} since this feature type has no declared property yet.
-     */
-    @Override
-    public PropertyType getProperty(final String name) throws IllegalArgumentException {
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyNotFound_2, getName(), name));
-    }
-
-    /**
-     * Returns an empty set since this feature has no declared property yet.
-     */
-    @Override
-    public Collection<? extends PropertyType> getProperties(final boolean includeSuperTypes) {
-        return Collections.emptySet();
-    }
-
-    /**
-     * Returns an empty set since this feature has no declared parent yet.
-     */
-    @Override
-    public Set<? extends FeatureType> getSuperTypes() {
-        return Collections.emptySet();
-    }
-
-    /**
-     * This feature type is considered to all other features except itself.
-     */
-    @Override
-    public boolean isAssignableFrom(final FeatureType type) {
-        return (type instanceof NamedFeatureType);
-    }
-
     /**
      * Returns a string representation of this feature type.
      */

Modified: sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -16,11 +16,17 @@
  */
 package org.apache.sis.feature;
 
+import java.util.Map;
+import org.opengis.util.GenericName;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static java.util.Collections.singletonMap;
+import static org.apache.sis.feature.DefaultAssociationRole.NAME_KEY;
+import static org.apache.sis.test.TestUtilities.getSingleton;
 import static org.apache.sis.test.Assert.*;
 
 
@@ -37,27 +43,57 @@ public final strictfp class DefaultAssoc
     /**
      * Creates an association to a twin town. We arbitrarily fix the maximum number
      * of occurrences to 1, even if in reality some cities have many twin towns.
+     *
+     * @param cyclic {@code true} if in addition to the association from <var>A</var> to <var>B</var>,
+     *        we also want an association from <var>B</var> to <var>A</var>, thus creating a cycle.
+     * @return The association to use for testing purpose.
      */
-    static DefaultAssociationRole twinTown() {
-        return new DefaultAssociationRole(singletonMap(DefaultAssociationRole.NAME_KEY, "twin town"),
-                DefaultFeatureTypeTest.city(), 0, 1);
+    static DefaultAssociationRole twinTown(final boolean cyclic) {
+        final Map<String,?> properties = singletonMap(NAME_KEY, "twin town");
+        if (cyclic) {
+            final GenericName valueType = DefaultFactories.SIS_NAMES.createTypeName(null, "Twin town");
+            return new DefaultAssociationRole(properties, valueType, 0, 1);
+        } else {
+            final DefaultFeatureType valueType = DefaultFeatureTypeTest.city();
+            return new DefaultAssociationRole(properties, valueType, 0, 1);
+        }
     }
 
     /**
      * Returns a City feature type which may have a twin town.
+     *
+     * @param cyclic {@code true} if in addition to the association from <var>A</var> to <var>B</var>,
+     *        we also want an association from <var>B</var> to <var>A</var>, thus creating a cycle.
+     * @return The association to use for testing purpose.
      */
-    static DefaultFeatureType twinTownCity() {
-        final DefaultAssociationRole twinTown = twinTown();
-        return new DefaultFeatureType(singletonMap(DefaultFeatureType.NAME_KEY, "Twin town"), false,
-                new DefaultFeatureType[] {twinTown.getValueType()}, twinTown);
+    static DefaultFeatureType twinTownCity(final boolean cyclic) {
+        final DefaultAssociationRole twinTown = twinTown(cyclic);
+        final DefaultFeatureType parent = cyclic ? DefaultFeatureTypeTest.city() : twinTown.getValueType();
+        return createType("Twin town", parent, twinTown);
+    }
+
+    /**
+     * Convenience method creating a feature type of the given name, parent and property.
+     *
+     * @param name     The name as either a {@link String} or a {@link GenericName}.
+     * @param parent   A feature type created by {@link DefaultFeatureTypeTest#city()}, or {@code null}.
+     * @param property The association to an other feature.
+     * @return The feature type to use for testing purpose.
+     */
+    private static DefaultFeatureType createType(final Object name,
+            final DefaultFeatureType parent, final DefaultAssociationRole... property)
+    {
+        return new DefaultFeatureType(singletonMap(NAME_KEY, name),
+                false, new DefaultFeatureType[] {parent}, property);
     }
 
     /**
      * Tests serialization of an {@link DefaultAssociationRole} instance.
+     * This will also indirectly tests {@link DefaultAssociationRole#equals(Object)}.
      */
     @Test
     public void testSerialization() {
-        assertSerializedEquals(twinTown());
+        assertSerializedEquals(twinTown(false));
     }
 
     /**
@@ -65,7 +101,7 @@ public final strictfp class DefaultAssoc
      */
     @Test
     public void testGetTitleProperty() {
-        final DefaultAssociationRole twinTown = twinTown();
+        final DefaultAssociationRole twinTown = twinTown(false);
         assertEquals("city", DefaultAssociationRole.getTitleProperty(twinTown));
     }
 
@@ -74,7 +110,91 @@ public final strictfp class DefaultAssoc
      */
     @Test
     public void testToString() {
-        final DefaultAssociationRole twinTown = twinTown();
+        final DefaultAssociationRole twinTown = twinTown(false);
         assertEquals("FeatureAssociationRole[“twin town” : City]", twinTown.toString());
     }
+
+    /**
+     * Tests a bidirectional association (a feature having an association to itself).
+     */
+    @Test
+    public void testBidirectionalAssociation() {
+        final DefaultFeatureType twinTown = twinTownCity(true);
+        final DefaultAssociationRole association = (DefaultAssociationRole) twinTown.getProperty("twin town");
+        assertSame("twinTown.property(“twin town”).valueType", twinTown, association.getValueType());
+        /*
+         * Creates a FeatureType copy containing the same properties. Used for verifying
+         * that 'DefaultFeatureType.equals(Object)' does not fall in an infinite loop.
+         */
+        final DefaultFeatureType copy = createType(twinTown.getName(),
+                getSingleton(twinTown.getSuperTypes()), association);
+
+        assertTrue("equals", copy.equals(twinTown));
+        assertTrue("equals", twinTown.equals(copy));
+        assertEquals("hashCode", copy.hashCode(), twinTown.hashCode());
+    }
+
+    /**
+     * Tests {@link DefaultFeatureType#isAssignableFrom(FeatureType)} and {@link DefaultFeatureType#equals(Object)}
+     * on a feature type having a bidirectional association to an other feature. This test will fall in an infinite
+     * loop if the implementation does not have proper guard against infinite recursivity.
+     */
+    @Test
+    @DependsOnMethod("testBidirectionalAssociation")
+    public void testCyclicAssociation() {
+        final GenericName nameOfA = DefaultFactories.SIS_NAMES.createTypeName(null, "A");
+        final GenericName nameOfB = DefaultFactories.SIS_NAMES.createTypeName(null, "B");
+        final GenericName nameOfC = DefaultFactories.SIS_NAMES.createTypeName(null, "C");
+        final GenericName nameOfD = DefaultFactories.SIS_NAMES.createTypeName(null, "D");
+        /*
+         * Associations defined only by the FeatureType name.
+         */
+        final DefaultAssociationRole toB = new DefaultAssociationRole(singletonMap(NAME_KEY, "toB"), nameOfB, 1, 1);
+        final DefaultAssociationRole toC = new DefaultAssociationRole(singletonMap(NAME_KEY, "toC"), nameOfC, 1, 1);
+        final DefaultAssociationRole toD = new DefaultAssociationRole(singletonMap(NAME_KEY, "toD"), nameOfD, 1, 1);
+        final DefaultFeatureType typeA = createType(nameOfA, null, toB);
+        final DefaultFeatureType typeB = createType(nameOfB, null, toC);
+        final DefaultFeatureType typeC = createType(nameOfC, null, toD);
+        /*
+         * Association defined with real FeatureType instance, except for an association to itself.
+         * Construction of this FeatureType shall cause the resolution of all above FeatureTypes.
+         */
+        final DefaultAssociationRole toAr = new DefaultAssociationRole(singletonMap(NAME_KEY, "toA"),         typeA, 1, 1);
+        final DefaultAssociationRole toBr = new DefaultAssociationRole(singletonMap(NAME_KEY, toB.getName()), typeB, 1, 1);
+        final DefaultAssociationRole toCr = new DefaultAssociationRole(singletonMap(NAME_KEY, toC.getName()), typeC, 1, 1);
+        final DefaultFeatureType typeD = createType(nameOfD, null, toAr, toBr, toCr, toD);
+        /*
+         * Verify the property given to the constructors. There is no reason for those properties
+         * to change as they are not the instances to be replaced by the name resolutions, but we
+         * verify them as a paranoiac check.
+         */
+        assertSame("A.properties", toB, getSingleton(typeA.getProperties(false)));
+        assertSame("B.properties", toC, getSingleton(typeB.getProperties(false)));
+        assertSame("C.properties", toD, getSingleton(typeC.getProperties(false)));
+        assertSame("D.properties", toAr, typeD.getProperty("toA"));
+        assertSame("D.properties", toBr, typeD.getProperty("toB"));
+        assertSame("D.properties", toCr, typeD.getProperty("toC"));
+        assertSame("D.properties", toD,  typeD.getProperty("toD"));
+        /*
+         * CORE OF THIS TEST: verify that the values of toB, toC and toD have been replaced by the actual
+         * FeatureType instances. Also verify that as a result, toB.equals(toBr) and toC.equals(toCr).
+         */
+        assertSame("toA", typeA, toAr.getValueType());
+        assertSame("toB", typeB, toBr.getValueType());
+        assertSame("toB", typeB, toB .getValueType());
+        assertSame("toC", typeC, toCr.getValueType());
+        assertSame("toC", typeC, toC .getValueType());
+        assertSame("toD", typeD, toD .getValueType());
+        assertEquals("toB", toB, toBr);
+        assertEquals("toC", toC, toCr);
+        /*
+         * Other equality tests, mostly for verifying that we do not fall in an infinite loop here.
+         */
+        assertFalse("equals", typeA.equals(typeD));
+        assertFalse("equals", typeD.equals(typeA));
+        assertFalse("equals", typeB.equals(typeC));
+        assertFalse("equals", typeC.equals(typeB));
+        assertFalse("hashCode", typeA.hashCode() == typeB.hashCode());
+        assertFalse("hashCode", typeC.hashCode() == typeD.hashCode());
+    }
 }

Modified: sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -63,7 +63,7 @@ public abstract strictfp class FeatureTe
      * Creates a feature for twin towns.
      */
     static AbstractFeature twinTown(final boolean isSparse) {
-        final DefaultFeatureType twinTown = DefaultAssociationRoleTest.twinTownCity();
+        final DefaultFeatureType twinTown = DefaultAssociationRoleTest.twinTownCity(false);
 
         final AbstractFeature leMans = isSparse ? new SparseFeature(twinTown) : new DenseFeature(twinTown);
         leMans.setPropertyValue("city", "Le Mans");

Modified: sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -47,7 +47,7 @@ public final strictfp class SingletonAss
         final AbstractFeature twinTown = DefaultFeatureTypeTest.city().newInstance();
         twinTown.setPropertyValue("city", "Le Mans");
         twinTown.setPropertyValue("population", 143240); // In 2011.
-        final AbstractAssociation association = new SingletonAssociation(DefaultAssociationRoleTest.twinTown());
+        final AbstractAssociation association = new SingletonAssociation(DefaultAssociationRoleTest.twinTown(false));
         association.setValue(twinTown);
         return association;
     }

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -171,9 +171,9 @@ public abstract class LegacyPropertyAdap
     /**
      * Emit a warning about extraneous ignored values.
      *
-     * @param  valueClass    The value class, used in case of warning only.
-     * @param  callerClass   The caller class, used in case of warning only.
-     * @param  callerMethod  The caller method, used in case of warning only.
+     * @param  valueClass    The value class (usually a GeoAPI interface).
+     * @param  callerClass   The caller class (usually an Apache SIS implementation of a GeoAPI interface).
+     * @param  callerMethod  The caller method (usually the name of a getter method).
      */
     public static void warnIgnoredExtraneous(final Class<?> valueClass,
             final Class<?> callerClass, final String callerMethod)

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -34,21 +34,11 @@ import static org.apache.sis.metadata.is
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.4
+ * @version 0.5
  * @module
  */
 public final class MetadataUtilities extends Static {
     /**
-     * The metadata standard name for ISO 19115-2.
-     */
-    public static final String STANDARD_NAME_2 = "ISO 19115-2 Geographic Information - Metadata Part 2 Extensions for imagery and gridded data";
-
-    /**
-     * The metadata standard version number for ISO 19115-2.
-     */
-    public static final String STANDARD_VERSION_2 = "ISO 19115-2:2009(E)";
-
-    /**
      * Do not allow instantiation of this class.
      */
     private MetadataUtilities() {

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -144,7 +144,7 @@ public abstract class AbstractMetadata i
      */
     @Override
     public boolean isEmpty() {
-        return Pruner.isEmpty(this, false);
+        return Pruner.isEmpty(this, true, false);
     }
 
     /**
@@ -155,7 +155,7 @@ public abstract class AbstractMetadata i
      * @throws UnmodifiableMetadataException If this metadata is not modifiable.
      */
     public void prune() {
-        Pruner.isEmpty(this, true);
+        Pruner.isEmpty(this, true, true);
     }
 
     /**

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -57,8 +57,14 @@ final class Pruner {
      * Returns the metadata properties. When used for pruning empty values, the map needs to
      * include empty (but non-null) values in order to allow us to set them to {@code null}.
      */
-    private static Map<String, Object> asMap(final MetadataStandard standard, final Object metadata, final boolean prune) {
-        return standard.asValueMap(metadata, KeyNamePolicy.JAVABEANS_PROPERTY, prune ? NON_NULL : NON_EMPTY);
+    private static Map<String, Object> asMap(final MetadataStandard standard, final Object metadata,
+            final boolean mandatory, final boolean prune)
+    {
+        final PropertyAccessor accessor = standard.getAccessor(metadata.getClass(), mandatory);
+        if (accessor != null) {
+            return new ValueMap(metadata, accessor, KeyNamePolicy.JAVABEANS_PROPERTY, prune ? NON_NULL : NON_EMPTY);
+        }
+        return null;
     }
 
     /**
@@ -78,12 +84,16 @@ final class Pruner {
      * It creates a map of visited nodes when the iteration begin, and deletes that map when the
      * iteration ends.</p>
      *
-     * @param  metadata The metadata object.
-     * @param  prune {@code true} for deleting empty entries.
+     * @param  metadata  The metadata object.
+     * @param  mandatory {@code true} if we shall throw an exception if {@code metadata} is not of the expected class.
+     * @param  prune     {@code true} for deleting empty entries.
      * @return {@code true} if all metadata properties are null or empty.
      */
-    static boolean isEmpty(final AbstractMetadata metadata, final boolean prune) {
-        final Map<String,Object> properties = asMap(metadata.getStandard(), metadata, prune);
+    static boolean isEmpty(final AbstractMetadata metadata, final boolean mandatory, final boolean prune) {
+        final Map<String,Object> properties = asMap(metadata.getStandard(), metadata, mandatory, prune);
+        if (properties == null) {
+            return false; // For metadata of unknown class, conservatively assume non-empty.
+        }
         final Map<Object,Boolean> tested = MAPS.get();
         if (!tested.isEmpty()) {
             return isEmpty(properties, tested, prune);
@@ -165,7 +175,7 @@ final class Pruner {
                         } else if (!(element instanceof Enum<?>) && !(element instanceof CodeList<?>)) {
                             final MetadataStandard standard = MetadataStandard.forClass(element.getClass());
                             if (standard != null) {
-                                isEmptyElement = isEmpty(asMap(standard, element, prune), tested, prune);
+                                isEmptyElement = isEmpty(asMap(standard, element, false, prune), tested, prune);
                                 if (!isEmptyElement && element instanceof Emptiable) {
                                     isEmptyElement = ((Emptiable) element).isEmpty();
                                 }

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -19,11 +19,9 @@ package org.apache.sis.metadata.iso;
 import java.util.Date;
 import java.util.Locale;
 import java.util.List;
-import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.ConcurrentModificationException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.Charset;
@@ -57,6 +55,7 @@ import org.opengis.metadata.spatial.Spat
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.util.InternationalString;
 import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.metadata.AbstractMetadata;
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.apache.sis.metadata.iso.citation.DefaultCitationDate;
 import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
@@ -262,8 +261,7 @@ public class DefaultMetadata extends ISO
      *
      * @param contact   Party responsible for the metadata information.
      * @param dateStamp Date that the metadata was created.
-     * @param identificationInfo Basic information about the resource
-     *        to which the metadata applies.
+     * @param identificationInfo Basic information about the resource to which the metadata applies.
      */
     public DefaultMetadata(final ResponsibleParty contact,
                            final Date             dateStamp,
@@ -409,9 +407,13 @@ public class DefaultMetadata extends ISO
     public final void setFileIdentifier(final String newValue) {
         DefaultIdentifier identifier = DefaultIdentifier.castOrCopy(getMetadataIdentifier());
         if (identifier == null) {
+            if (newValue == null) return;
             identifier = new DefaultIdentifier();
         }
         identifier.setCode(newValue);
+        if (newValue == null && (identifier instanceof AbstractMetadata) && ((AbstractMetadata) identifier).isEmpty()) {
+            identifier = null;
+        }
         setMetadataIdentifier(identifier);
     }
 
@@ -692,37 +694,6 @@ public class DefaultMetadata extends ISO
     }
 
     /**
-     * A specialization of {@link LegacyPropertyAdapter} which will try to merge the
-     * {@code "hierarchyLevel"} and {@code "hierarchyLevelName"} properties in the same
-     * {@link DefaultMetadataScope} instance.
-     */
-    private static abstract class ScopeAdapter<L> extends LegacyPropertyAdapter<L,DefaultMetadataScope> {
-        /**
-         * @param scopes Value of {@link DefaultMetadata#getMetadataScopes()}.
-         */
-        ScopeAdapter(final Collection<DefaultMetadataScope> scopes) {
-            super(scopes);
-        }
-
-        /**
-         * Invoked (indirectly) by JAXB when adding a new scope code or scope name. This implementation searches
-         * for an existing {@link MetadataScope} instance with a free slot for the new value before to create a
-         * new {@link DefaultMetadataScope} instance.
-         */
-        @Override
-        public boolean add(final L newValue) {
-            final Iterator<DefaultMetadataScope> it = elements.iterator();
-            if (it.hasNext()) {
-                DefaultMetadataScope scope = it.next();
-                if (unwrap(scope) == null) {
-                    return update(scope, newValue);
-                }
-            }
-            return super.add(newValue);
-        }
-    }
-
-    /**
      * Returns the scope to which the metadata applies.
      *
      * @return Scope to which the metadata applies.
@@ -734,10 +705,10 @@ public class DefaultMetadata extends ISO
     @Deprecated
     @XmlElement(name = "hierarchyLevel")
     public final Collection<ScopeCode> getHierarchyLevels() {
-        return new ScopeAdapter<ScopeCode>(getMetadataScopes()) {
+        return new MetadataScopeAdapter<ScopeCode>(getMetadataScopes()) {
             /** Stores a legacy value into the new kind of value. */
             @Override protected DefaultMetadataScope wrap(final ScopeCode value) {
-                return new DefaultMetadataScope(value);
+                return new DefaultMetadataScope(value, null);
             }
 
             /** Extracts the legacy value from the new kind of value. */
@@ -758,8 +729,8 @@ public class DefaultMetadata extends ISO
      *
      * @param newValues The new hierarchy levels.
      *
-     * @deprecated As of ISO 19115:2014, replaced by {@link #getMetadataScopes()}
-     *   followed by {@link DefaultMetadataScope#setResourceScope(ScopeCode)}.
+     * @deprecated As of ISO 19115:2014, replaced by {@link #setMetadataScopes(Collection)}
+     *   and {@link DefaultMetadataScope#setResourceScope(ScopeCode)}.
      */
     @Deprecated
     public final void setHierarchyLevels(final Collection<? extends ScopeCode> newValues) {
@@ -779,12 +750,10 @@ public class DefaultMetadata extends ISO
     @Deprecated
     @XmlElement(name = "hierarchyLevelName")
     public final Collection<String> getHierarchyLevelNames() {
-        return new ScopeAdapter<String>(getMetadataScopes()) {
+        return new MetadataScopeAdapter<String>(getMetadataScopes()) {
             /** Stores a legacy value into the new kind of value. */
             @Override protected DefaultMetadataScope wrap(final String value) {
-                final DefaultMetadataScope scope = new DefaultMetadataScope();
-                scope.setName(new SimpleInternationalString(value));
-                return scope;
+                return new DefaultMetadataScope(null, value);
             }
 
             /** Extracts the legacy value from the new kind of value. */
@@ -806,8 +775,8 @@ public class DefaultMetadata extends ISO
      *
      * @param newValues The new hierarchy level names.
      *
-     * @deprecated As of ISO 19115:2014, replaced by {@link #getMetadataScopes()}
-     *   followed by {@link DefaultMetadataScope#setName(InternationalString)}.
+     * @deprecated As of ISO 19115:2014, replaced by {@link #setMetadataScopes(Collection)}
+     *   and {@link DefaultMetadataScope#setName(InternationalString)}.
      */
     @Deprecated
     public final void setHierarchyLevelNames(final Collection<? extends String> newValues) {

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlElem
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.maintenance.ScopeCode;
+import org.apache.sis.util.iso.Types;
 
 // Branch-specific imports
 import org.opengis.annotation.UML;
@@ -87,9 +88,11 @@ public class DefaultMetadataScope extend
      * Constructs a metadata scope initialized to the given value.
      *
      * @param resourceScope code for the scope.
+     * @param name Description of the scope, or {@code null} if none.
      */
-    public DefaultMetadataScope(final ScopeCode resourceScope) {
+    public DefaultMetadataScope(final ScopeCode resourceScope, final CharSequence name) {
         this.resourceScope = resourceScope;
+        this.name = Types.toInternationalString(name);
     }
 
     /**

Copied: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java (from r1634173, sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java)
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java?p2=sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java&p1=sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java&r1=1634173&r2=1634239&rev=1634239&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -21,7 +21,6 @@ import java.util.List;
 import java.util.Iterator;
 import java.util.Collection;
 import java.util.ConcurrentModificationException;
-import org.opengis.metadata.MetadataScope;
 import org.apache.sis.internal.metadata.LegacyPropertyAdapter;
 
 
@@ -35,11 +34,11 @@ import org.apache.sis.internal.metadata.
  * @version 0.5
  * @module
  */
-abstract class MetadataScopeAdapter<L> extends LegacyPropertyAdapter<L,MetadataScope> {
+abstract class MetadataScopeAdapter<L> extends LegacyPropertyAdapter<L,DefaultMetadataScope> {
     /**
      * @param scopes Value of {@link DefaultMetadata#getMetadataScopes()}.
      */
-    MetadataScopeAdapter(final Collection<MetadataScope> scopes) {
+    MetadataScopeAdapter(final Collection<DefaultMetadataScope> scopes) {
         super(scopes);
     }
 
@@ -51,9 +50,9 @@ abstract class MetadataScopeAdapter<L> e
     @Override
     public boolean add(final L newValue) {
         int n = 0;
-        final Iterator<MetadataScope> it = elements.iterator();
+        final Iterator<DefaultMetadataScope> it = elements.iterator();
         while (it.hasNext()) {
-            MetadataScope scope = it.next();
+            DefaultMetadataScope scope = it.next();
             if (unwrap(scope) != null) {
                 n++;
                 continue;
@@ -64,16 +63,16 @@ abstract class MetadataScopeAdapter<L> e
              * But if the metadata is not modifiable, then we will need to clone it and replaces the element in
              * the collection.
              */
-            if (!(scope instanceof DefaultMetadataScope) || !((DefaultMetadataScope) scope).isModifiable()) {
+            if (!scope.isModifiable()) {
                 scope = new DefaultMetadataScope(scope);
                 if (elements instanceof List<?>) {
-                    ((List<MetadataScope>) elements).set(n, scope);
+                    ((List<DefaultMetadataScope>) elements).set(n, scope);
                 } else {
                     /*
                      * Not a list. Delete all the remaining parts, substitute the value
                      * and reinsert everything in the same order.
                      */
-                    final MetadataScope[] remaining = new MetadataScope[elements.size() - n];
+                    final DefaultMetadataScope[] remaining = new DefaultMetadataScope[elements.size() - n];
                     remaining[0] = scope;
                     n = 1;
                     it.remove();

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java?rev=1634239&r1=1634238&r2=1634239&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java [UTF-8] Sat Oct 25 18:11:34 2014
@@ -41,6 +41,10 @@ import org.apache.sis.util.CharSequences
  * @module
  */
 public final class Citations extends Static {
+    /*
+     * NOTE: other constants are defined in org.apache.sis.internal.metadata.Standards.
+     */
+
     /**
      * The <a href="http://www.iso.org/">International Organization for Standardization</a>.
      *
@@ -161,7 +165,7 @@ public final class Citations extends Sta
     /**
      * List of citations declared in this class.
      */
-    private static final Citation[] AUTHORITIES = {
+    private static final Citation[] CITATIONS = {
         ISO, OGC, OGP, SIS, ESRI, ORACLE, NETCDF, GEOTIFF, PROJ4, EPSG, ISBN, ISSN
     };
 
@@ -189,7 +193,7 @@ public final class Citations extends Sta
         if (title == null || ((title = CharSequences.trimWhitespaces(title)).isEmpty())) {
             return null;
         }
-        for (final Citation citation : AUTHORITIES) {
+        for (final Citation citation : CITATIONS) {
             if (titleMatches(citation, title)) {
                 return citation;
             }



Mime
View raw message