sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1519087 [1/2] - in /sis/trunk: ./ application/sis-console/ application/sis-console/src/main/java/org/apache/sis/console/ application/sis-console/src/test/java/org/apache/sis/console/ application/sis-console/src/test/java/org/apache/sis/tes...
Date Fri, 30 Aug 2013 21:00:41 GMT
Author: desruisseaux
Date: Fri Aug 30 21:00:40 2013
New Revision: 1519087

URL: http://svn.apache.org/r1519087
Log:
Merge from the JDK6 branch.

Added:
    sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/MimeTypeSCTest.java
      - copied unchanged from r1519084, sis/branches/JDK6/application/sis-console/src/test/java/org/apache/sis/console/MimeTypeSCTest.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/PropertiesConverter.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/PropertiesConverter.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractReferenceSystemTest.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractReferenceSystemTest.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java
      - copied unchanged from r1519084, sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java
Removed:
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Utilities.java
Modified:
    sis/trunk/   (props changed)
    sis/trunk/application/sis-console/pom.xml
    sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/AboutSC.java
    sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/Command.java
    sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/MimeTypeSC.java
    sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/package-info.java
    sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/AboutSCTest.java
    sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/HelpSCTest.java
    sis/trunk/application/sis-console/src/test/java/org/apache/sis/test/suite/ConsoleTestSuite.java
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/maven/Packer.java
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/ImmutableIdentifierTest.java
    sis/trunk/core/sis-referencing/pom.xml
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.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/math/FunctionProperty.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Locales.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultInternationalString.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.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/internal/converter/FallbackConverterTest.java
    sis/trunk/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypesTest.java
    sis/trunk/pom.xml
    sis/trunk/storage/sis-netcdf/pom.xml
    sis/trunk/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/IOUtilities.java
    sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/IOUtilitiesTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK7:r1517893-1519081
  Merged /sis/branches/JDK6:r1517896-1519084

Modified: sis/trunk/application/sis-console/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/pom.xml?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/pom.xml (original)
+++ sis/trunk/application/sis-console/pom.xml Fri Aug 30 21:00:40 2013
@@ -111,6 +111,13 @@ Console application.
       <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.sis.core</groupId>
+      <artifactId>sis-metadata</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/AboutSC.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/AboutSC.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/AboutSC.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/AboutSC.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -17,9 +17,20 @@
 package org.apache.sis.console;
 
 import java.util.EnumSet;
+import java.io.IOException;
+import java.rmi.registry.Registry;
+import javax.management.JMX;
+import javax.management.ObjectName;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
 import org.apache.sis.setup.About;
 import org.apache.sis.util.Version;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.internal.system.Supervisor;
+import org.apache.sis.internal.system.SupervisorMBean;
 
 
 /**
@@ -32,9 +43,14 @@ import org.apache.sis.util.resources.Voc
  *   <li>{@code --verbose}: prints all information including the libraries.</li>
  * </ul>
  *
+ * {@section About SIS installation on a remote machine}
+ * This sub-command can provide information about SIS installation on a remote machine,
+ * provided that remote access has been enabled at the Java Virtual Machine startup time.
+ * See {@link org.apache.sis.console} package javadoc for more information.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 final class AboutSC extends SubCommand {
@@ -50,23 +66,70 @@ final class AboutSC extends SubCommand {
      * Prints the information to the output stream.
      */
     @Override
