sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1548777 [4/6] - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/geometry/ core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-me...
Date Sat, 07 Dec 2013 02:00:45 GMT
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultPrimeMeridianTest.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -16,7 +16,17 @@
  */
 package org.apache.sis.referencing.datum;
 
-import org.apache.sis.test.TestCase;
+import javax.measure.unit.NonSI;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.JAXBException;
+import org.apache.sis.xml.XML;
+import org.apache.sis.xml.Namespaces;
+import org.apache.sis.xml.MarshallerPool;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
 import static org.apache.sis.referencing.Assert.*;
@@ -31,13 +41,112 @@ import static org.apache.sis.test.mock.P
  * @version 0.4
  * @module
  */
-public final strictfp class DefaultPrimeMeridianTest extends TestCase {
+@DependsOn(org.apache.sis.referencing.AbstractIdentifiedObjectTest.class)
+public final strictfp class DefaultPrimeMeridianTest extends DatumTestCase {
     /**
      * Tests {@link DefaultPrimeMeridian#toWKT()}.
      */
     @Test
     public void testToWKT() {
         final DefaultPrimeMeridian pm = new DefaultPrimeMeridian(GREENWICH);
+        assertIsGreenwichMeridian(pm);
         assertWktEquals(pm, "PRIMEM[“Greenwich”, 0.0]");
     }
+
+    /**
+     * Returns a XML representation of Greenwich prime meridian using the given GML namespace URI.
+     *
+     * @param  namespace The GML namespace.
+     * @return XML representation of Greenwich prime meridian.
+     */
+    private static String getGreenwichXml(final String namespace) {
+        return CharSequences.replace(
+                "<gml:PrimeMeridian xmlns:gml=\"" + Namespaces.GML + "\">\n" +
+                "  <gml:name codeSpace=\"test\">Greenwich</gml:name>\n" +
+                "  <gml:greenwichLongitude uom=\"urn:ogc:def:uom:EPSG::9102\">0.0</gml:greenwichLongitude>\n" +
+                "</gml:PrimeMeridian>\n",
+                Namespaces.GML, namespace).toString();
+    }
+
+    /**
+     * Tests marshalling in the default namespace.
+     *
+     * @throws JAXBException If an error occurred during marshalling.
+     */
+    @Test
+    public void testMarshall() throws JAXBException {
+        final DefaultPrimeMeridian pm = new DefaultPrimeMeridian(GREENWICH);
+        assertXmlEquals(getGreenwichXml(Namespaces.GML), marshal(pm), "xmlns:*", "xsi:schemaLocation");
+    }
+
+    /**
+     * Tests marshalling in the GML 3.1 namespace.
+     *
+     * @throws JAXBException If an error occurred during marshalling.
+     */
+    @Test
+    @DependsOnMethod("testMarshall")
+    public void testMarshallGML31() throws JAXBException {
+        final DefaultPrimeMeridian pm = new DefaultPrimeMeridian(GREENWICH);
+        final MarshallerPool pool = getMarshallerPool();
+        final Marshaller marshaller = pool.acquireMarshaller();
+        marshaller.setProperty(XML.GML_VERSION, LegacyNamespaces.VERSION_3_0);
+        final String xml = marshal(marshaller, pm);
+        pool.recycle(marshaller);
+        assertXmlEquals(getGreenwichXml(LegacyNamespaces.GML), xml, "xmlns:*", "xsi:schemaLocation");
+    }
+
+    /**
+     * Tests unmarshalling.
+     *
+     * @throws JAXBException If an error occurred during unmarshalling.
+     *
+     * @see <a href="http://epsg-registry.org/export.htm?gml=urn:ogc:def:meridian:EPSG::8901">GML export of EPSG:8901</a>
+     */
+    @Test
+    public void testUnmarshall() throws JAXBException {
+        final DefaultPrimeMeridian pm = unmarshall(DefaultPrimeMeridian.class, "Greenwich.xml");
+        assertIsGreenwichMeridian(pm);
+    }
+
+    /**
+     * Tests marshalling in the GML 3.1 namespace.
+     *
+     * @throws JAXBException If an error occurred during unmarshalling.
+     */
+    @Test
+    @DependsOnMethod("testUnmarshall")
+    public void testUnarshallGML31() throws JAXBException {
+        final MarshallerPool pool = getMarshallerPool();
+        final Unmarshaller unmarshaller = pool.acquireUnmarshaller();
+        unmarshaller.setProperty(XML.GML_VERSION, LegacyNamespaces.VERSION_3_0);
+        final DefaultPrimeMeridian pm = (DefaultPrimeMeridian)
+                unmarshal(unmarshaller, getGreenwichXml(LegacyNamespaces.GML));
+        pool.recycle(unmarshaller);
+        assertIsGreenwichMeridian(pm);
+    }
+
+    /**
+     * Tests unmarshalling of Paris prime meridian.
+     *
+     * @throws JAXBException If an error occurred during unmarshalling.
+     */
+    @Test
+    @DependsOnMethod({"testUnmarshall", "testMarshall"})
+    public void testParisMeridian() throws JAXBException {
+        final DefaultPrimeMeridian pm = unmarshall(DefaultPrimeMeridian.class, "Paris.xml");
+        assertIsParisMeridian(pm);
+        assertEquals("greenwichLongitude", 2.33722917, pm.getGreenwichLongitude(NonSI.DEGREE_ANGLE), 1E-12);
+        assertEquals("Equivalent to 2°20′14.025″.", pm.getRemarks().toString());
+        assertNull("name.codeSpace", pm.getName().getCodeSpace());
+        assertWktEquals(pm, "PRIMEM[“Paris”, 2.33722917, AUTHORITY[“OGP”, “urn:ogc:def:meridian:EPSG::8903”]]");
+        assertXmlEquals(
+                "<gml:PrimeMeridian xmlns:gml=\"" + Namespaces.GML + "\">\n" +
+                "  <gml:identifier codeSpace=\"OGP\">urn:ogc:def:meridian:EPSG::8903</gml:identifier>" +
+                "  <gml:name>Paris</gml:name>\n" +
+                "  <gml:remarks>Equivalent to 2°20′14.025″.</gml:remarks>\n" +
+                "  <gml:greenwichLongitude uom=\"urn:ogc:def:uom:EPSG::9105\">2.5969213</gml:greenwichLongitude>\n" +
+                "</gml:PrimeMeridian>\n",
+                marshal(pm), "xmlns:*", "xsi:schemaLocation");
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/DefaultVerticalDatumTest.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -20,7 +20,6 @@ import java.lang.reflect.Field;
 import org.opengis.referencing.datum.VerticalDatumType;
 import org.apache.sis.internal.referencing.VerticalDatumTypes;
 import org.apache.sis.test.DependsOn;
-import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.apache.sis.referencing.Assert.*;
@@ -36,7 +35,7 @@ import static java.util.Collections.sing
  * @module
  */
 @DependsOn(org.apache.sis.internal.referencing.VerticalDatumTypesTest.class)
-public final strictfp class DefaultVerticalDatumTest extends TestCase {
+public final strictfp class DefaultVerticalDatumTest extends DatumTestCase {
     /**
      * Tests the {@link DefaultVerticalDatum#getVerticalDatumType()} method in a state
      * simulating unmarshalling of GML 3.2 document.

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=1548777&r1=1548776&r2=1548777&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] Sat Dec  7 02:00:42 2013
@@ -43,9 +43,11 @@ import org.junit.BeforeClass;
 
     org.apache.sis.internal.referencing.FormulasTest.class,
     org.apache.sis.internal.referencing.VerticalDatumTypesTest.class,
+    org.apache.sis.internal.referencing.AxisDirectionsTest.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.referencing.IdentifiedObjectsTest.class,
     org.apache.sis.referencing.NamedIdentifierTest.class,
     org.apache.sis.referencing.AbstractIdentifiedObjectTest.class,
     org.apache.sis.referencing.AbstractReferenceSystemTest.class,
@@ -55,6 +57,10 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.datum.DefaultPrimeMeridianTest.class,
     org.apache.sis.referencing.datum.DefaultVerticalDatumTest.class,
     org.apache.sis.referencing.datum.DefaultGeodeticDatumTest.class,
+    org.apache.sis.referencing.cs.DirectionAlongMeridianTest.class,
+    org.apache.sis.referencing.cs.DefaultCoordinateSystemAxisTest.class,
+    org.apache.sis.referencing.cs.DefaultCartesianCSTest.class,
+    org.apache.sis.referencing.cs.CoordinateSystemsTest.class,
     org.apache.sis.referencing.StandardDefinitionsTest.class,
     org.apache.sis.referencing.GeodeticObjectsTest.class,
 

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -204,8 +204,9 @@ public final class Context extends Marsh
     }
 
     /**
-     * Returns the locale to use for marshalling, or {@code null} if no locale were explicitly
-     * specified.
+     * Returns the locale to use for marshalling, or {@code null} if no locale were explicitly specified.
+     *
+     * @return The locale in the context of current (un)marshalling process.
      */
     @Override
     public final Locale getLocale() {
@@ -213,8 +214,9 @@ public final class Context extends Marsh
     }
 
     /**
-     * Returns the timezone to use for marshalling, or {@code null} if none were explicitely
-     * specified.
+     * Returns the timezone to use for marshalling, or {@code null} if none were explicitely specified.
+     *
+     * @return The timezone in the context of current (un)marshalling process.
      */
     @Override
     public final TimeZone getTimeZone() {
@@ -224,6 +226,8 @@ public final class Context extends Marsh
     /**
      * Returns the schema version of the XML document being (un)marshalled.
      * See the super-class javadoc for the list of prefix that we shall support.
+     *
+     * @return The version in the context of current (un)marshalling process.
      */
     @Override
     public final Version getVersion(final String prefix) {

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/TypeRegistration.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -49,11 +49,13 @@ public abstract class TypeRegistration {
     /**
      * The JAXB context, or {@code null} if not yet created or if the classpath changed.
      */
-    private static volatile Reference<JAXBContext> context;
+    private static Reference<JAXBContext> context;
     static {
         SystemListener.add(new SystemListener(Modules.UTILITIES) {
             @Override protected void classpathChanged() {
-                context = null;
+                synchronized (TypeRegistration.class) {
+                    context = null;
+                }
             }
         });
     }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Distance.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Distance.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Distance.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Distance.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -48,12 +48,17 @@ public final class GO_Distance extends X
     }
 
     /**
-     * Constructs an adapter for the given value.
+     * Constructs an adapter for the given value before marshalling.
      *
      * @param value The value.
+     *
+     * @todo The unit of measurement is fixed to metres for now because we do not have this information
+     *       in current metadata interface. This will need to be revisited in a future SIS version if we
+     *       replace the Double type by some quantity type.
      */
     private GO_Distance(final Double value) {
         distance = new Measure(value, SI.METRE);
+        distance.asXPointer = true;
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Measure.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Measure.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Measure.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Measure.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -48,12 +48,17 @@ public final class GO_Measure extends Xm
     }
 
     /**
-     * Constructs an adapter for the given value.
+     * Constructs an adapter for the given value before marshalling.
      *
      * @param value The value.
+     *
+     * @todo The unit of measurement is fixed to metres for now because we do not have this information
+     *       in current metadata interface. This will need to be revisited in a future SIS version if we
+     *       replace the Double type by some quantity type.
      */
     private GO_Measure(final Double value) {
         measure = new Measure(value, SI.METRE);
+        measure.asXPointer = true;
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -16,18 +16,16 @@
  */
 package org.apache.sis.internal.jaxb.gco;
 
-import java.io.File;
-import java.net.URI;
 import java.net.URISyntaxException;
 import javax.measure.unit.Unit;
 import javax.measure.unit.NonSI;
+import javax.measure.quantity.Quantity;
 import javax.xml.bind.annotation.XmlValue;
 import javax.xml.bind.annotation.XmlAttribute;
 import org.apache.sis.internal.jaxb.gmd.CodeListProxy;
 import org.apache.sis.internal.jaxb.Context;
-import org.apache.sis.xml.ValueConverter;
-import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.measure.Units;
 
 
 /**
@@ -38,16 +36,33 @@ import org.apache.sis.util.resources.Err
  * because that{@code Measure} extends {@link Number} and we are not allowed to use the
  * {@code @XmlValue} annotation on a class that extends an other class.</p>
  *
+ * {@section XML marshalling}
+ * Measures are used in different ways by the ISO 19115 (Metadata) and GML standards.
+ * The former expresses some measurements with an object of XML type {@code gco:Distance}
+ * (as a substitution for XML type {@code gco:Measure}):
+ *
+ * {@preformat xml
+ *   <gmd:distance>
+ *     <gco:Distance uom=\"http://schemas.opengis.net/iso/19139/20070417/resources/uom/gmxUom.xml#xpointer(//*[@gml:id='m'])\">1000.0</gco:Distance>
+ *   </gmd:distance>
+ * }
+ *
+ * while GML will rather use a a syntax like below:
+ *
+ * {@preformat xml
+ *   <gml:semiMajorAxis uom="urn:ogc:def:uom:EPSG::9001">6378137</gml:semiMajorAxis>
+ * }
+ *
+ * Both have a value of type {@code xsd:double} and a {@code uom} attribute (without namespace)
+ * of type {@code gml:UomIdentifier}. Those two informations are represented by this class.
+ *
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see org.apache.sis.measure.Measure
- *
- * @todo We should annotate {@link org.apache.sis.measure.Measure} directly
- *       if we can find some way to use {@code @XmlValue} with that class.
  */
 public final class Measure {
     /**
@@ -62,6 +77,12 @@ public final class Measure {
     public Unit<?> unit;
 
     /**
+     * {@code true} if the units shall be formatted as {@code xpointer}.
+     * If {@code false} (the default), then this class will try to format the units using the GML syntax.
+     */
+    boolean asXPointer;
+
+    /**
      * Default empty constructor for JAXB. The value is initialized to NaN,
      * but JAXB will overwrite this value if a XML value is presents.
      */
@@ -84,79 +105,77 @@ public final class Measure {
     /**
      * Constructs a string representation of the units as defined in the ISO-19103 standard.
      * This method is invoked during XML marshalling. For example in the units are "metre",
-     * then this method returns:
+     * then this method returns one of the following strings, in preference order:
+     *
+     * {@preformat text
+     *     urn:ogc:def:uom:EPSG::9001
+     * }
+     *
+     * or
      *
      * {@preformat text
      *     http://schemas.opengis.net/iso/19139/20070417/resources/uom/gmxUom.xml#xpointer(//*[@gml:id='m'])
      * }
      *
      * @return The string representation of the unit of measure.
+     *
+     * @todo The file on schemas.opengis.net is <code>gmxUom.xml</code>, but the file on standards.iso.org
+     *       and eden.ign.fr is <code>ML_gmxUom.xml</code>. Is there some rule allowing us to know which
+     *       filename to use?
+     *
+     * @todo Strictly speaking, the above URL should be used only for "m", "deg" and "rad" units because they
+     *       are the only ones defined in the <code>gmxUom.xml</code> file. What should we do for other units?
      */
     @XmlAttribute(required = true)
     public String getUOM() {
         final Unit<?> unit = this.unit;
-        final String symbol;
         if (unit == null || unit.equals(Unit.ONE)) {
-            symbol = "";
-        } else if (unit.equals(NonSI.PIXEL)) {
-            symbol = "pixel";
-        } else {
-            symbol = Context.schema(Context.current(), "gmd", CodeListProxy.DEFAULT_SCHEMA)
-                    .append("resources/uom/gmxUom.xml#xpointer(//*[@gml:id='").append(unit).append("'])").toString();
+            return "";
+        }
+        if (unit.equals(NonSI.PIXEL)) {
+            return "pixel"; // TODO: maybe not the most appropriate unit.
+        }
+        if (!asXPointer) {
+            final Integer code = Units.getEpsgCode(unit);
+            if (code != null) {
+                return "urn:ogc:def:uom:EPSG::" + code;
+            }
         }
-        return symbol;
+        return Context.schema(Context.current(), "gmd", CodeListProxy.DEFAULT_SCHEMA)
+                .append("resources/uom/gmxUom.xml#xpointer(//*[@gml:id='").append(unit).append("'])").toString();
     }
 
     /**
-     * Sets the unit of measure. This method is invoked by JAXB at unmarshalling time,
-     * and can be invoked only once.
+     * Sets the unit of measure. This method is invoked by JAXB at unmarshalling time.
      *
      * @param uom The unit of measure as a string.
      * @throws URISyntaxException If the {@code uom} looks like a URI, but can not be parsed.
      */
     public void setUOM(String uom) throws URISyntaxException {
-        if (uom == null || (uom = CharSequences.trimWhitespaces(uom)).isEmpty()) {
-            unit = null;
-            return;
-        }
-        /*
-         * Try to guess if the UOM is a URN or URL. We looks for character that are usually
-         * part of URI but not part of unit symbols, for example ':'. We can not search for
-         * '/' and '.' since they are part of UCUM representation.
-         */
         final Context context = Context.current();
-        final ValueConverter converter = Context.converter(context);
-        if (uom.indexOf(':') >= 0) {
-            final URI uri = converter.toURI(context, uom);
-            String part = uri.getFragment();
-            if (part != null) {
-                uom = part;
-                int i = uom.lastIndexOf("@gml:id=");
-                if (i >= 0) {
-                    i += 8; // 8 is the length of "@gml:id="
-                    for (final int length=uom.length(); i<length;) {
-                        final int c = uom.codePointAt(i);
-                        if (!Character.isWhitespace(c)) {
-                            if (c == '\'') i++;
-                            break;
-                        }
-                        i += Character.charCount(c);
-                    }
-                    final int stop = uom.lastIndexOf('\'');
-                    uom = CharSequences.trimWhitespaces((stop > i) ? uom.substring(i, stop) : uom.substring(i));
-                }
-            } else if ((part = uri.getPath()) != null) {
-                uom = new File(part).getName();
-            }
-        }
-        unit = converter.toUnit(context, uom);
+        unit = Context.converter(context).toUnit(context, uom);
+    }
+
+    /**
+     * Returns {@link #unit} as a unit compatible with the given quantity.
+     *
+     * @todo For now, this method does not format useful error message in case of missing unit or wrong unit type.
+     *       We define this method merely as a placeholder for future improvement in error handling.
+     *
+     * @param  <Q>  Compile-time type of the {@code type} argument.
+     * @param  type The quantity for the desired unit.
+     * @return A unit compatible with the given type.
+     */
+    public <Q extends Quantity> Unit<Q> getUnit(final Class<Q> type) {
+        return unit.asType(type);
     }
 
     /**
-     * Sets the unit to the given value, with a warning logged if the user specified an other unit.
+     * Sets the unit to the given value, with a warning logged if the user specified a unit
+     * different than the previous {@link #unit} value.
      *
      * {@example Some users wrongly assign the "m" unit to <code>Ellipsoid.inverseFlattening</code>.
-     *           The SIS adapter force the unit to <code>Unit.ONE</code>, but we want to let the user
+     *           The SIS adapter forces the unit to <code>Unit.ONE</code>, but we want to let the user
      *           know that he probably did something wrong.}
      *
      * @param newUnit The new unit (can not be null).

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -255,12 +255,15 @@ public final class Citations extends Sta
      *   <li>Otherwise this method returns {@code null}.</li>
      * </ul>
      *
-     * This method searches in alternate titles as a fallback because ISO specification said
-     * that those titles are often used for abbreviations.
+     * {@note This method searches in alternate titles as a fallback because ISO specification said
+     *        that those titles are often used for abbreviations.}
+     *
+     * This method ignores leading and trailing whitespaces of every character sequences.
+     * Null references, empty character sequences and sequences of whitespaces only are ignored.
      *
      * @param  citation The citation for which to get the identifier, or {@code null}.
-     * @return An identifier for the given citation, or {@code null} if the given citation is null
-     *         or does not declare any identifier or title.
+     * @return A non-empty identifier for the given citation without leading or trailing whitespaces,
+     *         or {@code null} if the given citation is null or does not declare any identifier or title.
      */
     public static String getIdentifier(final Citation citation) {
         boolean isUnicode = false; // Whether 'identifier' is a Unicode identifier.
@@ -289,7 +292,7 @@ public final class Citations extends Sta
              * often used for abbreviations.
              */
             if (identifier == null) {
-                identifier = toString(citation.getTitle());
+                identifier = toString(citation.getTitle()); // Whitepaces removed by toString(…).
                 if (identifier != null) {
                     if (identifier.isEmpty()) {
                         identifier = null;

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/SexagesimalConverter.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -18,7 +18,9 @@ package org.apache.sis.measure;
 
 import java.math.BigDecimal;
 import java.math.MathContext;
-import java.io.ObjectStreamException;
+import javax.measure.unit.Unit;
+import javax.measure.unit.NonSI;
+import javax.measure.quantity.Angle;
 import javax.measure.converter.UnitConverter;
 import org.apache.sis.util.Immutable;
 import org.apache.sis.util.resources.Errors;
@@ -56,21 +58,73 @@ class SexagesimalConverter extends UnitC
     static final double EPS = 1E-10;
 
     /**
-     * The converter for DMS units.
+     * Pseudo-unit for sexagesimal degree. Numbers in this pseudo-unit have the following format:
+     *
+     * <cite>sign - degrees - decimal point - minutes (two digits) - fraction of minutes (any precision)</cite>.
+     *
+     * Using this unit is loosely equivalent to formatting decimal degrees with the
+     * {@code "D.MMm"} {@link AngleFormat} pattern.
+     *
+     * <p>This unit is non-linear and not practical for computation. Consequently, it should be
+     * avoided as much as possible. This pseudo-unit is defined only because used in the EPSG
+     * database (code 9111).</p>
+     *
+     * <p>This unit does not have an easily readable symbol because of the
+     * <a href="http://kenai.com/jira/browse/JSR_275-41">JSR-275 bug</a>.</p>
+     */
+    static final Unit<Angle> DM = NonSI.DEGREE_ANGLE.transform(
+            new SexagesimalConverter(false, 100).inverse()).asType(Angle.class);//.alternate("D.M");
+
+    /**
+     * Pseudo-unit for sexagesimal degree. Numbers in this pseudo-unit have the following format:
+     *
+     * <cite>sign - degrees - decimal point - minutes (two digits) - integer seconds (two digits) -
+     * fraction of seconds (any precision)</cite>.
+     *
+     * Using this unit is loosely equivalent to formatting decimal degrees with the
+     * {@code "D.MMSSs"} {@link AngleFormat} pattern.
+     *
+     * <p>This unit is non-linear and not practical for computation. Consequently, it should be
+     * avoided as much as possible. This pseudo-unit is defined only because extensively used in
+     * the EPSG database (code 9110).</p>
+     *
+     * <p>This unit does not have an easily readable symbol because of the
+     * <a href="http://kenai.com/jira/browse/JSR_275-41">JSR-275 bug</a>.</p>
+     */
+    static final Unit<Angle> DMS = NonSI.DEGREE_ANGLE.transform(
+            new SexagesimalConverter(true, 10000).inverse()).asType(Angle.class);//.alternate("D.MS");
+
+    /**
+     * Pseudo-unit for degree - minute - second.
+     * Numbers in this pseudo-unit have the following format:
+     *
+     * <cite>signed degrees (integer) - arc-minutes (integer) - arc-seconds
+     * (real, any precision)</cite>.
+     *
+     * Using this unit is loosely equivalent to formatting decimal degrees with the
+     * {@code "DMMSS.s"} {@link AngleFormat} pattern.
+     *
+     * <p>This unit is non-linear and not practical for computation. Consequently, it should be
+     * avoided as much as possible. This pseudo-unit is defined only because extensively used in
+     * EPSG database (code 9107).</p>
+     *
+     * <p>This unit does not have an easily readable symbol because of the
+     * <a href="http://kenai.com/jira/browse/JSR_275-41">JSR-275 bug</a>.</p>
      */
-    static final SexagesimalConverter INTEGER = new SexagesimalConverter(1);
+    static final Unit<Angle> DMS_SCALED = NonSI.DEGREE_ANGLE.transform(
+            new SexagesimalConverter(true, 1).inverse()).asType(Angle.class);//.alternate("DMS");
 
     /**
-     * The converter for D.MS units.
+     * {@code true} if the seconds field is present.
      */
-    static final SexagesimalConverter FRACTIONAL = new SexagesimalConverter(10000);
+    final boolean hasSeconds;
 
     /**
      * The value to divide DMS unit by.
      * For "degree minute second" (EPSG code 9107), this is 1.
      * For "sexagesimal degree" (EPSG code 9110), this is 10000.
      */
-    final int divider;
+    final double divider;
 
     /**
      * The inverse of this converter.
@@ -80,22 +134,25 @@ class SexagesimalConverter extends UnitC
     /**
      * Constructs a converter for sexagesimal units.
      *
+     * @param hasSeconds {@code true} if the seconds field is present.
      * @param divider The value to divide DMS unit by.
      *        For "degree minute second" (EPSG code 9107), this is 1.
      *        For "sexagesimal degree" (EPSG code 9110), this is 10000.
      */
-    private SexagesimalConverter(final int divider) {
-        this.divider = divider;
-        this.inverse = new Inverse(this);
+    private SexagesimalConverter(final boolean hasSeconds, final double divider) {
+        this.hasSeconds = hasSeconds;
+        this.divider    = divider;
+        this.inverse    = new Inverse(this);
     }
 
     /**
      * Constructs a converter for sexagesimal units.
      * This constructor is for {@link Inverse} usage only.
      */
-    private SexagesimalConverter(final int divider, final UnitConverter inverse) {
-        this.divider = divider;
-        this.inverse = inverse;
+    private SexagesimalConverter(final SexagesimalConverter inverse) {
+        this.hasSeconds = inverse.hasSeconds;
+        this.divider    = inverse.divider;
+        this.inverse    = inverse;
     }
 
     /**
@@ -111,11 +168,16 @@ class SexagesimalConverter extends UnitC
      */
     @Override
     public double convert(double angle) {
-        final double deg,min,sec;  deg = truncate(angle);
-        angle = (angle-deg)*60;    min = truncate(angle);
-        angle = (angle-min)*60;    sec = truncate(angle);
-        angle -= sec; // The remainer (fraction of seconds)
-        return (((deg*100 + min)*100 + sec) + angle) / divider;
+        final double deg = truncate(angle);
+        angle = (angle - deg) * 60;
+        if (hasSeconds) {
+            final double min = truncate(angle);
+            angle  = (angle - min) * 60; // Secondes
+            angle += (deg*100 + min)*100;
+        } else {
+            angle += deg * 100;
+        }
+        return angle / divider;
     }
 
     /**
@@ -141,27 +203,8 @@ class SexagesimalConverter extends UnitC
      * Returns a hash value for this converter.
      */
     @Override
-    public int hashCode() {
-        return divider ^ (int) serialVersionUID;
-    }
-
-    /**
-     * On deserialization, returns an existing instance.
-     */
-    protected final Object readResolve() throws ObjectStreamException {
-        UnitConverter candidate = INTEGER;
-        for (int i=0; i<4; i++) {
-            switch (i) {
-                case 0:  break; // Do nothing since candidate is already set to INTEGER.
-                case 1:  // Fallthrough
-                case 3:  candidate = candidate.inverse(); break;
-                case 2:  candidate = FRACTIONAL; break;
-            }
-            if (equals(candidate)) {
-                return candidate;
-            }
-        }
-        return this;
+    public final int hashCode() {
+        return ((int) divider) ^ getClass().hashCode();
     }
 
     /**
@@ -178,7 +221,7 @@ class SexagesimalConverter extends UnitC
          * Constructs a converter.
          */
         public Inverse(final SexagesimalConverter inverse) {
-            super(inverse.divider, inverse);
+            super(inverse);
         }
 
         /**
@@ -189,9 +232,16 @@ class SexagesimalConverter extends UnitC
         @Override
         public double convert(final double angle) throws IllegalArgumentException {
             double deg,min,sec;
-            sec = angle * divider;
-            deg = truncate(sec/10000); sec -= 10000*deg;
-            min = truncate(sec/  100); sec -=   100*min;
+            if (hasSeconds) {
+                sec = angle * divider;
+                deg = truncate(sec/10000); sec -= 10000*deg;
+                min = truncate(sec/  100); sec -=   100*min;
+            } else {
+                sec = 0;
+                min = angle * divider;
+                deg = truncate(min / 100);
+                min -= deg * 100;
+            }
             if (min <= -60 || min >= 60) {  // Do not enter for NaN
                 if (Math.abs(Math.abs(min) - 100) <= (EPS * 100)) {
                     if (min >= 0) deg++; else deg--;
@@ -222,13 +272,5 @@ class SexagesimalConverter extends UnitC
         private static IllegalArgumentException illegalField(final double value, final double field, final int unit) {
             return new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentField_4, "angle", value, unit, field));
         }
-
-        /**
-         * Returns a hash value for this converter.
-         */
-        @Override
-        public int hashCode() {
-            return divider ^ (int) serialVersionUID;
-        }
     }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -16,10 +16,6 @@
  */
 package org.apache.sis.measure;
 
-import java.util.Map;
-import java.util.HashMap;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
 import javax.measure.unit.SI;
 import javax.measure.unit.NonSI;
 import javax.measure.unit.Unit;
@@ -35,6 +31,7 @@ import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.URIParser;
 
 import static java.lang.Math.PI;
 import static java.lang.Math.abs;
@@ -48,7 +45,7 @@ import static org.apache.sis.util.CharSe
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.1)
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final class Units extends Static {
@@ -71,45 +68,6 @@ public final class Units extends Static 
     public static final Unit<Duration> MILLISECOND = SI.MetricPrefix.MILLI(SI.SECOND);
 
     /**
-     * Pseudo-unit for sexagesimal degree. Numbers in this pseudo-unit have the following format:
-     *
-     * <cite>sign - degrees - decimal point - minutes (two digits) - integer seconds (two digits) -
-     * fraction of seconds (any precision)</cite>.
-     *
-     * Using this unit is loosely equivalent to formatting decimal degrees with the
-     * {@code "D.MMSSs"} {@link AngleFormat} pattern.
-     *
-     * <p>This unit is non-linear and not practical for computation. Consequently, it should be
-     * avoided as much as possible. This pseudo-unit is defined only because extensively used in
-     * the EPSG database (code 9110).</p>
-     *
-     * <p>This unit does not have an easily readable symbol because of the
-     * <a href="http://kenai.com/jira/browse/JSR_275-41">JSR-275 bug</a>.</p>
-     */
-    static final Unit<Angle> SEXAGESIMAL_DMS = NonSI.DEGREE_ANGLE.transform(
-            SexagesimalConverter.FRACTIONAL.inverse()).asType(Angle.class);//.alternate("D.MS");
-
-    /**
-     * Pseudo-unit for degree - minute - second.
-     * Numbers in this pseudo-unit have the following format:
-     *
-     * <cite>signed degrees (integer) - arc-minutes (integer) - arc-seconds
-     * (real, any precision)</cite>.
-     *
-     * Using this unit is loosely equivalent to formatting decimal degrees with the
-     * {@code "DMMSS.s"} {@link AngleFormat} pattern.
-     *
-     * <p>This unit is non-linear and not practical for computation. Consequently, it should be
-     * avoided as much as possible. This pseudo-unit is defined only because extensively used in
-     * EPSG database (code 9107).</p>
-     *
-     * <p>This unit does not have an easily readable symbol because of the
-     * <a href="http://kenai.com/jira/browse/JSR_275-41">JSR-275 bug</a>.</p>
-     */
-    static final Unit<Angle> DEGREE_MINUTE_SECOND = NonSI.DEGREE_ANGLE.transform(
-            SexagesimalConverter.INTEGER.inverse()).asType(Angle.class);//.alternate("DMS");
-
-    /**
      * Parts per million.
      *
      * <p>This unit does not have an easily readable symbol because of the
@@ -118,33 +76,6 @@ public final class Units extends Static 
     public static final Unit<Dimensionless> PPM = Unit.ONE.times(1E-6);//.alternate("ppm");
 
     /**
-     * A few units commonly used in GIS.
-     */
-    private static final Map<Unit<?>,Unit<?>> COMMONS = new HashMap<Unit<?>,Unit<?>>(48);
-    static {
-        COMMONS.put(PPM, PPM);
-        boolean nonSI = false;
-        do for (final Field field : (nonSI ? NonSI.class : SI.class).getFields()) {
-            final int modifiers = field.getModifiers();
-            if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
-                final Object value;
-                try {
-                    value = field.get(null);
-                } catch (Exception e) { // (ReflectiveOperationException) on JDK7
-                    // Should not happen since we asked only for public static constants.
-                    throw new AssertionError(e);
-                }
-                if (value instanceof Unit<?>) {
-                    final Unit<?> unit = (Unit<?>) value;
-                    if (isLinear(unit) || isAngular(unit) || isScale(unit)) {
-                        COMMONS.put(unit, unit);
-                    }
-                }
-            }
-        } while ((nonSI = !nonSI) == true);
-    }
-
-    /**
      * Returns {@code true} if the given unit is a linear unit.
      * Linear units are convertible to {@link NonSI#DEGREE_ANGLE}.
      *
@@ -327,23 +258,7 @@ public final class Units extends Static 
                 unit = unit.times(factor);
             }
         }
-        return canonicalize(unit);
-    }
-
-    /**
-     * Returns a unique instance of the given units if possible, or the units unchanged otherwise.
-     *
-     * @param  <A>    The quantity measured by the unit.
-     * @param  unit   The unit to canonicalize.
-     * @return A unit equivalents to the given unit, canonicalized if possible.
-     */
-    @SuppressWarnings({"unchecked","rawtypes"})
-    private static <A extends Quantity> Unit<A> canonicalize(final Unit<A> unit) {
-        final Unit<?> candidate = COMMONS.get(unit);
-        if (candidate != null) {
-            return (Unit) candidate;
-        }
-        return unit;
+        return UnitsMap.canonicalize(unit);
     }
 
     /**
@@ -397,8 +312,8 @@ public final class Units extends Static 
      *
      * {@section Parsing authority codes}
      * As a special case, if the given {@code uom} arguments is of the form {@code "EPSG:####"}
-     * (ignoring case and whitespaces), then {@code "####"} is parsed as an integer and forwarded
-     * to the {@link #valueOfEPSG(int)} method.
+     * or {@code "urn:ogc:def:uom:EPSG:####"} (ignoring case and whitespaces), then {@code "####"}
+     * is parsed as an integer and forwarded to the {@link #valueOfEPSG(int)} method.
      *
      * {@section NetCDF unit symbols}
      * The attributes in NetCDF files often merge the axis direction with the angular unit,
@@ -422,16 +337,24 @@ public final class Units extends Static 
          * Check for authority codes (currently only EPSG, but more could be added later).
          * If the unit is not an authority code (which is the most common case), then we
          * will check for hard-coded unit symbols.
+         *
+         * URIParser.codeOf(…) returns 'uom' directly (provided that whitespaces were already trimmed)
+         * if no ':' character were found, in which case the string is assumed to be the code directly.
+         * This is the intended behavior for AuthorityFactory, but in the particular case of this method
+         * we want to try to parse as a xpointer before to give up.
          */
-        int s = uom.indexOf(':');
-        if (s >= 0) {
-            final String authority = (String) trimWhitespaces(uom, 0, s);
-            if (authority.equalsIgnoreCase("EPSG")) try {
-                return valueOfEPSG(Integer.parseInt((String) trimWhitespaces(uom, s+1, length)));
+        if (isURI(uom)) {
+            String code = URIParser.codeOf("uom", "EPSG", uom);
+            if (code != null && code != uom) try { // Really identity check, see above comment.
+                return valueOfEPSG(Integer.parseInt(code));
             } catch (NumberFormatException e) {
                 throw new IllegalArgumentException(Errors.format(
                         Errors.Keys.IllegalArgumentValue_2, "uom", uom), e);
             }
+            code = URIParser.xpointer("uom", uom);
+            if (code != null) {
+                uom = code;
+            }
         }
         /*
          * Check for degrees units. Note that "deg" could be both angular and Celsius degrees.
@@ -445,7 +368,7 @@ public final class Units extends Static 
             }
             String prefix = uom;
             boolean isTemperature = false;
-            s = Math.max(uom.lastIndexOf(' '), uom.lastIndexOf('_'));
+            final int s = Math.max(uom.lastIndexOf(' '), uom.lastIndexOf('_'));
             if (s >= 1) {
                 final String suffix = (String) trimWhitespaces(uom, s+1, length);
                 if (ArraysExt.containsIgnoreCase(DEGREE_SUFFIXES, suffix) || (isTemperature = isCelsius(suffix))) {
@@ -486,7 +409,7 @@ public final class Units extends Static 
             // Provides a better error message than the default JSR-275 0.9.4 implementation.
             throw Exceptions.setMessage(e, Errors.format(Errors.Keys.IllegalArgumentValue_2, "uom", uom), true);
         }
-        return canonicalize(unit);
+        return UnitsMap.canonicalize(unit);
     }
 
     /**
@@ -513,6 +436,23 @@ public final class Units extends Static 
     }
 
     /**
+     * Returns {@code true} if the given unit seems to be an URI. Example:
+     * <ul>
+     *   <li>{@code "urn:ogc:def:uom:EPSG::9102"}</li>
+     *   <li>{@code "http://schemas.opengis.net/iso/19139/20070417/resources/uom/gmxUom.xml#xpointer(//*[@gml:id='m'])"}</li>
+     * </ul>
+     */
+    private static boolean isURI(final String uom) {
+        for (int i=uom.length(); --i>=0;) {
+            final char c = uom.charAt(i);
+            if (c == ':' || c == '#') {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns a hard-coded unit from an EPSG code. The {@code code} argument given to this
      * method shall be a code identifying a record in the {@code "Unit of Measure"} table of
      * the EPSG database. If this method does not recognize the given code, then it returns
@@ -581,15 +521,28 @@ public final class Units extends Static 
             case 9103: return NonSI.MINUTE_ANGLE;
             case 9104: return NonSI.SECOND_ANGLE;
             case 9105: return NonSI.GRADE;
-            case 9107: return Units.DEGREE_MINUTE_SECOND;
-            case 9108: return Units.DEGREE_MINUTE_SECOND;
+            case 9107: // Fall through
+            case 9108: return SexagesimalConverter.DMS_SCALED;
             case 9109: return SI.MetricPrefix.MICRO(SI.RADIAN);
-            case 9111: // Sexagesimal DM: use DMS.
-            case 9110: return Units.SEXAGESIMAL_DMS;
+            case 9111: return SexagesimalConverter.DM;
+            case 9110: return SexagesimalConverter.DMS;
             case 9203: // Fall through
             case 9201: return Unit .ONE;
             case 9202: return Units.PPM;
             default:   return null;
         }
     }
+
+    /**
+     * Returns the EPSG code of the given units, or {@code null} if unknown.
+     * This method is the converse of {@link #valueOfEPSG(int)}.
+     *
+     * @param  unit The unit for which to get the EPSG code.
+     * @return The EPSG code of the given units, or {@code null} if unknown.
+     *
+     * @since 0.4
+     */
+    public static Integer getEpsgCode(final Unit<?> unit) {
+        return UnitsMap.EPSG_CODES.get(unit);
+    }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -109,7 +109,16 @@ public final class ArgumentChecks extend
     /**
      * Makes sure that an array element is non-null. If {@code element} is null, then a
      * {@link NullArgumentException} is thrown with a localized message containing the
-     * given name and index.
+     * given name and index. The name and index are formatted as below:
+     *
+     * <ul>
+     *   <li>If the {@code name} contains the {@code "[#]"} sequence of characters, then the {@code '#'} character
+     *       is replaced by the {@code index} value. For example if {@code name} is {@code "axes[#].unit"} and the
+     *       index is 2, then the formatted message will contain {@code "axes[2].unit"}.</li>
+     *   <li>If the {@code name} does not contain the {@code "[#]"} sequence of characters, then {@code index} value
+     *       is appended between square brackets. For example if {@code name} is {@code "axes"} and the index is 2,
+     *       then the formatted message will contain {@code "axes[2]"}.</li>
+     * </ul>
      *
      * @param  name    The name of the argument to be checked. Used only if an exception is thrown.
      * @param  index   The Index of the element to check in an array or a list. Used only if an exception is thrown.
@@ -120,8 +129,15 @@ public final class ArgumentChecks extend
             throws NullArgumentException
     {
         if (element == null) {
-            throw new NullArgumentException(Errors.format(
-                    Errors.Keys.NullArgument_1, name + '[' + index + ']'));
+            final StringBuilder buffer = new StringBuilder(name);
+            final int s = name.indexOf("[#]");
+            if (s >= 0) {
+                buffer.setLength(s + 1);
+                buffer.append(index).append(name, s + 2, name.length());
+            } else {
+                buffer.append('[').append(index).append(']');
+            }
+            throw new NullArgumentException(Errors.format(Errors.Keys.NullArgument_1, buffer.toString()));
         }
     }
 

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -20,7 +20,6 @@ import java.util.Arrays;
 import java.nio.CharBuffer;
 
 import static java.lang.Character.*;
-import static org.apache.sis.util.StringBuilders.replace;
 
 // Related to JDK7
 import static org.apache.sis.internal.jdk7.JDK7.lowSurrogate;
@@ -954,8 +953,8 @@ search:     for (; fromIndex <= toIndex;
      *
      * @param  text The text to scan for Unicode characters to replace by ASCII characters,
      *         or {@code null}.
-     * @return The given text with substitution applied, or {@code text} if no replacement
-     *         has been applied.
+     * @return The given text with substitutions applied, or {@code text} if no replacement
+     *         has been applied, or {@code null} if the given text was null.
      *
      * @see StringBuilders#toASCII(StringBuilder)
      */
@@ -1254,16 +1253,16 @@ searchWordBreak:    while (true) {
                 return ((String) identifier).replace('_', '-');
             }
             buffer = new StringBuilder(identifier);
-            replace(buffer, '_', '-');
+            StringBuilders.replace(buffer, '_', '-');
         } else {
             buffer = (StringBuilder) camelCaseToWords(identifier, true);
             final int length = buffer.length();
             if (length != 0) {
-                replace(buffer, '_', ' ');
+                StringBuilders.replace(buffer, '_', ' ');
                 final int c = buffer.codePointAt(0);
                 final int up = toUpperCase(c);
                 if (c != up) {
-                    replace(buffer, 0, charCount(c), toChars(up));
+                    StringBuilders.replace(buffer, 0, charCount(c), toChars(up));
                 }
             }
         }
@@ -1321,7 +1320,7 @@ searchWordBreak:    while (true) {
                     final int c = buffer.codePointAt(pos);
                     final int low = toLowerCase(c);
                     if (c != low) {
-                        replace(buffer, pos, pos + charCount(c), toChars(low));
+                        StringBuilders.replace(buffer, pos, pos + charCount(c), toChars(low));
                     }
                 }
                 last = i;
@@ -1390,7 +1389,7 @@ searchWordBreak:    while (true) {
                     final int c = buffer.codePointAt(0);
                     final int up = toUpperCase(c);
                     if (c != up) {
-                        replace(buffer, 0, charCount(c), toChars(up));
+                        StringBuilders.replace(buffer, 0, charCount(c), toChars(up));
                     }
                 }
                 if (!equals(text, buffer)) {
@@ -1565,8 +1564,8 @@ cmp:    while (ia < lga) {
     }
 
     /**
-     * Returns {@code true} if the given texts are equal, optionally ignoring case and filtered-out
-     * characters. This method is sometime used for comparing identifiers in a lenient way.
+     * Returns {@code true} if the given texts are equal, optionally ignoring case and filtered-out characters.
+     * This method is sometime used for comparing identifiers in a lenient way.
      *
      * <p><b>Example:</b> the following call compares the two strings ignoring case and any
      * characters which are not {@linkplain Character#isLetterOrDigit(int) letter or digit}.
@@ -1574,11 +1573,11 @@ cmp:    while (ia < lga) {
      * ignored:</p>
      *
      * {@preformat java
-     *     assert equals("WGS84", "WGS_84", Characters.Filter.LETTERS_AND_DIGITS, true) == true;
+     *     assert equalsFiltered("WGS84", "WGS_84", Characters.Filter.LETTERS_AND_DIGITS, true) == true;
      * }
      *
-     * @param  s1 The first string to compare, or {@code null}.
-     * @param  s2 The second string to compare, or {@code null}.
+     * @param  s1 The first characters sequence to compare, or {@code null}.
+     * @param  s2 The second characters sequence to compare, or {@code null}.
      * @param  filter The subset of characters to compare, or {@code null} for comparing all characters.
      * @param  ignoreCase {@code true} for comparing cases, or {@code false} for requiring exact match.
      * @return {@code true} if or if both arguments are {@code null} or if the two given texts are equal,
@@ -1598,10 +1597,10 @@ cmp:    while (ia < lga) {
         }
         final int lg1 = s1.length();
         final int lg2 = s2.length();
-        int i2 = 0, n;
-        for (int i1=0; i1<lg1; i1+=n) {
+        int i1 = 0, i2 = 0;
+        while (i1 < lg1) {
             int c1 = codePointAt(s1, i1);
-            n = charCount(c1);
+            final int n = charCount(c1);
             if (filter.contains(c1)) {
                 // Fetch the next significant character from the second string.
                 int c2;
@@ -1618,6 +1617,7 @@ cmp:    while (ia < lga) {
                     return false;
                 }
             }
+            i1 += n;
         }
         while (i2 < lg2) {
             final int s = codePointAt(s2, i2);
@@ -1967,9 +1967,53 @@ cmp:    while (ia < lga) {
     }
 
     /**
+     * Replaces all occurrences of a given string in the given character sequence. If no occurrence of
+     * {@code toSearch} is found in the given text, then this method returns the {@code text} unchanged.
+     * Otherwise this method returns a new character sequence with all occurrences replaced by {@code replaceBy}.
+     *
+     * <p>This method is similar to {@link String#replace(CharSequence, CharSequence)} except for the following:</p>
+     * <ul>
+     *   <li>This method accepts arbitrary {@code CharSequence} objects.</li>
+     *   <li>This method <strong>does not use regular expression</strong>.
+     *       The {@code toSearch} value is searched verbatim.</li>
+     * </ul>
+     *
+     * @param  text      The character sequence in which to perform the replacements, or {@code null}.
+     * @param  toSearch  The string to replace.
+     * @param  replaceBy The replacement for the searched string.
+     * @return The given text with replacements applied, or {@code text} if no replacement has been applied,
+     *         or {@code null} if the given text was null
+     *
+     * @see String#replace(char, char)
+     * @see StringBuilders#replace(StringBuilder, String, String)
+     * @see String#replace(CharSequence, CharSequence)
+     *
+     * @since 0.4
+     */
+    public static CharSequence replace(final CharSequence text, final CharSequence toSearch, final CharSequence replaceBy) {
+        ArgumentChecks.ensureNonEmpty("toSearch",  toSearch);
+        ArgumentChecks.ensureNonNull ("replaceBy", replaceBy);
+        if (text != null && !toSearch.equals(replaceBy)) {
+            final int length = text.length();
+            int i = indexOf(text, toSearch, 0, length);
+            if (i >= 0) {
+                int p = 0;
+                final int sl = toSearch.length();
+                final StringBuilder buffer = new StringBuilder(length + (replaceBy.length() - sl));
+                do {
+                    buffer.append(text, p, i).append(replaceBy);
+                    i = indexOf(text, toSearch, p = i + sl, length);
+                } while (i >= 0);
+                return buffer.append(text, p, length);
+            }
+        }
+        return text;
+    }
+
+    /**
      * Copies a sequence of characters in the given {@code char[]} array.
      *
-     * @param src       The character sequences from which to copy characters.
+     * @param src       The characters sequence from which to copy characters.
      * @param srcOffset Index of the first character from {@code src} to copy.
      * @param dst       The array where to copy the characters.
      * @param dstOffset Index where to write the first character in {@code dst}.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Static.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Static.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Static.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Static.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -43,6 +43,10 @@ package org.apache.sis.util;
  * <tr><th colspan="2" class="hsep">Mathematics and units of measurement</th></tr>
  * <tr><td>{@link org.apache.sis.math.MathFunctions}</td>
  *     <td>Additions to the {@link java.lang.Math} methods.</td></tr>
+ * <tr><td>{@link org.apache.sis.math.DecimalFunctions}</td>
+ *     <td>Mathematical methods related to base 10 representation of numbers.</td></tr>
+ * <tr><td>{@link org.apache.sis.referencing.operation.matrix.Matrices}</td>
+ *     <td>Creates and compares {@link org.opengis.referencing.operation.Matrix} objects</td></tr>
  * <tr><td>{@link org.apache.sis.measure.Units}</td>
  *     <td>Get a {@linkplain javax.measure.unit.Unit unit} from a symbol or EPSG code,
  *         and test if a unit is angular, linear or temporal.</td></tr>
@@ -50,13 +54,19 @@ package org.apache.sis.util;
  * <tr><th colspan="2" class="hsep">OGC/ISO objects (metadata, referencing, geometries)</th></tr>
  * <tr><td>{@link org.apache.sis.util.iso.Types}</td>
  *     <td>Provide UML identifier and description for GeoAPI types.</td></tr>
+ * <tr><td>{@link org.apache.sis.metadata.iso.citation.Citations}</td>
+ *     <td>Pre-defined {@link org.opengis.metadata.citation.Citation}
+ *         and methods for comparing against titles or identifiers.</td></tr>
  * <tr><td>{@link org.apache.sis.metadata.iso.extent.Extents}</td>
  *     <td>Extract information from {@link org.opengis.metadata.extent.Extent} objects.</td></tr>
  * <tr><td>{@link org.apache.sis.geometry.Envelopes}</td>
- *     <td>Parse, format and transform {@linkplain org.opengis.geometry.Envelope envelopes}.</td></tr>
+ *     <td>Parse, format and transform {@link org.opengis.geometry.Envelope} objects.</td></tr>
  * <tr><td>{@link org.apache.sis.referencing.IdentifiedObjects}</td>
  *     <td>Handle names, identifiers or properties of
- *         {@linkplain org.opengis.referencing.IdentifiedObject identified objects}.</td></tr>
+ *         {@link org.opengis.referencing.IdentifiedObject} instances.</td></tr>
+ * <tr><td>{@link org.apache.sis.referencing.cs.CoordinateSystems}</td>
+ *     <td>Parses axis names and creates transforms between {@link org.opengis.referencing.cs.CoordinateSystem}
+ *         instances.</td></tr>
  *
  * <tr><th colspan="2" class="hsep">Input / Output (including CRS, XML, images)</th></tr>
  * <tr><td>{@link org.apache.sis.io.IO}</td>

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -86,7 +86,7 @@ public final class StringBuilders extend
      * @throws IllegalArgumentException If the {@code toSearch} argument is empty.
      *
      * @see String#replace(char, char)
-     * @see String#replace(CharSequence, CharSequence)
+     * @see CharSequences#replace(CharSequence, String, String)
      * @see StringBuilder#replace(int, int, String)
      */
     public static void replace(final StringBuilder buffer, final String toSearch, final String replaceBy) {
@@ -219,7 +219,7 @@ public final class StringBuilders extend
                 if (r >= 0 && r<ASCII.length()) {
                     final char ac = ASCII.charAt(r);
                     if (buffer == null) {
-                        buffer = new StringBuilder(text);
+                        buffer = new StringBuilder(text.length()).append(text);
                         text = buffer;
                     }
                     // Nothing special do to about codepoint here, since 'c' is

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Version.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Version.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Version.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/Version.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -244,6 +244,7 @@ public class Version implements CharSequ
      *         value if it is higher, or 0 if they are equal.
      */
     public int compareTo(final Version other, final int limit) {
+        ArgumentChecks.ensureNonNull ("other", other);
         ArgumentChecks.ensurePositive("limit", limit);
         for (int i=0; i<limit; i++) {
             final Comparable<?> v1 =  this.getComponent(i);

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -67,6 +67,11 @@ import org.apache.sis.internal.system.De
  */
 public final class Types extends Static {
     /**
+     * The separator character between class name and attribute name in resource files.
+     */
+    private static final char SEPARATOR = '.';
+
+    /**
      * The types for ISO 19115 UML identifiers. The keys are UML identifiers. Values
      * are either class names as {@link String} objects, or the {@link Class} instances.
      * This map will be built only when first needed.
@@ -180,10 +185,10 @@ public final class Types extends Static 
      * results without the need of resource bundles. For better results, consider using
      * {@link #getCodeTitle(CodeList)} instead.
      *
-     * <p>The current heuristic implementation iterates over {@linkplain CodeList#names() all
-     * code names}, selects the longest one excluding the {@linkplain CodeList#name() field name}
-     * if possible, then {@linkplain CharSequences#camelCaseToSentence(CharSequence) makes a sentence}
-     * from that name. Examples:</p>
+     * <p>The current heuristic implementation iterates over {@linkplain CodeList#names() all code names},
+     * selects the longest one excluding the {@linkplain CodeList#name() field name} if possible, then
+     * {@linkplain CharSequences#camelCaseToSentence(CharSequence) makes a sentence} from that name.
+     * Examples:</p>
      *
      * <ul>
      *   <li>{@code getCodeLabel(AxisDirection.NORTH)} returns {@code "North"}.</li>
@@ -243,7 +248,7 @@ public final class Types extends Static 
         if (code != null) {
             final String resources = getResources(code.getClass().getName());
             if (resources != null) {
-                return new Description(resources, getListName(code) + '.' + getCodeName(code));
+                return new Description(resources, resourceKey(code));
             }
         }
         return null;
@@ -285,7 +290,7 @@ public final class Types extends Static 
             if (name != null) {
                 final String resources = getResources(type.getName());
                 if (resources != null) {
-                    return new Description(resources, name + '.' + property);
+                    return new Description(resources, name + SEPARATOR + property);
                 }
             }
         }
@@ -349,7 +354,7 @@ public final class Types extends Static 
          * Returns a fallback if no resource is found.
          */
         String fallback() {
-            return CharSequences.camelCaseToSentence(key.substring(key.lastIndexOf('.') + 1)).toString();
+            return CharSequences.camelCaseToSentence(key.substring(key.lastIndexOf(SEPARATOR) + 1)).toString();
         }
     }
 
@@ -382,7 +387,7 @@ public final class Types extends Static 
          * @param code The code list for which to create a title.
          */
         CodeTitle(final CodeList<?> code) {
-            super("org.opengis.metadata.CodeLists", getListName(code) + '.' + getCodeName(code));
+            super("org.opengis.metadata.CodeLists", resourceKey(code));
             this.code = code;
         }
 
@@ -411,6 +416,17 @@ public final class Types extends Static 
     }
 
     /**
+     * Returns the resource key for the given code list.
+     */
+    static String resourceKey(final CodeList<?> code) {
+        String key = getCodeName(code);
+        if (key.indexOf(SEPARATOR) < 0) {
+            key = getListName(code) + SEPARATOR + key;
+        }
+        return key;
+    }
+
+    /**
      * Returns all known values for the given type of code list.
      * Note that the size of the returned array may growth between different invocations of this method,
      * since users can add their own codes to an existing list.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1548777&r1=1548776&r2=1548777&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Sat Dec  7 02:00:42 2013
@@ -226,6 +226,11 @@ public final class Errors extends Indexe
         public static final int IllegalArgumentValue_2 = 14;
 
         /**
+         * Coordinate system of class ‘{0}’ can not have axis in the {1} direction.
+         */
+        public static final int IllegalAxisDirection_2 = 128;
+
+        /**
          * Illegal bits pattern: {0}.
          */
         public static final int IllegalBitsPattern_1 = 16;
@@ -276,6 +281,16 @@ public final class Errors extends Indexe
         public static final int IllegalUnicodeCodePoint_2 = 112;
 
         /**
+         * Unit of measurement “{1}” is not valid for “{0}” values.
+         */
+        public static final int IllegalUnitFor_2 = 129;
+
+        /**
+         * Incompatible coordinate system types.
+         */
+        public static final int IncompatibleCoordinateSystemTypes = 130;
+
+        /**
          * Property “{0}” has an incompatible value.
          */
         public static final int IncompatiblePropertyValue_1 = 86;
@@ -466,11 +481,21 @@ public final class Errors extends Indexe
         public static final int NonInvertibleTransform = 83;
 
         /**
+         * Unit conversion from “{0}” to “{1}” is non-linear.
+         */
+        public static final int NonLinearUnitConversion_2 = 131;
+
+        /**
          * “{0}” is not a linear unit.
          */
         public static final int NonLinearUnit_1 = 47;
 
         /**
+         * Axis directions {0} and {1} are not perpendicular.
+         */
+        public static final int NonPerpendicularDirections_2 = 132;
+
+        /**
          * “{0}” is not a scale unit.
          */
         public static final int NonScaleUnit_1 = 48;
@@ -601,6 +626,11 @@ public final class Errors extends Indexe
         public static final int UnexpectedFileFormat_2 = 92;
 
         /**
+         * Axis direction “{0}” is unknown.
+         */
+        public static final int UnknownAxisDirection_1 = 133;
+
+        /**
          * Command “{0}” is not recognized.
          */
         public static final int UnknownCommand_1 = 102;



Mime
View raw message