You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2014/10/24 19:28:30 UTC
svn commit: r1634099 [1/3] - in /sis/branches/JDK6: ./
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/sr...
Author: desruisseaux
Date: Fri Oct 24 17:28:28 2014
New Revision: 1634099
URL: http://svn.apache.org/r1634099
Log:
Merge from the JDK7 branch.
Added:
sis/branches/JDK6/core/sis-build-helper/src/test/
- copied from r1634098, sis/branches/JDK7/core/sis-build-helper/src/test/
sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
- copied unchanged from r1634098, sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Standards.java
- copied unchanged from r1634098, sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Standards.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
- copied unchanged from r1634098, sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/internal/
- copied from r1634098, sis/branches/JDK7/storage/sis-shapefile/src/main/java/org/apache/sis/internal/
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/Database.java
- copied, changed from r1634098, sis/branches/JDK7/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/Database.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/package-info.java
- copied unchanged from r1634098, sis/branches/JDK7/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/package-info.java
sis/branches/JDK6/storage/sis-shapefile/src/test/java/org/apache/sis/internal/
- copied from r1634098, sis/branches/JDK7/storage/sis-shapefile/src/test/java/org/apache/sis/internal/
Removed:
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/CmdLineDriver.java
sis/branches/JDK6/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/CmdLineDriverTest.java
Modified:
sis/branches/JDK6/ (props changed)
sis/branches/JDK6/application/sis-javafx/pom.xml
sis/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java
sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
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/DefaultAssociationRole.java
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/FeatureFormat.java
sis/branches/JDK6/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/FeatureTestCase.java
sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/code/CodeListMarshallingTest.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/PrunerTest.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/DefaultMetadataTest.java
sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/test/integration/DefaultMetadataTest.java
sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/test/integration/ReferencingInMetadataTest.java
sis/branches/JDK6/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/Version.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.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/jaxb/gmd/LanguageCodeTest.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/XMLComparator.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/mock/MetadataMock.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/xml/NilReasonTest.java
sis/branches/JDK6/ide-project/NetBeans/build.xml
sis/branches/JDK6/ide-project/NetBeans/nbproject/project.properties
sis/branches/JDK6/pom.xml
sis/branches/JDK6/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
sis/branches/JDK6/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
sis/branches/JDK6/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java
sis/branches/JDK6/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
sis/branches/JDK6/storage/sis-shapefile/pom.xml
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/jdbc/AbstractConnection.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/jdbc/AbstractResultSet.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/jdbc/AbstractStatement.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/jdbc/DBFDriver.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/CodePage.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/DataType.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/FieldDescriptor.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java
sis/branches/JDK6/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeTypeEnum.java
sis/branches/JDK6/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/ShapeFileTest.java
sis/branches/JDK6/storage/sis-shapefile/src/test/java/org/apache/sis/test/suite/ShapefileTestSuite.java
sis/branches/JDK6/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/XMLStoreTest.java
Propchange: sis/branches/JDK6/
------------------------------------------------------------------------------
Merged /sis/branches/JDK8:r1631331-1634096
Merged /sis/branches/JDK7:r1631333-1634098
Modified: sis/branches/JDK6/application/sis-javafx/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/application/sis-javafx/pom.xml?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/application/sis-javafx/pom.xml (original)
+++ sis/branches/JDK6/application/sis-javafx/pom.xml Fri Oct 24 17:28:28 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/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/util/resources/ResourceCompilerMojo.java [UTF-8] Fri Oct 24 17:28:28 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/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -223,7 +223,7 @@ public abstract class AbstractAssociatio
public String toString() {
final String pt = DefaultAssociationRole.getTitleProperty(role);
final Iterator<Feature> 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/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=1634099&r1=1634098&r2=1634099&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] Fri Oct 24 17:28:28 2014
@@ -203,10 +203,17 @@ 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.
*/
@Override
- public GenericName getName() {
+ public final GenericName getName() {
return name;
}
Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAssociationRole.java [UTF-8] Fri Oct 24 17:28:28 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.*;
@@ -65,7 +69,7 @@ public class DefaultAssociationRole exte
*
* @see #getValueType()
*/
- private final FeatureType 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.
@@ -77,7 +81,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:
*
@@ -125,13 +129,203 @@ 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<FeatureType> deferred = new ArrayList<FeatureType>();
+ 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 FeatureType search(final FeatureType feature, final GenericName name, final List<FeatureType> 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 PropertyType property : feature.getProperties(false)) {
+ if (property instanceof FeatureAssociationRole) {
+ final FeatureType valueType;
+ if (property instanceof DefaultAssociationRole) {
+ valueType = ((DefaultAssociationRole) property).valueType;
+ if (valueType instanceof NamedFeatureType) {
+ continue; // Skip unresolved feature types.
+ }
+ } else {
+ valueType = ((FeatureAssociationRole) property).getValueType();
+ }
+ if (name.equals(valueType.getName())) {
+ return valueType;
+ }
+ deferred.add(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 (FeatureType 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 FeatureType deepSearch(final List<FeatureType> deferred, final GenericName name) {
+ final Map<FeatureType,Boolean> done = new IdentityHashMap<FeatureType,Boolean>(8);
+ for (int i=0; i<deferred.size();) {
+ FeatureType 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.
*
* @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.
*/
@Override
public final FeatureType 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 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 FeatureAssociationRole role) {
+ return (role instanceof DefaultAssociationRole ? ((DefaultAssociationRole) role).valueType : role.getValueType()).getName();
}
/**
@@ -198,7 +392,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/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=1634099&r1=1634098&r2=1634099&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] Fri Oct 24 17:28:28 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;
@@ -105,7 +106,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()
*/
@@ -118,6 +119,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()
@@ -211,14 +229,24 @@ public class DefaultFeatureType extends
super(identification);
ArgumentChecks.ensureNonNull("properties", properties);
this.isAbstract = isAbstract;
- this.superTypes = (superTypes == null) ? Collections.<FeatureType>emptySet() :
- CollectionsExt.<FeatureType>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, PropertyType[].class)); break;
}
computeTransientFields();
+ isResolved = resolve(this, null, isSimple);
}
/**
@@ -240,10 +268,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>
*
@@ -254,9 +283,9 @@ public class DefaultFeatureType extends
byName = new LinkedHashMap<String,PropertyType>(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<?,?>) {
@@ -291,7 +320,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
@@ -306,25 +335,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 FeatureType source) {
for (final FeatureType parent : source.getSuperTypes()) {
@@ -369,6 +388,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.
@@ -390,6 +411,82 @@ 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 FeatureType 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. If the given argument is not a DefaultFeatureType
+ * instance, conservatively assumes 'isSimple'. It may cause more calculation than needed, but should not
+ * change the result.
+ */
+ if (feature instanceof DefaultFeatureType) {
+ final DefaultFeatureType dt = (DefaultFeatureType) feature;
+ return dt.isResolved = resolve(feature, previous, dt.isResolved);
+ } else {
+ return resolve(feature, previous, feature.isSimple());
+ }
+ }
+
+ /**
+ * Implementation of {@link #resolve(FeatureType, Map)}, also to be invoked from the constructor.
+ *
+ * @param feature The feature type for which to resolve the properties.
+ * @param previous Previous results, for avoiding never ending loop. Initially {@code null}.
+ * @param resolved {@code true} if we already know that all names are resolved.
+ * @return {@code true} if all names have been resolved.
+ */
+ private boolean resolve(final FeatureType feature, Map<FeatureType,Boolean> previous, boolean resolved) {
+ if (!resolved) {
+ resolved = true;
+ for (final FeatureType type : feature.getSuperTypes()) {
+ resolved &= resolve(type, previous);
+ }
+ for (final PropertyType property : feature.getProperties(false)) {
+ if (property instanceof FeatureAssociationRole) {
+ 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 FeatureType valueType = ((FeatureAssociationRole) 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 ------------------------------------------------------------------------------
@@ -415,7 +512,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.
*/
@@ -542,10 +640,16 @@ public class DefaultFeatureType extends
* if we compare {@code FeatureType} to {@link Class} in the Java language, then this method is equivalent
* to {@link Class#getSuperclass()} except that feature types allow multi-inheritance.</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.
*/
@Override
- public Set<FeatureType> getSuperTypes() {
+ public final Set<FeatureType> getSuperTypes() {
return superTypes;
}
@@ -555,13 +659,19 @@ public class DefaultFeatureType extends
* inherited from the {@linkplain #getSuperTypes() super-types} only if {@code includeSuperTypes}
* is {@code true}.
*
+ * <div class="note"><b>Note for subclasses:</b>
+ * this method is final because it is invoked (indirectly) by constructors, and invoking a user-overrideable
+ * method at construction time is not recommended. Furthermore, many Apache SIS methods need guarantees about
+ * the stability of this collection.
+ * </div>
+ *
* @param includeSuperTypes {@code true} for including the properties inherited from the super-types,
* or {@code false} for returning only the properties defined explicitely in this type.
* @return Feature operation, attribute type and association role that carries characteristics of this
* feature type (not including parent types).
*/
@Override
- public Collection<PropertyType> getProperties(final boolean includeSuperTypes) {
+ public final Collection<PropertyType> getProperties(final boolean includeSuperTypes) {
return includeSuperTypes ? allProperties : properties;
}
Modified: sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureFormat.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -220,7 +220,7 @@ header: for (int i=0; ; i++) {
final FeatureAssociationRole pt = (FeatureAssociationRole) propertyType;
minimumOccurs = pt.getMinimumOccurs();
maximumOccurs = pt.getMaximumOccurs();
- valueType = toString(pt.getValueType().getName());
+ valueType = toString(DefaultAssociationRole.getValueTypeName(pt));
valueClass = Feature.class;
} else if (propertyType instanceof Operation) {
final IdentifiedType resultType = ((Operation) propertyType).getResult();
Modified: sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultAssociationRoleTest.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -16,11 +16,18 @@
*/
package org.apache.sis.feature;
+import java.util.Map;
+import org.opengis.feature.FeatureAssociationRole;
+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.*;
// Branch-dependent imports
@@ -40,27 +47,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 FeatureType[] {twinTown.getValueType()}, twinTown);
+ static DefaultFeatureType twinTownCity(final boolean cyclic) {
+ final DefaultAssociationRole twinTown = twinTown(cyclic);
+ final FeatureType 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 FeatureType parent, final FeatureAssociationRole... property)
+ {
+ return new DefaultFeatureType(singletonMap(NAME_KEY, name),
+ false, new FeatureType[] {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));
}
/**
@@ -68,7 +105,7 @@ public final strictfp class DefaultAssoc
*/
@Test
public void testGetTitleProperty() {
- final DefaultAssociationRole twinTown = twinTown();
+ final DefaultAssociationRole twinTown = twinTown(false);
assertEquals("city", DefaultAssociationRole.getTitleProperty(twinTown));
}
@@ -77,7 +114,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 FeatureAssociationRole association = (FeatureAssociationRole) 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/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/FeatureTestCase.java [UTF-8] Fri Oct 24 17:28:28 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/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAssociationTest.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -51,7 +51,7 @@ public final strictfp class SingletonAss
final Feature 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/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/LegacyPropertyAdapter.java [UTF-8] Fri Oct 24 17:28:28 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/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] Fri Oct 24 17:28:28 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/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] Fri Oct 24 17:28:28 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/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java [UTF-8] Fri Oct 24 17:28:28 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 Enumerated)) {
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/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -260,8 +260,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 Responsibility contact,
final Date dateStamp,
@@ -542,7 +541,7 @@ public class DefaultMetadata extends ISO
@Override
@Deprecated
@XmlElement(name = "characterSet")
- public final Charset getCharacterSet() {
+ public final Charset getCharacterSet() {
return LegacyPropertyAdapter.getSingleton(characterSets, Charset.class, null, DefaultMetadata.class, "getCharacterSet");
}
@@ -646,40 +645,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,MetadataScope> {
- /**
- * @param scopes Value of {@link DefaultMetadata#getMetadataScopes()}.
- */
- ScopeAdapter(final Collection<MetadataScope> 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<MetadataScope> it = elements.iterator();
- if (it.hasNext()) {
- MetadataScope scope = it.next();
- if (unwrap(scope) == null) {
- if (!(scope instanceof DefaultMetadataScope)) {
- scope = new DefaultMetadataScope(scope);
- }
- return update(scope, newValue);
- }
- }
- return super.add(newValue);
- }
- }
-
- /**
* Returns the scope to which the metadata applies.
*
* @return Scope to which the metadata applies.
@@ -691,10 +656,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 MetadataScope wrap(final ScopeCode value) {
- return new DefaultMetadataScope(value);
+ return new DefaultMetadataScope(value, null);
}
/** Extracts the legacy value from the new kind of value. */
@@ -718,8 +683,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) {
@@ -739,12 +704,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 MetadataScope 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. */
@@ -756,7 +719,7 @@ public class DefaultMetadata extends ISO
/** Updates the legacy value in an existing instance of the new kind of value. */
@Override protected boolean update(final MetadataScope container, final String value) {
if (container instanceof DefaultMetadataScope) {
- ((DefaultMetadataScope) container).setName(new SimpleInternationalString(value));
+ ((DefaultMetadataScope) container).setName(value != null ? new SimpleInternationalString(value) : null);
return true;
}
return false;
@@ -769,8 +732,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/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadataScope.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -20,6 +20,7 @@ import javax.xml.bind.annotation.XmlType
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.opengis.util.InternationalString;
+import org.apache.sis.util.iso.Types;
import org.opengis.metadata.MetadataScope;
import org.opengis.metadata.maintenance.ScopeCode;
@@ -73,9 +74,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);
}
/**
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java [UTF-8] Fri Oct 24 17:28:28 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;
}
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -31,6 +31,7 @@ import org.opengis.metadata.citation.Res
import org.opengis.metadata.citation.Role;
import org.opengis.util.InternationalString;
import org.apache.sis.util.iso.Types;
+import org.apache.sis.internal.metadata.LegacyPropertyAdapter;
/**
@@ -116,26 +117,60 @@ public class DefaultResponsibleParty ext
}
/**
- * Returns the name of the first party of the given type, or {@code null} if none.
+ * Returns the name or the position of the first individual. If no individual is found in the list of parties,
+ * then this method will search in the list of organization members. The later structure is used by our NetCDF
+ * reader.
+ *
+ * @param position {@code true} for returning the position name instead than individual name.
+ * @return The name or position of the first individual, or {@code null}.
+ *
+ * @see #getIndividualName()
+ * @see #getPositionName()
*/
- private InternationalString getName(final Class<? extends Party> type, final boolean position) {
+ private InternationalString getIndividual(final boolean position) {
final Collection<Party> parties = getParties();
+ InternationalString name = getName(parties, Individual.class, position);
+ if (name == null && parties != null) {
+ for (final Party party : parties) {
+ if (party instanceof Organisation) {
+ name = getName(((Organisation) party).getIndividual(), Individual.class, position);
+ if (name != null) {
+ break;
+ }
+ }
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Returns the name of the first party of the given type, or {@code null} if none.
+ *
+ * @param position {@code true} for returning the position name instead than individual name.
+ * @return The name or position of the first individual, or {@code null}.
+ *
+ * @see #getOrganisationName()
+ * @see #getIndividualName()
+ * @see #getPositionName()
+ */
+ private static InternationalString getName(final Collection<? extends Party> parties,
+ final Class<? extends Party> type, final boolean position)
+ {
+ InternationalString name = null;
if (parties != null) { // May be null on marshalling.
for (final Party party : parties) {
if (type.isInstance(party)) {
- final InternationalString name;
- if (position) {
- name = ((Individual) party).getPositionName();
- } else {
- name = party.getName();
- }
if (name != null) {
- return name;
+ LegacyPropertyAdapter.warnIgnoredExtraneous(type, DefaultResponsibleParty.class,
+ position ? "getPositionName" : (type == Individual.class)
+ ? "getIndividualName" : "getOrganisationName");
+ break;
}
+ name = position ? ((Individual) party).getPositionName() : party.getName();
}
}
}
- return null;
+ return name;
}
/**
@@ -168,8 +203,9 @@ public class DefaultResponsibleParty ext
* Only one of {@code individualName}, {@link #getOrganisationName() organisationName}
* and {@link #getPositionName() positionName} shall be provided.
*
- * <p>This implementation returns the first non-null name of an {@link Individual}
- * in the collection of {@linkplain #getParties() parties}.</p>
+ * <p>This implementation returns the name of the first {@link Individual} found in the collection of
+ * {@linkplain #getParties() parties}. If no individual is found in the parties, then this method fallbacks
+ * on the first {@linkplain Organisation#getIndividual() organisation member}.</p>
*
* @return Name, surname, given name and title of the responsible person, or {@code null}.
*
@@ -179,7 +215,7 @@ public class DefaultResponsibleParty ext
@Deprecated
@XmlElement(name = "individualName")
public String getIndividualName() {
- final InternationalString name = getName(Individual.class, false);
+ final InternationalString name = getIndividual(false);
return (name != null) ? name.toString() : null;
}
@@ -207,8 +243,8 @@ public class DefaultResponsibleParty ext
* {@link #getIndividualName() individualName}, {@code organisationName}
* and {@link #getPositionName() positionName} shall be provided.
*
- * <p>This implementation returns the first non-null name of an {@link Organisation}
- * in the collection of {@linkplain #getParties() parties}.</p>
+ * <p>This implementation returns the name of the first {@link Organisation}
+ * found in the collection of {@linkplain #getParties() parties}.</p>
*
* @return Name of the responsible organization, or {@code null}.
*
@@ -218,7 +254,7 @@ public class DefaultResponsibleParty ext
@Deprecated
@XmlElement(name = "organisationName")
public InternationalString getOrganisationName() {
- return getName(Organisation.class, false);
+ return getName(getParties(), Organisation.class, false);
}
/**
@@ -245,8 +281,9 @@ public class DefaultResponsibleParty ext
* {@link #getIndividualName() individualName}, {@link #getOrganisationName() organisationName}
* and {@code positionName} shall be provided.
*
- * <p>This implementation returns the first non-null position name of an {@link Individual}
- * in the collection of {@linkplain #getParties() parties}.</p>
+ * <p>This implementation returns the position of the first {@link Individual} found in the collection of
+ * {@linkplain #getParties() parties}. If no individual is found in the parties, then this method fallbacks
+ * on the first {@linkplain Organisation#getIndividual() organisation member}.</p>
*
* @return Role or position of the responsible person, or {@code null}
*
@@ -256,7 +293,7 @@ public class DefaultResponsibleParty ext
@Deprecated
@XmlElement(name = "positionName")
public InternationalString getPositionName() {
- return getName(DefaultIndividual.class, true);
+ return getIndividual(true);
}
/**
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -249,7 +249,7 @@ public class DefaultCoverageDescription
/**
* Sets the type of information represented by the cell value.
- * This method stores the value in the {@linkplain #getAttributeGroups() attribute groups}.
+ * This method stores the value in the first writable {@linkplain #getAttributeGroups() attribute groups}.
*
* @param newValue The new content type.
*
@@ -273,7 +273,7 @@ public class DefaultCoverageDescription
/**
* Returns the information on the dimensions of the cell measurement value.
- * This method fetches the values from the {@linkplain #getAttributeGroups() attribute groups}.
+ * This method fetches the values from the first {@linkplain #getAttributeGroups() attribute groups}.
*
* @return Dimensions of the cell measurement value.
*
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java?rev=1634099&r1=1634098&r2=1634099&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java [UTF-8] Fri Oct 24 17:28:28 2014
@@ -298,11 +298,11 @@ public class DefaultScopeDescription ext
* “<cite>Administrative area A — Road network</cite>” description.
* </div>
*
- * @return Feature types to which the information applies.
- *
* {@section Conditions}
* This method returns a modifiable collection only if no other property is set.
* Otherwise, this method returns an unmodifiable empty collection.
+ *
+ * @return Feature types to which the information applies.
*/
@Override
public Set<CharSequence> getFeatures() {
@@ -331,11 +331,11 @@ public class DefaultScopeDescription ext
* “<cite>Administrative area A — Overhead clearance</cite>” description.
* </div>
*
- * @return Attribute types to which the information applies.
- *
* {@section Conditions}
* This method returns a modifiable collection only if no other property is set.
* Otherwise, this method returns an unmodifiable empty collection.
+ *
+ * @return Attribute types to which the information applies.
*/
@Override
public Set<CharSequence> getAttributes() {
@@ -364,11 +364,11 @@ public class DefaultScopeDescription ext
* “<cite>Administrative area A — New bridge</cite>” description.
* </div>
*
- * @return Feature instances to which the information applies.
- *
* {@section Conditions}
* This method returns a modifiable collection only if no other property is set.
* Otherwise, this method returns an unmodifiable empty collection.
+ *
+ * @return Feature instances to which the information applies.
*/
@Override
public Set<CharSequence> getFeatureInstances() {
@@ -397,11 +397,11 @@ public class DefaultScopeDescription ext
* “<cite>Administrative area A — New bridge — Overhead clearance</cite>” description.
* </div>
*
- * @return Attribute instances to which the information applies.
- *
* {@section Conditions}
* This method returns a modifiable collection only if no other property is set.
* Otherwise, this method returns an unmodifiable empty collection.
+ *
+ * @return Attribute instances to which the information applies.
*/
@Override
public Set<CharSequence> getAttributeInstances() {