-    public int run() {
-        if (hasUnexpectedFileCount(0, 0)) {
+    public int run() throws Exception {
+        /*
+         * Check the number of arguments, which can be 0 or 1. If present,
+         * the argument is the name and port number of a remote machine.
+         *
+         * In the current implementation, the --brief option is supported only on the local machine.
+         */
+        final boolean brief = options.containsKey(Option.BRIEF);
+        if (hasUnexpectedFileCount(0, brief ? 0 : 1)) {
             return Command.INVALID_ARGUMENT_EXIT_CODE;
         }
         final String configuration;
-        if (options.containsKey(Option.BRIEF)) {
+        if (brief) {
             configuration = Vocabulary.getResources(locale).getString(
                     Vocabulary.Keys.Version_2, "Apache SIS", Version.SIS);
         } else {
-            final EnumSet<About> sections = EnumSet.allOf(About.class);
-            if (!options.containsKey(Option.VERBOSE)) {
-                sections.remove(About.LIBRARIES);
+            if (files.isEmpty()) {
+                /*
+                 * Provide information about the local SIS installation.
+                 */
+                final EnumSet<About> sections = EnumSet.allOf(About.class);
+                if (!options.containsKey(Option.VERBOSE)) {
+                    sections.remove(About.LIBRARIES);
+                }
+                configuration = About.configuration(sections, locale, timezone).toString();
+            } else {
+                /*
+                 * Provide information about a remote SIS installation. Those information are accessible
+                 * only if explicitely enabled at JVM startup time.
+                 *
+                 * Tutorial: http://docs.oracle.com/javase/tutorial/jmx/remote/custom.html
+                 */
+                final String path = toRemoveURL(files.get(0));
+                try {
+                    final JMXServiceURL url = new JMXServiceURL(path);
+                    final JMXConnector jmxc = JMXConnectorFactory.connect(url);
+                    try {
+                        final MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
+                        final SupervisorMBean bean = JMX.newMBeanProxy(mbsc, new ObjectName(Supervisor.NAME), SupervisorMBean.class);
+                        configuration = bean.configuration().toString();
+                    } finally {
+                        jmxc.close();
+                    }
+                } catch (IOException e) {
+                    error(Errors.format(Errors.Keys.CanNotConnectTo_1, path), e);
+                    return Command.IO_EXCEPTION_EXIT_CODE;
+                }
             }
-            configuration = About.configuration(sections, locale, timezone).toString();
         }
         out.println(configuration);
         out.flush();
         return 0;
     }
+
+    /**
+     * Creates a {@code "service:jmx:rmi:///jndi/rmi://host:port/jmxrmi"} URL for the given host name.
+     * The host name can optionally be followed by a port number.
+     */
+    static String toRemoveURL(final String host) {
+        final StringBuilder buffer = new StringBuilder(60).append("service:jmx:rmi:///jndi/rmi://")
+                .append(host, host.regionMatches(true, 0, "localhost", 0, 9) ? 9 : 0, host.length());
+        if (host.lastIndexOf(':') < 0) {
+            buffer.append(':').append(Registry.REGISTRY_PORT);
+        }
+        return buffer.append("/jmxrmi").toString();
+    }
 }

Modified: sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/Command.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/Command.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/Command.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/Command.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -56,9 +56,13 @@ import org.apache.sis.util.logging.Monol
  * to the {@linkplain System#err standard error stream}. The reason is that command output may be targeted to
  * a client, while the error messages are usually for the operator.
  *
+ * {@section SIS installation on remote machines}
+ * Some sub-commands can operate on SIS installation on remote machines, provided that remote access has been enabled
+ * at the Java Virtual Machine startup time. See {@link org.apache.sis.console} package javadoc for more information.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final class Command {

Modified: sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/MimeTypeSC.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/MimeTypeSC.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/MimeTypeSC.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/MimeTypeSC.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -23,10 +23,17 @@ import java.net.URISyntaxException;
 import org.apache.sis.storage.DataStores;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.CharSequences;
 
 
 /**
  * The "mime-type" subcommand.
+ * This sub-command reproduces the functionality of the following Unix command,
+ * except that {@code MimeTypeSC} uses the SIS detection mechanism instead than the OS one.
+ *
+ * {@preformat shell
+ *   file --mime-type <files>
+ * }
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
@@ -51,28 +58,50 @@ final class MimeTypeSC extends SubComman
      */
     @Override
     public int run() throws InvalidOptionException, IOException, DataStoreException, URISyntaxException {
-        if (hasUnexpectedFileCount(1, 1)) {
+        if (hasUnexpectedFileCount(1, Integer.MAX_VALUE)) {
             return Command.INVALID_ARGUMENT_EXIT_CODE;
         }
-        final String file = files.get(0);
-        final URI uri;
-        try {
-            uri = new URI(file);
-        } catch (URISyntaxException e) {
-            canNotOpen(0, e);
-            return Command.IO_EXCEPTION_EXIT_CODE;
-        }
-        String type;
-        if (!uri.isAbsolute()) {
-            // If the URI is not absolute, we will not be able to convert to Path.
-            // Open as a String, leaving the conversion to DataStore implementations.
-            type = DataStores.probeContentType(file);
-        } else {
-            type = DataStores.probeContentType(uri);
+        /*
+         * Computes the width of the first column, which will contain file names.
+         */
+        int width = 0;
+        for (final String file : files) {
+            final int length = file.length() + 1;
+            if (length > width) {
+                width = length;
+            }
         }
-        if (type != null) {
-            out.println(type);
-            out.flush();
+        /*
+         * Now detect and print MIME type.
+         */
+        for (final String file : files) {
+            final URI uri;
+            try {
+                uri = new URI(file);
+            } catch (URISyntaxException e) {
+                canNotOpen(0, e);
+                return Command.IO_EXCEPTION_EXIT_CODE;
+            }
+            String type;
+            if (!uri.isAbsolute()) {
+                // If the URI is not absolute, we will not be able to convert to Path.
+                // Open as a String, leaving the conversion to DataStore implementations.
+                type = DataStores.probeContentType(file);
+            } else {
+                type = DataStores.probeContentType(uri);
+            }
+            /*
+             * Output of Unix "file --mime-type" Unix command is of the form:
+             *
+             *   file: type
+             */
+            if (type != null) {
+                out.print(file);
+                out.print(':');
+                out.print(CharSequences.spaces(width - file.length()));
+                out.println(type);
+                out.flush();
+            }
         }
         return 0;
     }

Modified: sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/package-info.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/package-info.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/main/java/org/apache/sis/console/package-info.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -17,10 +17,37 @@
 
 /**
  * Command line interface for Apache SIS.
+ * See {@link org.apache.sis.console.Command} for the list of supported commands.
+ *
+ * {@section SIS installation on remote machines}
+ * Some sub-commands can operate on SIS installation on remote machines, provided
+ * that the remote Java Virtual Machine has been started with the following options:
+ *
+ * {@preformat shell
+ *   java -Dcom.sun.management.jmxremote.port=1099 \
+ *        -Dcom.sun.management.jmxremote.authenticate=false \
+ *        -Dcom.sun.management.jmxremote.ssl=false \
+ *        -Dcom.sun.management.jmxremote.local.only=true \
+ *        <other options>
+ * }
+ *
+ * If the port number is different than {@value java.rmi.registry.Registry#REGISTRY_PORT}, then it must be specified
+ * to the {@code sis} subcommand after the host name. For example if the port number has been set to 9999, then the
+ * {@code about} sub-command shall be invoked as below:
+ *
+ * {@preformat shell
+ *   java org.apache.sis.console.Command about localhost:9999
+ * }
+ *
+ * The {@code com.sun.management.jmxremote.local.only} property is recommended if the remote JVM is an other
+ * JVM instance running on the local machine. Otherwise this property can be omitted for debugging purpose.
+ * For production environment, see the security settings documented on the
+ * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/management/agent.html">Monitoring
+ * and Management Using JMX Technology</a> page.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 package org.apache.sis.console;

Modified: sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/AboutSCTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/AboutSCTest.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/AboutSCTest.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/AboutSCTest.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -31,7 +31,7 @@ import static org.apache.sis.test.TestUt
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 @DependsOn(SubCommandTest.class)
@@ -39,10 +39,10 @@ public final strictfp class AboutSCTest 
     /**
      * Tests the sub-command without option.
      *
-     * @throws InvalidOptionException Should never happen.
+     * @throws Exception Should never happen.
      */
     @Test
-    public void testDefault() throws InvalidOptionException {
+    public void testDefault() throws Exception {
         final AboutSC test = new AboutSC(0, SubCommand.TEST);
         test.run();
         verify(test.outputBuffer.toString());
@@ -68,10 +68,10 @@ public final strictfp class AboutSCTest 
     /**
      * Tests the sub-command with the {@code --brief} option.
      *
-     * @throws InvalidOptionException Should never happen.
+     * @throws Exception Should never happen.
      */
     @Test
-    public void testBrief() throws InvalidOptionException {
+    public void testBrief() throws Exception {
         final AboutSC test = new AboutSC(0, SubCommand.TEST, "--brief");
         test.run();
         final String result = getSingleton(CharSequences.splitOnEOL(test.outputBuffer.toString().trim())).toString();
@@ -81,10 +81,10 @@ public final strictfp class AboutSCTest 
     /**
      * Tests the sub-command with the {@code --verbose} option.
      *
-     * @throws InvalidOptionException Should never happen.
+     * @throws Exception Should never happen.
      */
     @Test
-    public void testVerbose() throws InvalidOptionException {
+    public void testVerbose() throws Exception {
         final AboutSC test = new AboutSC(0, SubCommand.TEST, "--verbose");
         test.run();
         final String result = test.outputBuffer.toString();
@@ -93,4 +93,17 @@ public final strictfp class AboutSCTest 
         // Check for a dependency which should be present.
         assertTrue("geoapi", result.contains("geoapi"));
     }
+
+    /**
+     * Tests the {@link AboutSC#toRemoveURL(String)} method.
+     */
+    @Test
+    public void testToRemoveURL() {
+        assertEquals("service:jmx:rmi:///jndi/rmi://myhost:9999/jmxrmi",    AboutSC.toRemoveURL("myhost:9999"));
+        assertEquals("service:jmx:rmi:///jndi/rmi://myhost:1099/jmxrmi",    AboutSC.toRemoveURL("myhost"));
+        assertEquals("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi",          AboutSC.toRemoveURL("localhost:9999"));
+        assertEquals("service:jmx:rmi:///jndi/rmi://:1099/jmxrmi",          AboutSC.toRemoveURL("localhost"));
+        assertEquals("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi",          AboutSC.toRemoveURL(":9999"));
+        assertEquals("service:jmx:rmi:///jndi/rmi://localhosx:1099/jmxrmi", AboutSC.toRemoveURL("localhosx"));
+    }
 }

Modified: sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/HelpSCTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/HelpSCTest.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/HelpSCTest.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/test/java/org/apache/sis/console/HelpSCTest.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -54,7 +54,7 @@ public final strictfp class HelpSCTest e
 
     /**
      * Tests the sub-command with the {@code --help} option.
-     * Shall contains only a subset of {@link #testDefault()}.
+     * Shall contain only a subset of {@link #testDefault()}.
      *
      * @throws InvalidOptionException Should never happen.
      */

Modified: sis/trunk/application/sis-console/src/test/java/org/apache/sis/test/suite/ConsoleTestSuite.java
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-console/src/test/java/org/apache/sis/test/suite/ConsoleTestSuite.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/application/sis-console/src/test/java/org/apache/sis/test/suite/ConsoleTestSuite.java [UTF-8] (original)
+++ sis/trunk/application/sis-console/src/test/java/org/apache/sis/test/suite/ConsoleTestSuite.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -26,13 +26,14 @@ import org.junit.BeforeClass;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 @Suite.SuiteClasses({
     org.apache.sis.console.SubCommandTest.class,
     org.apache.sis.console.HelpSCTest.class,
     org.apache.sis.console.AboutSCTest.class,
+    org.apache.sis.console.MimeTypeSCTest.class,
     org.apache.sis.console.MetadataSCTest.class
 })
 public final strictfp class ConsoleTestSuite extends TestSuite {

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/maven/Packer.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/maven/Packer.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/maven/Packer.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/maven/Packer.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -46,7 +46,7 @@ final class Packer implements FilenameFi
 
     /**
      * The Maven target directory. Shall contain the {@code "binaries"} sub-directory,
-     * which shall contains all JAR files collected by {@code sis-build-helper} plugin.
+     * which shall contain all JAR files collected by {@code sis-build-helper} plugin.
      */
     private final File targetDirectory;
 

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -24,7 +24,7 @@ import com.sun.tools.doclets.formats.htm
 
 /**
  * The <code>@scmUrl</code> tag for inserting a URL to a file in the source code repository.
- * This tag shall contains a keyword, for example <code>{@scmUrl gmd-data}</code>.
+ * This tag shall contain a keyword, for example <code>{@scmUrl gmd-data}</code>.
  * Valid keywords are:
  *
  * <table class="sis>

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -101,7 +101,7 @@ final class Cloner extends org.apache.si
                 // Do not use the SIS Checked* classes since
                 // we don't need type checking anymore.
                 if (isSet) {
-                    collection = CollectionsExt.immutableSet(array);
+                    collection = CollectionsExt.immutableSet(false, array);
                 } else {
                     // Conservatively assumes a List if we are not sure to have a Set,
                     // since the list is less destructive (no removal of duplicated).

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -18,7 +18,6 @@ package org.apache.sis.metadata.iso;
 
 import java.util.Map;
 import java.util.Locale;
-import java.util.logging.Level;
 import java.io.Serializable;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -28,20 +27,17 @@ import org.opengis.metadata.citation.Cit
 import org.opengis.parameter.InvalidParameterValueException;
 import org.opengis.referencing.ReferenceIdentifier;
 import org.opengis.util.InternationalString;
-import org.apache.sis.util.Locales;
 import org.apache.sis.util.Immutable;
 import org.apache.sis.util.Deprecable;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.resources.Messages;
-import org.apache.sis.util.iso.SimpleInternationalString;
-import org.apache.sis.util.iso.DefaultInternationalString;
+import org.apache.sis.util.iso.Types;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.internal.jaxb.metadata.CI_Citation;
 import org.apache.sis.internal.jaxb.gco.StringAdapter;
 import org.apache.sis.internal.simple.SimpleIdentifiedObject;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+import static org.apache.sis.util.collection.Containers.property;
 import static org.opengis.referencing.IdentifiedObject.REMARKS_KEY;
 
 // Related to JDK7
@@ -60,7 +56,7 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * @author Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.03)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see DefaultIdentifier
@@ -150,6 +146,7 @@ public class ImmutableIdentifier impleme
         } else {
             remarks = null;
         }
+        validate();
     }
 
     /**
@@ -191,25 +188,25 @@ public class ImmutableIdentifier impleme
     public ImmutableIdentifier(final Citation authority, final String codeSpace,
             final String code, final String version, final InternationalString remarks)
     {
-        ensureNonNull("code", code);
         this.code      = code;
         this.codeSpace = codeSpace;
         this.authority = authority;
         this.version   = version;
         this.remarks   = remarks;
+        validate();
     }
 
     /**
      * Constructs an identifier from a set of properties. Keys are strings from the table below.
-     * Keys are case-insensitive, and leading and trailing spaces are ignored. The map given in
-     * argument shall contains at least a {@code "code"} property. Other properties listed in
-     * the table below are optional.
+     * The map given in argument shall contain an entry at least for the
+     * {@value org.opengis.metadata.Identifier#CODE_KEY} key.
+     * Other properties listed in the table below are optional.
      *
      * <table class="sis">
      *   <tr>
      *     <th>Property name</th>
      *     <th>Value type</th>
-     *     <th>Value given to</th>
+     *     <th>Returned by</th>
      *   </tr>
      *   <tr>
      *     <td>{@value org.opengis.metadata.Identifier#CODE_KEY}</td>
@@ -249,111 +246,56 @@ public class ImmutableIdentifier impleme
      */
     public ImmutableIdentifier(final Map<String,?> properties) throws IllegalArgumentException {
         ensureNonNull("properties", properties);
-        Object code      = null;
-        Object codeSpace = null;
-        Object version   = null;
-        Object authority = null;
-        Object remarks   = null;
-        DefaultInternationalString localized = null;
+        code      = property(properties, CODE_KEY,      String.class);
+        version   = property(properties, VERSION_KEY,   String.class);
+        remarks   = Types.toInternationalString(properties, REMARKS_KEY);
         /*
-         * Iterate through each map entry. This have two purposes:
-         *
-         *   1) Ignore case (a call to properties.get("foo") can't do that)
-         *   2) Find localized remarks.
-         *
-         * This algorithm is sub-optimal if the map contains a lot of entries of no interest to
-         * this identifier. Hopefully, most users will fill a map with only useful entries.
+         * Map String authority to one of the pre-defined constants (typically EPSG or OGC).
          */
-        for (final Map.Entry<String,?> entry : properties.entrySet()) {
-            String key   = entry.getKey().trim().toLowerCase();
-            Object value = entry.getValue();
-            /*switch (key)*/ { // This is a "string in switch" on the JDK7 branch.
-                if (key.equals(CODE_KEY)) {
-                    code = value;
-                    continue;
-                }
-                else if (key.equals(CODESPACE_KEY)) {
-                    codeSpace = value;
-                    continue;
-                }
-                else if (key.equals(VERSION_KEY)) {
-                    version = value;
-                    continue;
-                }
-                else if (key.equals(AUTHORITY_KEY)) {
-                    if (value instanceof String) {
-                        value = Citations.fromName((String) value);
-                    }
-                    authority = value;
-                    continue;
-                }
-                else if (key.equals(REMARKS_KEY)) {
-                    if (value instanceof String) {
-                        value = new SimpleInternationalString((String) value);
-                    }
-                    remarks = value;
-                    continue;
-                }
-            }
-            /*
-             * Search for additional locales (e.g. "remarks_fr").
-             */
-            final Locale locale = Locales.parseSuffix(REMARKS_KEY, key);
-            if (locale != null) {
-                if (localized == null) {
-                    localized = new DefaultInternationalString();
-                }
-                localized.add(locale, (String) value);
-            }
-        }
-        /*
-         * Get the localized remarks, if it was not yet set. If a user specified remarks
-         * both as InternationalString and as String for some locales (which is a weird
-         * usage...), then current implementation discards the later with a warning.
-         */
-        if (localized != null) {
-            if (remarks == null) {
-                remarks = localized;
-            } else if (remarks instanceof SimpleInternationalString) {
-                localized.add(Locale.ROOT, remarks.toString());
-                remarks = localized;
-            } else {
-                Logging.log(ImmutableIdentifier.class, "<init>",
-                    Messages.getResources(null).getLogRecord(Level.WARNING, Messages.Keys.LocalesDiscarded));
-            }
-        }
-        /*
-         * Complete the code space if it was not explicitly set. We take the first
-         * identifier if there is any, otherwise we take the shortest title.
-         */
-        if (codeSpace == null && authority instanceof Citation) {
-            codeSpace = Citations.getIdentifier((Citation) authority);
+        Object value = properties.get(AUTHORITY_KEY);
+        if (value instanceof String) {
+            authority = Citations.fromName((String) value);
+        } else if (value == null || value instanceof Citation) {
+            authority = (Citation) value;
+        } else {
+            throw illegalPropertyType(AUTHORITY_KEY, value);
         }
         /*
-         * Store the definitive reference to the attributes. Note that casts are performed only
-         * there (not before). This is a wanted feature, since we want to catch ClassCastExceptions
-         * and rethrown them as more informative exceptions.
+         * Complete the code space if it was not explicitly set. We take a short identifier (preferred) or title
+         * (as a fallback), with precedence given to Unicode identifier (see Citations.getIdentifier(…) for more
+         * information). Then the getCodeSpace(…) method applies additional restrictions in order to reduce the
+         * risk of false code space.
          */
-        String key   = null;
-        Object value = null;
-        try {
-            key=      CODE_KEY; this.code      = (String)              (value = code);
-            key=   VERSION_KEY; this.version   = (String)              (value = version);
-            key= CODESPACE_KEY; this.codeSpace = (String)              (value = codeSpace);
-            key= AUTHORITY_KEY; this.authority = (Citation)            (value = authority);
-            key=   REMARKS_KEY; this.remarks   = (InternationalString) (value = remarks);
-        } catch (ClassCastException exception) {
-            final InvalidParameterValueException e = new InvalidParameterValueException(
-                    Errors.format(Errors.Keys.IllegalArgumentValue_2, key, value), key, value);
-            e.initCause(exception);
-            throw e;
+        value = properties.get(CODESPACE_KEY);
+        if (value == null && !properties.containsKey(CODESPACE_KEY)) {
+            codeSpace = getCodeSpace(authority);
+        } else if (value instanceof String) {
+            codeSpace = (String) value;
+        } else {
+            throw illegalPropertyType(CODESPACE_KEY, value);
         }
-        if (code == null) {
-            throw new IllegalArgumentException(Errors.format(Errors.Keys.MissingValueForProperty_1, CODE_KEY));
+        validate();
+    }
+
+    /**
+     * Ensures that the properties of this {@code ImmutableIdentifier} are valid.
+     */
+    private void validate() {
+        if (code == null || code.isEmpty()) {
+            throw new IllegalArgumentException(Errors.format((code == null)
+                    ? Errors.Keys.MissingValueForProperty_1
+                    : Errors.Keys.EmptyProperty_1, CODE_KEY));
         }
     }
 
     /**
+     * Returns the exception to be thrown when a property if of illegal type.
+     */
+    private static IllegalArgumentException illegalPropertyType(final String key, final Object value) {
+        return new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyClass_2, key, value.getClass()));
+    }
+
+    /**
      * Returns a SIS identifier implementation with the values of the given arbitrary implementation.
      * This method performs the first applicable actions in the following choices:
      *
@@ -404,6 +346,37 @@ public class ImmutableIdentifier impleme
     }
 
     /**
+     * Infers a code space from the given authority. First, this method takes a short identifier or title with
+     * preference for Unicode identifier - see {@link Citations#getIdentifier(Citation)} for more information.
+     * Next this method applies additional restrictions in order to reduce the risk of undesired code space.
+     * Those restrictions are arbitrary and may change in any future SIS version. Currently, the restriction
+     * is to accept only letters or digits.
+     *
+     * @param  authority The authority for which to get a code space.
+     * @return The code space, or {@code null} if none.
+     *
+     * @see Citations#getIdentifier(Citation)
+     */
+    private static String getCodeSpace(final Citation authority) {
+        final String codeSpace = Citations.getIdentifier(authority);
+        if (codeSpace != null) {
+            final int length = codeSpace.length();
+            if (length != 0) {
+                int i = 0;
+                do {
+                    final int c = codeSpace.charAt(i);
+                    if (!Character.isLetterOrDigit(c)) {
+                        return null;
+                    }
+                    i += Character.charCount(c);
+                } while (i < length);
+                return codeSpace;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Organization or party responsible for definition and maintenance of the
      * {@linkplain #getCode code}.
      *

Modified: sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -425,7 +425,7 @@ public final strictfp class PropertyAcce
         assertInstanceOf("alternateTitles", Collection.class, oldValue);
         assertTrue("alternateTitles", ((Collection<?>) oldValue).isEmpty());
 
-        // Insert the second value. Old collection shall contains the first value.
+        // Insert the second value. Old collection shall contain the first value.
         oldValue = accessor.set(index, instance, conversion ? text2 : title2, RETURN_PREVIOUS);
         assertInstanceOf("alternateTitles", Collection.class, oldValue);
         oldValue = getSingleton((Collection<?>) oldValue);

Modified: sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/ImmutableIdentifierTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/ImmutableIdentifierTest.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/ImmutableIdentifierTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/ImmutableIdentifierTest.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -19,7 +19,7 @@ package org.apache.sis.metadata.iso;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Locale;
-import org.opengis.parameter.InvalidParameterValueException;
+import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.apache.sis.util.iso.SimpleInternationalString;
 import org.apache.sis.test.DependsOnMethod;
@@ -65,6 +65,7 @@ public final strictfp class ImmutableIde
         Validators.validate(identifier);
 
         assertEquals(CODE_KEY,        "This is a code",       identifier.getCode());
+        assertNull  (CODESPACE_KEY,                           identifier.getCodeSpace());
         assertEquals(AUTHORITY_KEY,   "This is an authority", identifier.getAuthority().getTitle().toString());
         assertEquals(VERSION_KEY,     "This is a version",    identifier.getVersion());
         assertEquals("remarks",       "There is remarks",     identifier.getRemarks().toString(Locale.ENGLISH));
@@ -85,6 +86,7 @@ public final strictfp class ImmutableIde
         Validators.validate(identifier);
 
         assertEquals(CODE_KEY,        "This is a code",       identifier.getCode());
+        assertNull  (CODESPACE_KEY,                           identifier.getCodeSpace());
         assertEquals(AUTHORITY_KEY,   "This is an authority", identifier.getAuthority().getTitle().toString());
         assertEquals(VERSION_KEY,     "This is a version",    identifier.getVersion());
         assertEquals("remarks",       "Overwritten remarks",  identifier.getRemarks().toString(Locale.ENGLISH));
@@ -94,18 +96,17 @@ public final strictfp class ImmutableIde
 
     /**
      * Tests the constructor with the {@code "authority"} attribute as a {@link DefaultCitation}.
-     * This test also opportunistically test mixed-case key.
      */
     @Test
     @DependsOnMethod("testConstructorWithStringValues")
     public void testConstructorWithCitation() {
         final Map<String,Object> properties = properties();
-        assertNotNull(properties.remove(AUTHORITY_KEY));
-        assertNull(properties.put("AutHOrITY", new DefaultCitation("An other authority")));
+        assertNotNull(properties.put(AUTHORITY_KEY, new DefaultCitation("An other authority")));
         final ImmutableIdentifier identifier = new ImmutableIdentifier(properties);
         Validators.validate(identifier);
 
         assertEquals(CODE_KEY,        "This is a code",       identifier.getCode());
+        assertNull  (CODESPACE_KEY,                           identifier.getCodeSpace());
         assertEquals(AUTHORITY_KEY,   "An other authority",   identifier.getAuthority().getTitle().toString());
         assertEquals(VERSION_KEY,     "This is a version",    identifier.getVersion());
         assertEquals("remarks",       "There is remarks",     identifier.getRemarks().toString(Locale.ENGLISH));
@@ -114,6 +115,28 @@ public final strictfp class ImmutableIde
     }
 
     /**
+     * Tests the constructor with the {@code "authority"} attribute as one of the pre-defined constants.
+     *
+     * @see Citations#fromName(String)
+     */
+    @Test
+    @DependsOnMethod("testConstructorWithStringValues")
+    public void testPredefinedCitation() {
+        final Map<String,Object> properties = properties();
+        assertNotNull(properties.put(AUTHORITY_KEY, "EPSG"));
+        final ImmutableIdentifier identifier = new ImmutableIdentifier(properties);
+        Validators.validate(identifier);
+
+        assertEquals(CODE_KEY,        "This is a code",       identifier.getCode());
+        assertSame  (AUTHORITY_KEY,   Citations.EPSG,         identifier.getAuthority());
+        assertEquals(CODESPACE_KEY,   "EPSG",                 identifier.getCodeSpace()); // Inferred from authority.
+        assertEquals(VERSION_KEY,     "This is a version",    identifier.getVersion());
+        assertEquals("remarks",       "There is remarks",     identifier.getRemarks().toString(Locale.ENGLISH));
+        assertEquals("remarks_fr",    "Voici des remarques",  identifier.getRemarks().toString(Locale.FRENCH));
+        assertEquals("remarks_fr_CA", "Pareil",               identifier.getRemarks().toString(Locale.CANADA_FRENCH));
+    }
+
+    /**
      * Tests the constructor with an argument of the wrong type.
      */
     @Test
@@ -124,7 +147,7 @@ public final strictfp class ImmutableIde
         try {
             final ImmutableIdentifier identifier = new ImmutableIdentifier(properties);
             fail(identifier.toString());
-        } catch (InvalidParameterValueException e) {
+        } catch (IllegalArgumentException e) {
             // This is the expected exception
             final String message = e.getMessage();
             assertTrue(message, message.contains(AUTHORITY_KEY));

Modified: sis/trunk/core/sis-referencing/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/pom.xml?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/pom.xml (original)
+++ sis/trunk/core/sis-referencing/pom.xml Fri Aug 30 21:00:40 2013
@@ -138,6 +138,13 @@ Implementations of Coordinate Reference 
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.sis.core</groupId>
+      <artifactId>sis-metadata</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -606,10 +606,16 @@ public abstract class AbstractEnvelope i
     }
 
     /**
-     * Determines whether or not this envelope is empty. An envelope is non-empty only if it has
-     * at least one {@linkplain #getDimension() dimension}, and the {@linkplain #getSpan(int) span}
-     * is greater than 0 along all dimensions. Note that {@link #isAllNaN()} always returns
-     * {@code false} for a non-empty envelope, but the converse is not always true.
+     * Determines whether or not this envelope is empty. An envelope is empty if it has zero
+     * {@linkplain #getDimension() dimension}, or if the {@linkplain #getSpan(int) span} of
+     * at least one axis is negative, 0 or {@link Double#NaN NaN}.
+     *
+     * {@note Strictly speaking, there is an ambiguity if a span is <code>NaN</code> or if the envelope
+     *        contains both 0 and infinite spans (since 0⋅∞ = <code>NaN</code>). In such cases, this method
+     *        arbitrarily ignores the infinite values and returns <code>true</code>.}
+     *
+     * If {@code isEmpty()} returns {@code false}, then {@link #isAllNaN()} is guaranteed to
+     * also return {@code false}. However the converse is not always true.
      *
      * @return {@code true} if this envelope is empty.
      *
@@ -632,10 +638,10 @@ public abstract class AbstractEnvelope i
 
     /**
      * Returns {@code false} if at least one ordinate value is not {@linkplain Double#NaN NaN}.
-     * This {@code isAllNaN()} check is a little bit different than the {@link #isEmpty()} check
-     * since it returns {@code false} for a partially initialized envelope, while {@code isEmpty()}
-     * returns {@code false} only after all dimensions have been initialized. More specifically,
-     * the following rules apply:
+     * This {@code isAllNaN()} check is different than the {@link #isEmpty()} check since it
+     * returns {@code false} for a partially initialized envelope, while {@code isEmpty()}
+     * returns {@code false} only after all dimensions have been initialized.
+     * More specifically, the following rules apply:
      *
      * <ul>
      *   <li>If {@code isAllNaN() == true}, then {@code isEmpty() == true}</li>
@@ -643,7 +649,7 @@ public abstract class AbstractEnvelope i
      *   <li>The converse of the above-cited rules are not always true.</li>
      * </ul>
      *
-     * Note that a all-NaN envelope can still have a non-null
+     * Note that an all-NaN envelope can still have a non-null
      * {@linkplain #getCoordinateReferenceSystem() coordinate reference system}.
      *
      * @return {@code true} if this envelope has NaN values.

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -37,7 +37,7 @@ import org.apache.sis.referencing.CRS;
 
 import static org.apache.sis.util.ArgumentChecks.*;
 import static org.apache.sis.math.MathFunctions.isNegative;
-import static org.apache.sis.internal.referencing.Utilities.isPoleToPole;
+import static org.apache.sis.internal.referencing.Formulas.isPoleToPole;
 
 // Related to JDK7
 import org.apache.sis.internal.jdk7.Objects;
@@ -89,6 +89,11 @@ class ArrayEnvelope extends AbstractEnve
         this.ordinates = ordinates;
     }
 
+    /*
+     * Constructors below this point have public access because if we decided to make class
+     * ArrayEnvelope public, then we would probably want to make those constructors public too.
+     */
+
     /**
      * Constructs an envelope defined by two corners given as direct positions.
      * If at least one corner is associated to a CRS, then the new envelope will also

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -37,7 +37,7 @@ import static org.apache.sis.math.MathFu
 import static org.apache.sis.math.MathFunctions.isNegative;
 import static org.apache.sis.math.MathFunctions.isSameSign;
 import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
-import static org.apache.sis.internal.referencing.Utilities.isPoleToPole;
+import static org.apache.sis.internal.referencing.Formulas.isPoleToPole;
 
 // Following imports are needed because we can't extend AbstractEnvelope.
 // We want to write this class as if it was an AbstractEnvelope subclass.

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -16,9 +16,610 @@
  */
 package org.apache.sis.referencing;
 
+import java.util.Map;
+import java.util.Set;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Locale;
+import java.io.Serializable;
+import javax.xml.bind.annotation.XmlID;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import org.opengis.util.GenericName;
+import org.opengis.util.InternationalString;
+import org.opengis.metadata.Identifier;
+import org.opengis.metadata.citation.Citation;
+import org.opengis.referencing.ObjectFactory;
+import org.opengis.referencing.AuthorityFactory;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.ReferenceIdentifier;
+import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.xml.Namespaces;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.Immutable;
+import org.apache.sis.util.ThreadSafe;
+import org.apache.sis.util.Deprecable;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.LenientComparable;
+import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.resources.Errors;
 
-public class AbstractIdentifiedObject {
+import static org.apache.sis.util.ArgumentChecks.*;
+import static org.apache.sis.util.Utilities.deepEquals;
+import static org.apache.sis.internal.util.Citations.iterator;
+import static org.apache.sis.internal.util.CollectionsExt.nonNull;
+import static org.apache.sis.internal.util.CollectionsExt.nonEmpty;
+import static org.apache.sis.internal.util.CollectionsExt.immutableSet;
+
+// Related to JDK7
+import org.apache.sis.internal.jdk7.Objects;
+
+
+/**
+ * Base class for objects identified by a name or a code. Those objects are typically
+ * {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum geodetic datum}   (e.g. "<cite>World Geodetic System 1984</cite>"),
+ * {@linkplain org.apache.sis.referencing.crs.AbstractCRS Coordinate Reference System} (e.g. "<cite>WGS 84 / World Mercator</cite>") or
+ * {@linkplain org.apache.sis.referencing.operation.DefaultProjection map projection}  (e.g. "<cite>Mercator (variant A)</cite>").
+ * Those names, or a code (e.g. {@code "EPSG:3395"}), can be used for fetching an object from a database.
+ * However it is not sufficient to know the object name. We also need to know who define that name
+ * (the {@linkplain NamedIdentifier#getAuthority() authority}) since the same objects are often named differently
+ * depending on the providers, or conversely the same name is used for different objects depending on the provider.
+ *
+ * <p>The main information stored in an {@code IdentifiedObject} are:</p>
+ * <ul>
+ *   <li>a primary {@linkplain #getName() name}, considered by the object creator as the preferred name,</li>
+ *   <li>an arbitrary amount of {@linkplain #getAlias() aliases}, for example a list of names used by other providers,</li>
+ *   <li>an arbitrary amount of {@linkplain #getIdentifiers() identifiers}, typically primary keys in the provider database,</li>
+ *   <li>optional {@linkplain #getRemarks() remarks}.</li>
+ * </ul>
+ *
+ * {@section Instantiation}
+ * This class is conceptually <cite>abstract</cite>, even if it is technically possible to instantiate it.
+ * Applications should instead instantiate the most specific subclass having a name starting by {@code Default}.
+ * However exceptions to this rule may occur when it is not possible to identify the exact type.
+ *
+ * {@example It is sometime not possible to infer the exact coordinate system from version 1 of
+ *           <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
+ *           Known Text</cite></a>, for example when parsing a <code>LOCAL_CS</code> element. In such exceptional
+ *           situation, a plain <code>AbstractCS</code> object may be instantiated.}
+ *
+ * {@code IdentifiedObject} instances are created in two main ways:
+ *
+ * <ul>
+ *   <li>Using an {@link ObjectFactory}, in which case all properties can be explicitely specified.</li>
+ *   <li>Using an {@link AuthorityFactory}, in which case only a code (typically a primary key) is specified.
+ *       The {@linkplain NamedIdentifier#getAuthority() authority}
+ *       and {@linkplain NamedIdentifier#getCode() authority code} values are set to the authority name
+ *       of the factory object, and the authority code supplied by the client, respectively.
+ *       All other information are fetched from the database.</li>
+ * </ul>
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.4 (derived from geotk-1.2)
+ * @version 0.4
+ * @module
+ */
+@Immutable
+@ThreadSafe
+@XmlType(name="IdentifiedObjectType", propOrder={
+    "identifier",
+    "name"
+})
+public class AbstractIdentifiedObject extends FormattableObject implements IdentifiedObject,
+        LenientComparable, Deprecable, Serializable
+{
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = -5173281694258483264L;
+
+    /**
+     * The name for this object or code. Should never be {@code null}.
+     *
+     * @see #getName()
+     * @see #getIdentifier()
+     */
+    @XmlElement
+    private final ReferenceIdentifier name;
+
+    /**
+     * An alternative name by which this object is identified, or {@code null} if none.
+     * We must be prepared to handle either null or an empty set for "no alias" because
+     * we may get both on unmarshalling.
+     */
+    private final Collection<GenericName> alias;
+
+    /**
+     * An identifier which references elsewhere the object's defining information.
+     * Alternatively an identifier by which this object can be referenced.
+     *
+     * <p>We must be prepared to handle either null or an empty set for
+     * "no identifiers" because we may get both on unmarshalling.</p>
+     *
+     * @see #getIdentifiers()
+     * @see #getIdentifier()
+     */
+    private final Set<ReferenceIdentifier> identifiers;
+
+    /**
+     * Comments on or information about this object, or {@code null} if none.
+     */
+    private final InternationalString remarks;
+
+    /**
+     * The cached hash code value, or 0 if not yet computed. This field is calculated only when
+     * first needed. We do not declare it {@code volatile} because it is not a big deal if this
+     * field is calculated many time, and the same value should be produced by all computations.
+     * The only possible outdated value is 0, which is okay.
+     */
+    private transient int hashCode;
+
+    /**
+     * Constructs a new identified object with the same values than the specified one.
+     * This copy constructor provides a way to convert an arbitrary implementation into a SIS one or a
+     * user-defined one (as a subclass), usually in order to leverage some implementation-specific API.
+     *
+     * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p>
+     *
+     * @param object The object to shallow copy.
+     */
+    public AbstractIdentifiedObject(final IdentifiedObject object) {
+        ensureNonNull("object", object);
+        name        =          object.getName();
+        alias       = nonEmpty(object.getAlias()); // Favor null for empty set in case it is not Collections.EMPTY_SET
+        identifiers = nonEmpty(object.getIdentifiers());
+        remarks     =          object.getRemarks();
+    }
+
+    /**
+     * Constructs an object from a set of properties. Keys are strings from the table below.
+     * The map given in argument shall contain an entry at least for the
+     * {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} key.
+     * Other properties listed in the table below are optional.
+     *
+     * <table class="sis">
+     *   <tr>
+     *     <th>Property name</th>
+     *     <th>Value type</th>
+     *     <th>Returned by</th>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
+     *     <td>{@link String} or {@link ReferenceIdentifier}</td>
+     *     <td>{@link #getName()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td>
+     *     <td>{@link CharSequence}, {@link GenericName} or an array of those</td>
+     *     <td>{@link #getAlias()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#AUTHORITY_KEY}</td>
+     *     <td>{@link String} or {@link Citation}</td>
+     *     <td>{@link NamedIdentifier#getAuthority()} on the {@linkplain #getName() name}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.metadata.Identifier#CODE_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link NamedIdentifier#getCode()} on the {@linkplain #getName() name}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.ReferenceIdentifier#CODESPACE_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link NamedIdentifier#getCodeSpace()} on the {@linkplain #getName() name}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.ReferenceIdentifier#VERSION_KEY}</td>
+     *     <td>{@link String}</td>
+     *     <td>{@link NamedIdentifier#getVersion()} on the {@linkplain #getName() name}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
+     *     <td>{@link ReferenceIdentifier} or <code>{@linkplain ReferenceIdentifier}[]</code></td>
+     *     <td>{@link #getIdentifiers()}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td>
+     *     <td>{@link String} or {@link InternationalString}</td>
+     *     <td>{@link #getRemarks()}</td>
+     *   </tr>
+     * </table>
+     *
+     * Additionally, all localizable attributes like {@code "remarks"} may have a language and country code suffix.
+     * For example the {@code "remarks_fr"} property stands for remarks in {@linkplain Locale#FRENCH French} and
+     * the {@code "remarks_fr_CA"} property stands for remarks in {@linkplain Locale#CANADA_FRENCH French Canadian}.
+     *
+     * <p>Note that the {@code "authority"} and {@code "version"} properties are ignored if the {@code "name"}
+     * property is already a {@link ReferenceIdentifier} object instead than a {@link String}.</p>
+     *
+     * @param  properties The properties to be given to this identified object.
+     * @throws IllegalArgumentException if a property has an invalid value.
+     */
+    public AbstractIdentifiedObject(final Map<String,?> properties) throws IllegalArgumentException {
+        ensureNonNull("properties", properties);
+
+        // -------------------------------------
+        // "name": String or ReferenceIdentifier
+        // -------------------------------------
+        Object value = properties.get(NAME_KEY);
+        if (value == null || value instanceof String) {
+            name = new NamedIdentifier(PropertiesConverter.convert(properties));
+        } else if (value instanceof ReferenceIdentifier) {
+            name = (ReferenceIdentifier) value;
+        } else {
+            throw illegalPropertyType(NAME_KEY, value);
+        }
+
+        // -------------------------------------------------------------------
+        // "alias": CharSequence, CharSequence[], GenericName or GenericName[]
+        // -------------------------------------------------------------------
+        value = properties.get(ALIAS_KEY);
+        try {
+            alias = immutableSet(true, Types.toGenericNames(value, null));
+        } catch (ClassCastException e) {
+            throw (IllegalArgumentException) illegalPropertyType(ALIAS_KEY, value).initCause(e);
+        }
+
+        // -----------------------------------------------------------
+        // "identifiers": ReferenceIdentifier or ReferenceIdentifier[]
+        // -----------------------------------------------------------
+        value = properties.get(IDENTIFIERS_KEY);
+        if (value == null) {
+            identifiers = null;
+        } else if (value instanceof ReferenceIdentifier) {
+            identifiers = Collections.singleton((ReferenceIdentifier) value);
+        } else if (value instanceof ReferenceIdentifier[]) {
+            identifiers = immutableSet(true, (ReferenceIdentifier[]) value);
+        } else {
+            throw illegalPropertyType(IDENTIFIERS_KEY, value);
+        }
+
+        // ----------------------------------------
+        // "remarks": String or InternationalString
+        // ----------------------------------------
+        remarks = Types.toInternationalString(properties, REMARKS_KEY);
+    }
+
+    /**
+     * Returns the exception to be thrown when a property if of illegal type.
+     */
+    private static IllegalArgumentException illegalPropertyType(final String key, final Object value) {
+        return new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyClass_2, key, value.getClass()));
+    }
+
+    /**
+     * The {@code gml:id}, which is mandatory. The current implementation searches for the first identifier,
+     * regardless its authority. If no identifier is found, then the name is used.
+     * If no name is found (which should not occur for valid objects), then this method returns {@code null}.
+     *
+     * <p>When an identifier has been found, this method returns the concatenation of its code space with its code,
+     * <em>without separator</em>. For example this method may return {@code "EPSG4326"}, not {@code "EPSG:4326"}.</p>
+     *
+     * <p>The returned ID needs to be unique only in the XML document being marshalled.
+     * Consecutive invocations of this method do not need to return the same value,
+     * since it may depends on the marshalling context.</p>
+     */
+    @XmlID
+    @XmlAttribute(name = "id", namespace = Namespaces.GML, required = true)
+    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+    final String getID() {
+        final StringBuilder id = new StringBuilder();
+        /*
+         * We will iterate over the identifiers first. Only after the iteration is over,
+         * if we found no suitable ID, then we will use the primary name as a last resort.
+         */
+        Iterator<ReferenceIdentifier> it = iterator(identifiers);
+        do {
+            final ReferenceIdentifier identifier;
+            if (it != null && it.hasNext()) {
+                identifier = it.next();
+            } else {
+                it = null;
+                identifier = name;
+            }
+            if (identifier != null) {
+                boolean codeSpace = true;
+                do { // Executed exactly twice: once for codespace, then once for code.
+                    final String part = codeSpace ? identifier.getCodeSpace() : identifier.getCode();
+                    if (part != null) {
+                        /*
+                         * Found a codespace (in the first iteration) or a code (in the second iteration).
+                         * Append to the buffer only the characters that are valid for a Unicode identifier.
+                         */
+                        for (int i=0; i<part.length();) {
+                            final int c = part.codePointAt(i);
+                            if (id.length() == 0 ? Character.isUnicodeIdentifierStart(c)
+                                                 : Character.isUnicodeIdentifierPart(c))
+                            {
+                                id.appendCodePoint(c);
+                            }
+                            i += Character.charCount(c);
+                        }
+                    }
+                } while ((codeSpace = !codeSpace) == false);
+                if (id.length() != 0) {
+                    /*
+                     * TODO: If we want to check for ID uniqueness or any other condition before to accept the ID,
+                     * we would do that here. If the ID is rejected, then we just need to clear the buffer and let
+                     * the iteration continue the search for an other ID.
+                     */
+                    return id.toString();
+                }
+            }
+        } while (it != null);
+        return null;
+    }
+
+    /**
+     * Returns the primary name by which this object is identified.
+     *
+     * @return The primary name.
+     *
+     * @see IdentifiedObjects#getName(IdentifiedObject, Citation)
+     */
+    @Override
+    public ReferenceIdentifier getName() {
+        return name;
+    }
+
+    /**
+     * Returns alternative names by which this object is identified.
+     *
+     * @return The aliases, or an empty collection if there is none.
+     *
+     * @see #getName()
+     */
+    @Override
+    public Collection<GenericName> getAlias() {
+        return nonNull(alias); // Needs to be null-safe because we may have a null value on unmarshalling.
+    }
+
+    /**
+     * Returns identifiers which references elsewhere the object's defining information.
+     * Alternatively identifiers by which this object can be referenced.
+     *
+     * @return This object identifiers, or an empty set if there is none.
+     *
+     * @see IdentifiedObjects#getIdentifier(IdentifiedObject, Citation)
+     */
+    @Override
+    public Set<ReferenceIdentifier> getIdentifiers() {
+        return nonNull(identifiers); // Needs to be null-safe because we may have a null value on unmarshalling.
+    }
+
+    /**
+     * Returns the first identifier found, or {@code null} if none.
+     * This method is invoked by JAXB at marshalling time.
+     *
+     * @see #name
+     */
+    @XmlElement(name = "identifier")
+    final ReferenceIdentifier getIdentifier() {
+        final Iterator<ReferenceIdentifier> it = iterator(identifiers);
+        return (it != null && it.hasNext()) ? it.next() : null;
+    }
+
+    /**
+     * Returns comments on or information about this object, including data source information.
+     *
+     * @return The remarks, or {@code null} if none.
+     */
+    @Override
+    public InternationalString getRemarks(){
+        return remarks;
+    }
+
+    /**
+     * Returns {@code true} if this object is deprecated. Deprecated objects exist in some
+     * {@linkplain org.opengis.referencing.AuthorityFactory authority factories} like the
+     * EPSG database. Deprecated objects are usually obtained from a deprecated authority code.
+     * For this reason, the default implementation applies the following rules:
+     *
+     * <ul>
+     *   <li>If the {@linkplain #getName() name} is deprecated, then returns {@code true}.</li>
+     *   <li>Otherwise if <strong>all</strong> {@linkplain #getIdentifiers() identifiers}
+     *       are deprecated, ignoring the identifiers that are not instance of {@link Deprecable}
+     *       (because they can not be tested), then returns {@code true}.</li>
+     *   <li>Otherwise returns {@code false}.</li>
+     * </ul>
+     *
+     * @return {@code true} if this object is deprecated.
+     *
+     * @see org.apache.sis.metadata.iso.ImmutableIdentifier#isDeprecated()
+     */
+    @Override
+    public boolean isDeprecated() {
+        if (name instanceof Deprecable) {
+            if (((Deprecable) name).isDeprecated()) {
+                return true;
+            }
+        }
+        boolean isDeprecated = false;
+        for (final ReferenceIdentifier identifier : nonNull(identifiers)) {
+            if (identifier instanceof Deprecable) {
+                if (!((Deprecable) identifier).isDeprecated()) {
+                    return false;
+                }
+                isDeprecated = true;
+            }
+        }
+        return isDeprecated;
+    }
+
+    /**
+     * Returns {@code true} if either the {@linkplain #getName() primary name} or at least
+     * one {@linkplain #getAlias alias} matches the specified string.
+     * This method performs the search in the following order, regardless of any authority:
+     *
+     * <ul>
+     *   <li>The {@linkplain #getName() primary name} of this object</li>
+     *   <li>The {@linkplain ScopedName fully qualified name} of an alias</li>
+     *   <li>The {@linkplain LocalName local name} of an alias</li>
+     * </ul>
+     *
+     * @param  name The name to compare.
+     * @return {@code true} if the primary name of at least one alias matches the specified {@code name}.
+     *
+     * @see IdentifiedObjects#nameMatches(IdentifiedObject, String)
+     */
     public boolean nameMatches(final String name) {
-        return false;
+        return IdentifiedObjects.nameMatches(this, alias, name);
+    }
+
+    /**
+     * Compares the specified object with this object for equality.
+     * This method is implemented as below (omitting assertions):
+     *
+     * {@preformat java
+     *     return equals(other, ComparisonMode.STRICT);
+     * }
+     *
+     * @param  object The other object (may be {@code null}).
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public final boolean equals(final Object object) {
+        final boolean eq = equals(object, ComparisonMode.STRICT);
+        // If objects are equal, then they must have the same hash code value.
+        assert !eq || hashCode() == object.hashCode() : this;
+        return eq;
+    }
+
+    /**
+     * Compares this object with the specified object for equality.
+     * The strictness level is controlled by the second argument:
+     *
+     * <ul>
+     *   <li>If {@code mode} is {@link ComparisonMode#STRICT STRICT}, then all available properties
+     *       are compared including {@linkplain #getName() name}, {@linkplain #getRemarks() remarks},
+     *       {@linkplain #getIdentifiers() identifiers code}, <i>etc.</i></li>
+     *   <li>If {@code mode} is {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA},
+     *       then this method compare only the properties needed for computing transformations.
+     *       In other words, {@code sourceCS.equals(targetCS, IGNORE_METADATA)} returns {@code true}
+     *       if the transformation from {@code sourceCS} to {@code targetCS} is likely to be the
+     *       identity transform, no matter what {@link #getName()} said.</li>
+     * </ul>
+     *
+     * {@section Exceptions to the above rules}
+     * Some subclasses (especially {@link org.apache.sis.referencing.datum.AbstractDatum}
+     * and {@link org.apache.sis.parameter.AbstractParameterDescriptor}) will test for the
+     * {@linkplain #getName() name}, since objects with different name have completely
+     * different meaning. For example nothing differentiate the {@code "semi_major"} and
+     * {@code "semi_minor"} parameters except the name. The name comparison may be loose
+     * however, i.e. we may accept a name matching an alias.
+     *
+     * @param  object The object to compare to {@code this}.
+     * @param  mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
+     *         {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
+     *         relevant to transformations.
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object object, final ComparisonMode mode) {
+        if (object == null) {
+            return false;
+        }
+        if (getClass() == object.getClass()) {
+            /*
+             * If the classes are the same, then the hash codes should be computed in the same
+             * way. Since those codes are cached, this is an efficient way to quickly check if
+             * the two objects are different. Note that using the hash codes for comparisons
+             * that ignore metadata is okay only if the implementation note described in the
+             * 'computeHashCode()' javadoc hold (metadata not used in hash code computation).
+             */
+            if (mode.ordinal() < ComparisonMode.APPROXIMATIVE.ordinal()) {
+                final int tc = hashCode;
+                if (tc != 0) {
+                    final int oc = ((AbstractIdentifiedObject) object).hashCode;
+                    if (oc != 0 && tc != oc) {
+                        return false;
+                    }
+                }
+            }
+        } else {
+            if (mode == ComparisonMode.STRICT) { // Same classes was required for this mode.
+                return false;
+            }
+            if (!(object instanceof IdentifiedObject)) {
+                return false;
+            }
+        }
+        switch (mode) {
+            case STRICT: {
+                final AbstractIdentifiedObject that = (AbstractIdentifiedObject) object;
+                return Objects.equals(        name,                 that.name)         &&
+                       Objects.equals(nonNull(alias),       nonNull(that.alias))       &&
+                       Objects.equals(nonNull(identifiers), nonNull(that.identifiers)) &&
+                       Objects.equals(        remarks,              that.remarks);
+            }
+            case BY_CONTRACT: {
+                final IdentifiedObject that = (IdentifiedObject) object;
+                return deepEquals(        getName(),                 that.getName(),         mode) &&
+                       deepEquals(nonNull(getAlias()),       nonNull(that.getAlias()),       mode) &&
+                       deepEquals(nonNull(getIdentifiers()), nonNull(that.getIdentifiers()), mode) &&
+                       deepEquals(        getRemarks(),              that.getRemarks(),      mode);
+            }
+            case IGNORE_METADATA:
+            case APPROXIMATIVE:
+            case DEBUG: {
+                return true;
+            }
+            default: {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.UnknownEnumValue_1, mode));
+            }
+        }
+    }
+
+    /**
+     * Returns a hash value for this identified object.
+     * This method invokes {@link #computeHashCode()} when first needed and caches the value for future invocations.
+     * Subclasses shall override {@code computeHashCode()} instead than this method.
+     *
+     * {@section Implementation specific feature}
+     * In the Apache SIS implementation, the {@linkplain #getName() name}, {@linkplain #getIdentifiers() identifiers}
+     * and {@linkplain #getRemarks() remarks} are not used for hash code computation.
+     * Consequently two identified objects will return the same hash value if they are equal in the sense of
+     * <code>{@linkplain #equals(Object, ComparisonMode) equals}(…, {@linkplain ComparisonMode#IGNORE_METADATA})</code>.
+     * This feature allows users to implement metadata-insensitive {@link java.util.HashMap}.
+     *
+     * @return The hash code value. This value may change between different execution of the Apache SIS library.
+     */
+    @Override
+    public final int hashCode() { // No need to synchronize; ok if invoked twice.
+        int hash = hashCode;
+        if (hash == 0) {
+            hash = computeHashCode();
+            if (hash == 0) {
+                hash = -1;
+            }
+            hashCode = hash;
+        }
+        assert hash == -1 || hash == computeHashCode() : this;
+        return hash;
+    }
+
+    /**
+     * Computes a hash value for this identified object.
+     * This method is invoked by {@link #hashCode()} when first needed.
+     *
+     * <p>The default implementation computes a code derived from the list of {@link IdentifiedObject} interfaces
+     * implemented by this instance. The {@linkplain #getName() name}, {@linkplain #getIdentifiers() identifiers}
+     * and {@linkplain #getRemarks() remarks} are intentionally <strong>not</strong> used for hash code computation.
+     * See the <cite>Implementation specific feature</cite> section in {@link #hashCode()} for more information.</p>
+     *
+     * @return The hash code value. This value may change between different execution of the Apache SIS library.
+     */
+    protected int computeHashCode() {
+        // Subclasses need to overrides this!!!!
+        int code = (int) serialVersionUID;
+        for (final Class<?> type : Classes.getLeafInterfaces(getClass(), IdentifiedObject.class)) {
+            // Use a plain addition in order to be insensitive to array element order.
+            code += type.hashCode();
+        }
+        return code;
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -308,6 +308,8 @@ public final class IdentifiedObjects ext
      */
     public static boolean nameMatches(final IdentifiedObject object, final String name) {
         if (object instanceof AbstractIdentifiedObject) {
+            // DefaultCoordinateSystemAxis overrides this method.
+            // We really need to delegate to the overridden method.
             return ((AbstractIdentifiedObject) object).nameMatches(name);
         } else {
             ensureNonNull("object", object);

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -26,10 +26,16 @@ import org.junit.BeforeClass;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 @Suite.SuiteClasses({
+    org.apache.sis.io.wkt.ConventionTest.class,
+    org.apache.sis.io.wkt.SymbolsTest.class,
+    org.apache.sis.io.wkt.FormatterTest.class,
+    org.apache.sis.referencing.NamedIdentifierTest.class,
+    org.apache.sis.referencing.AbstractIdentifiedObjectTest.class,
+    org.apache.sis.referencing.AbstractReferenceSystemTest.class,
     org.apache.sis.geometry.AbstractDirectPositionTest.class,
     org.apache.sis.geometry.GeneralDirectPositionTest.class,
     org.apache.sis.geometry.DirectPosition1DTest.class,
@@ -38,10 +44,7 @@ import org.junit.BeforeClass;
     org.apache.sis.geometry.GeneralEnvelopeTest.class,
     org.apache.sis.geometry.SubEnvelopeTest.class,
     org.apache.sis.geometry.ImmutableEnvelopeTest.class,
-    org.apache.sis.geometry.Envelope2DTest.class,
-    org.apache.sis.io.wkt.ConventionTest.class,
-    org.apache.sis.io.wkt.SymbolsTest.class,
-    org.apache.sis.io.wkt.FormatterTest.class
+    org.apache.sis.geometry.Envelope2DTest.class
 })
 public final strictfp class ReferencingTestSuite extends TestSuite {
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java?rev=1519087&r1=1519086&r2=1519087&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] Fri Aug 30 21:00:40 2013
@@ -49,7 +49,7 @@ import org.apache.sis.util.collection.Tr
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final class Supervisor extends StandardMBean implements SupervisorMBean, Localized {
@@ -60,6 +60,11 @@ public final class Supervisor extends St
     static final boolean ENABLED = true;
 
     /**
+     * The JMX object name for the {@code Supervisor} service.
+     */
+    public static final String NAME = "org.apache.sis:type=Supervisor";
+
+    /**
      * The JMX object name, created when the {@link #register()} is first invoked.
      * {@link ObjectName#WILDCARD} is used as a sentinel value if the registration failed.
      */
@@ -79,7 +84,7 @@ public final class Supervisor extends St
             name = ObjectName.WILDCARD; // In case of failure.
             final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
             try {
-                final ObjectName n = new ObjectName("org.apache.sis:type=Supervisor");
+                final ObjectName n = new ObjectName(NAME);
                 server.registerMBean(new Supervisor(null, null), n);
                 name = n; // Store only on success.
             } catch (Exception e) { // (SecurityException | JMException) on the JDK7 branch.



Mime
View raw message