sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1574221 - in /sis/branches/JDK7/core: sis-referencing/src/main/java/org/apache/sis/parameter/ sis-referencing/src/main/java/org/apache/sis/referencing/ sis-referencing/src/test/java/org/apache/sis/parameter/ sis-referencing/src/test/java/o...
Date Tue, 04 Mar 2014 22:02:38 GMT
Author: desruisseaux
Date: Tue Mar  4 22:02:37 2014
New Revision: 1574221

URL: http://svn.apache.org/r1574221
Log:
Clarification of GenericName <-> ReferenceIdentifier mapping.
Attempt to simplify DescriptorBuilder with an API which handle "name" and "aliases" as just
"names", like GML does.

Modified:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DescriptorBuilder.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DescriptorBuilderTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DescriptorBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DescriptorBuilder.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DescriptorBuilder.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DescriptorBuilder.java
[UTF-8] Tue Mar  4 22:02:37 2014
@@ -17,25 +17,54 @@
 package org.apache.sis.parameter;
 
 import java.util.Map;
+import java.util.List;
 import java.util.HashMap;
+import java.util.ArrayList;
 import javax.measure.unit.Unit;
+import org.opengis.util.NameSpace;
 import org.opengis.util.GenericName;
+import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.referencing.ReferenceIdentifier;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
+import org.apache.sis.referencing.NamedIdentifier;
+import org.apache.sis.metadata.iso.ImmutableIdentifier;
+import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Range;
+import org.apache.sis.util.resources.Errors;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+import static org.apache.sis.internal.system.DefaultFactories.NAMES;
+
+// Related to JDK7
+import java.util.Objects;
 
 
 /**
  * Provides convenience methods for easier {@link DefaultParameterDescriptor} instantiations.
  * This builder can be helpful for map projection <em>providers</em>, or for
implementation of
- * any process that use parameters. Map projection or process <em>users</em>
should rather invoke
- * {@link ParameterDescriptor#createValue()} on the descriptor provided by the implementor.
+ * any process that use parameters. Map projection or process <em>users</em>
do not need this
+ * builder since they can invoke {@link ParameterDescriptor#createValue()} on the descriptor
+ * provided by the implementor.
+ *
+ * {@section Identification properties}
+ * Each parameter must have a name, which can be specified by any of the {@code name(…)}
methods.
+ * Parameters can optionally have an arbitrary amount of aliases, which are also specified
by the
+ * {@code name(…)} methods — each call after the first one adds an alias.
+ *
+ * <p>Parameters can also have an arbitrary amount of identifiers, which are specified
by the
+ * {@code identifier(…)} methods. Like names, more than one identifier can be added by
invoking
+ * the method many time.</p>
+ *
+ * <p>Parameters can have at most one remark, which is specified by the {@code remarks(…)}
method.</p>
+ *
+ * <p>All the above-cited properties are cleared after a call to any {@code create(…)}
method,
+ * since those properties are specific to the each parameter. Other properties like codespace,
+ * version and cardinality are left unchanged because they may be shared by many parameters.</p>
  *
  * {@section Usage example}
  * Parameter descriptors are typically grouped in a {@link ParameterDescriptorGroup}.
@@ -68,6 +97,23 @@ public class DescriptorBuilder {
     private final Map<String,Object> properties;
 
     /**
+     * The aliases.
+     */
+    private final List<GenericName> aliases;
+
+    /**
+     * The identifiers.
+     */
+    private final List<ReferenceIdentifier> identifiers;
+
+    /**
+     * The codespace as a {@code NameSpace} object, or {@code null} if not yet created.
+     *
+     * @see #namespace()
+     */
+    private NameSpace namespace;
+
+    /**
      * {@code true} if the parameter is mandatory, or {@code false} if optional.
      *
      * @see #mandatory()
@@ -79,112 +125,286 @@ public class DescriptorBuilder {
      * Creates a new builder.
      */
     public DescriptorBuilder() {
-        properties = new HashMap<>(4);
+        properties  = new HashMap<>(8);
+        aliases     = new ArrayList<>(4);
+        identifiers = new ArrayList<>(4);
+    }
+
+    /**
+     * Clears the identification information.
+     * This does not clear the codespace, version and cardinality (mandatory versus optional)
properties.
+     */
+    private void clearIdentification() {
+        properties .put(ParameterDescriptor.NAME_KEY, null);
+        properties .remove(ParameterDescriptor.REMARKS_KEY);
+        aliases    .clear();
+        identifiers.clear();
+    }
+
+    /**
+     * Sets the property value for the given key, if a change is still possible. The check
for change permission
+     * is needed for all keys defined in the {@link ReferenceIdentifier} interface. This
check is not needed for
+     * other keys, so callers do not need to invoke this method for other keys.
+     *
+     * @param  key The key of the property to set.
+     * @param  value The value to set.
+     * @return {@code true} if the property changed as a result of this method call.
+     * @throws IllegalStateException if a new value is specified in a phase where the value
can not be changed.
+     */
+    private boolean setProperty(final String key, final Object value) throws IllegalStateException
{
+        if (Objects.equals(properties.get(key), value)) {
+            return false;
+        }
+        if (properties.get(ParameterDescriptor.NAME_KEY) != null) {
+            throw new IllegalStateException(Errors.format(Errors.Keys.ValueAlreadyDefined_1,
key));
+        }
+        properties.put(key, value);
+        return true;
+    }
+
+    /**
+     * Returns the namespace, creating it when first needed.
+     */
+    private NameSpace namespace() {
+        if (namespace == null) {
+            final String codespace = (String) properties.get(ReferenceIdentifier.CODESPACE_KEY);
+            if (codespace != null) {
+                namespace = NAMES.createNameSpace(NAMES.createLocalName(null, codespace),
null);
+            }
+        }
+        return namespace;
     }
 
     /**
      * Sets the authority and code space. This method is typically invoked only once, since
      * a group of parameters often uses the same code space for all individual parameters.
      *
+     * <p><b>Condition:</b>
+     * this method can not be invoked after one or more names or identifiers have been added
(by calls to the
+     * {@code name(…)} or {@code identifier(…)} methods) for the next descriptor to create.
This method can be
+     * invoked again after the name, aliases and identifiers have been cleared by a call
to {@code createXXX(…)}.</p>
+     *
+     * <p><b>Life cycle:</b>
+     * this property is kept unchanged until this {@code codespace(…)} method is invoked
again.</p>
+     *
      * @param  authority Bibliographic reference to the authority defining the codes, or
{@code null} if none.
      * @param  codespace The parameter codespace, or {@code null} for inferring it from the
authority.
      * @return {@code this}, for method call chaining.
+     * @throws IllegalStateException if {@code name(…)} or {@code identifier(…)} has
been invoked at least
+     *         once since builder construction or since the last call to a {@code createXXX(…)}
method.
      */
     public DescriptorBuilder codespace(final Citation authority, final String codespace)
{
-        properties.put(ReferenceIdentifier.AUTHORITY_KEY, authority);
-        properties.put(ReferenceIdentifier.CODESPACE_KEY, codespace);
+        if (!setProperty(ReferenceIdentifier.CODESPACE_KEY, codespace)) {
+            namespace = null;
+        }
+        setProperty(ReferenceIdentifier.AUTHORITY_KEY, authority);
         return this;
     }
 
     /**
-     * Sets the version of code definitions. This method is typically invoked only once,
+     * Sets the version of parameter definitions. This method is typically invoked only once,
      * since a group of parameters often uses the same version for all individual parameters.
      *
+     * <p><b>Condition:</b>
+     * this method can not be invoked after one or more names or identifiers have been added
(by calls to the
+     * {@code name(…)} or {@code identifier(…)} methods) for the next descriptor to create.
This method can be
+     * invoked again after the name, aliases and identifiers have been cleared by a call
to {@code createXXX(…)}.</p>
+     *
+     * <p><b>Life cycle:</b>
+     * this property is kept unchanged until this {@code version(…)} method is invoked
again.</p>
+     *
      * @param  version The version of code definitions, or {@code null} if none.
      * @return {@code this}, for method call chaining.
+     * @throws IllegalStateException if {@code name(…)} or {@code identifier(…)} has
been invoked at least
+     *         once since builder construction or since the last call to a {@code createXXX(…)}
method.
      */
     public DescriptorBuilder version(final String version) {
-        properties.put(ReferenceIdentifier.VERSION_KEY, version);
+        setProperty(ReferenceIdentifier.VERSION_KEY, version);
+        return this;
+    }
+
+    /**
+     * Adds a parameter name given by a {@code String} or {@code InternationalString}.
+     * The given string will be combined with the authority, {@linkplain #codespace(Citation,
String) code space}
+     * and {@linkplain #version(String) version} information for creating the {@link ReferenceIdentifier}
or
+     * {@link GenericName} object.
+     *
+     * {@section Name and aliases}
+     * This method can be invoked many times. The first invocation sets the
+     * {@linkplain AbstractIdentifiedObject#getName() primary name}, and
+     * all subsequent invocations add an {@linkplain AbstractIdentifiedObject#getAlias()
alias}.
+     *
+     * <p><b>Life cycle:</b>
+     * the name and all aliases are cleared after a {@code createXXX(…)} method has been
invoked.</p>
+     *
+     * @param  name The parameter name.
+     * @return {@code this}, for method call chaining.
+     */
+    public DescriptorBuilder name(final CharSequence name) {
+        ensureNonNull("name", name);
+        final Object old = properties.put(ParameterDescriptor.NAME_KEY, name.toString());
+        if (old != null) {
+            properties.put(ParameterDescriptor.NAME_KEY, old); // Restore previous value.
+            aliases.add(name instanceof GenericName ? (GenericName) name : NAMES.createLocalName(namespace(),
name));
+        }
         return this;
     }
 
     /**
-     * Sets the name to the given string. The given string will be combined with the authority,
-     * {@linkplain #codespace(Citation, String) code space} and {@linkplain #version(String)
version}
-     * information for creating a {@link ReferenceIdentifier}.
+     * Adds a parameter name in an alternative namespace. This method is typically invoked
for
+     * {@linkplain AbstractIdentifiedObject#getAlias() aliases} defined after the primary
name.
+     *
+     * <div class="note"><b>Example:</b>
+     * The "<cite>Longitude of natural origin</cite>" parameter defined by EPSG
is named differently
+     * by OGC and GeoTIFF. Those alternative names can be defined as below:
+     *
+     * {@preformat java
+     *   builder.name("Longitude of natural origin")        // Primary name in builder default
namespace.
+     *          .name(Citations.OGC, "central_meridian")    // First alias in "OGC" namespace.
+     *          .name(Citations.GEOTIFF, "NatOriginLong");  // Second alias in "GeoTIFF"
namespace.
+     * }
+     *
+     * In this example, {@code "central_meridian"} will be the
+     * {@linkplain org.apache.sis.util.iso.DefaultScopedName#tip() tip} and {@code "OGC"}
will be the
+     * {@linkplain org.apache.sis.util.iso.DefaultScopedName#head() head} of the first alias.</div>
      *
-     * @param  code The parameter name as a string.
+     * <p><b>Life cycle:</b>
+     * the name and all aliases are cleared after a {@code createXXX(…)} method has been
invoked.</p>
+     *
+     * @param  authority Bibliographic reference to the authority defining the codes, or
{@code null} if none.
+     * @param  name The parameter alias as a name in the namespace of the given authority.
      * @return {@code this}, for method call chaining.
+     *
+     * @see #identifier(Citation, String)
      */
-    public DescriptorBuilder name(final String code) {
-        properties.put(ParameterDescriptor.NAME_KEY, code);
+    public DescriptorBuilder name(final Citation authority, final CharSequence name) {
+        ensureNonNull("name", name);
+        final NamedIdentifier identifier;
+        if (name instanceof InternationalString) {
+            identifier = new NamedIdentifier(authority, (InternationalString) name);
+        } else {
+            identifier = new NamedIdentifier(authority, name.toString());
+        }
+        final Object old = properties.put(ParameterDescriptor.NAME_KEY, identifier);
+        if (old != null) {
+            properties.put(ParameterDescriptor.NAME_KEY, old); // Restore previous value.
+            aliases.add(identifier);
+        }
         return this;
     }
 
     /**
-     * Sets the name to the given identifier. If an authority, {@linkplain #codespace(Citation,
String) code space}
-     * or {@linkplain #version(String) version} have been specified to this builder, they
will be ignored since the
-     * given identifier is expected to contain all those information.
+     * Adds a parameter name fully specified by the given identifier.
+     * This method ignores the authority, {@linkplain #codespace(Citation, String) code space}
or
+     * {@linkplain #version(String) version} specified to this builder (if any), since the
given
+     * identifier already contains those information.
+     *
+     * {@section Name and aliases}
+     * This method can be invoked many times. The first invocation sets the
+     * {@linkplain AbstractIdentifiedObject#getName() primary name} to the given value, and
+     * all subsequent invocations add an {@linkplain AbstractIdentifiedObject#getAlias()
alias}.
+     *
+     * <p><b>Life cycle:</b>
+     * the name and all aliases are cleared after a {@code createXXX(…)} method has been
invoked.</p>
      *
      * @param  name The parameter name as an identifier.
      * @return {@code this}, for method call chaining.
      */
     public DescriptorBuilder name(final ReferenceIdentifier name) {
-        properties.put(ParameterDescriptor.NAME_KEY, name);
+        ensureNonNull("name", name);
+        final Object old = properties.put(ParameterDescriptor.NAME_KEY, name);
+        if (old != null) {
+            properties.put(ParameterDescriptor.NAME_KEY, old); // Restore previous value.
+            aliases.add(name instanceof GenericName ? (GenericName) name : new NamedIdentifier(name));
+        }
         return this;
     }
 
     /**
-     * Sets the aliases as {@code String} or {@code InternationalString} instances.
-     * An arbitrary amount of aliases can be specified. Each alias is parsed using
-     * using the {@value org.apache.sis.util.iso.DefaultNameSpace#DEFAULT_SEPARATOR}
-     * separator.
+     * Adds a parameter name fully specified by the given generic name.
+     * This method ignores the authority, {@linkplain #codespace(Citation, String) code space}
or
+     * {@linkplain #version(String) version} specified to this builder (if any), since the
given
+     * generic name already contains those information.
+     *
+     * {@section Name and aliases}
+     * This method can be invoked many times. The first invocation sets the
+     * {@linkplain AbstractIdentifiedObject#getName() primary name} to the given value, and
+     * all subsequent invocations add an {@linkplain AbstractIdentifiedObject#getAlias()
alias}.
      *
-     * <div class="note"><b>Example:</b>
-     * The "<cite>Longitude of natural origin</cite>" parameter defined by EPSG
is named differently
-     * by OGC and GeoTIFF. Those alternative names can be defined as below:
+     * <p><b>Life cycle:</b>
+     * the name and all aliases are cleared after a {@code createXXX(…)} method has been
invoked.</p>
      *
-     * {@preformat java
-     *   builder.aliases("OGC:central_meridian", "GeoTIFF:NatOriginLong");
-     * }
+     * @param  name The parameter name as an identifier.
+     * @return {@code this}, for method call chaining.
+     */
+    public DescriptorBuilder name(final GenericName name) {
+        ensureNonNull("name", name);
+        if (properties.get(ParameterDescriptor.NAME_KEY) == null) {
+            properties.put(ParameterDescriptor.NAME_KEY, new NamedIdentifier(name));
+        } else {
+            aliases.add(name);
+        }
+        return this;
+    }
+
+    /**
+     * Adds a parameter identifier given by a {@code String}.
+     * The given string will be combined with the authority, {@linkplain #codespace(Citation,
String) code space}
+     * and {@linkplain #version(String) version} information for creating the {@link ReferenceIdentifier}
object.
      *
-     * In this example, {@code "central_meridian"} will be the name
-     * {@linkplain org.apache.sis.util.iso.DefaultScopedName#tip() tip} and {@code "OGC"}
will be the name
-     * {@linkplain org.apache.sis.util.iso.DefaultScopedName#head() head}.</div>
+     * <p><b>Life cycle:</b>
+     * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  aliases The aliases, or {@code null} or empty if none.
+     * @param  identifier The parameter identifier.
      * @return {@code this}, for method call chaining.
      */
-    public DescriptorBuilder aliases(final CharSequence... aliases) {
-        properties.put(ParameterDescriptor.ALIAS_KEY, aliases);
+    public DescriptorBuilder identifier(final String identifier) {
+        ensureNonNull("identifier", identifier);
+        identifiers.add(new ImmutableIdentifier((Citation) properties.get(ReferenceIdentifier.AUTHORITY_KEY),
+                (String) properties.get(ReferenceIdentifier.CODESPACE_KEY), identifier));
         return this;
     }
 
     /**
-     * Sets aliases as {@code GenericName} instances.
+     * Adds a parameter identifier in an alternative namespace. This method is typically
invoked in
+     * complement to {@link #name(Citation, CharSequence)}.
+     *
+     * <p><b>Life cycle:</b>
+     * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  aliases The aliases, or {@code null} or empty if none.
+     * @param  authority Bibliographic reference to the authority defining the codes, or
{@code null} if none.
+     * @param  identifier The parameter identifier as a code in the namespace of the given
authority.
      * @return {@code this}, for method call chaining.
+     *
+     * @see #name(Citation, CharSequence)
      */
-    public DescriptorBuilder aliases(final GenericName... aliases) {
-        properties.put(ParameterDescriptor.ALIAS_KEY, aliases);
+    public DescriptorBuilder identifier(final Citation authority, final String identifier)
{
+        ensureNonNull("identifier", identifier);
+        identifiers.add(new ImmutableIdentifier(authority, Citations.getIdentifier(authority),
identifier));
         return this;
     }
 
     /**
-     * Sets the identifiers as {@code ReferenceIdentifier} instances.
-     * This information is optional and can be specified as a complement to the parameter
name.
+     * Adds a parameter identifier fully specified by the given identifier.
+     * This method ignores the authority, {@linkplain #codespace(Citation, String) code space}
or
+     * {@linkplain #version(String) version} specified to this builder (if any), since the
given
+     * identifier already contains those information.
+     *
+     * <p><b>Life cycle:</b>
+     * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  identifiers The identifiers, or {@code null} or empty if none.
+     * @param  identifier The parameter identifier.
      * @return {@code this}, for method call chaining.
      */
-    public DescriptorBuilder identifiers(final ReferenceIdentifier... identifiers) {
-        properties.put(ParameterDescriptor.IDENTIFIERS_KEY, identifiers);
+    public DescriptorBuilder identifier(final ReferenceIdentifier identifier) {
+        ensureNonNull("identifier", identifier);
+        identifiers.add(identifier);
         return this;
     }
 
     /**
-     * Sets remarks as {@code String} or {@code InternationalString} instances.
+     * Sets remarks as a {@code String} or {@code InternationalString} instance.
+     * Calls to this method overwrite any previous value.
      *
      * @param  remarks The remarks, or {@code null} if none.
      * @return {@code this}, for method call chaining.
@@ -231,8 +451,7 @@ public class DescriptorBuilder {
         } else {
             valueDomain = null;
         }
-        return new DefaultParameterDescriptor<>(properties, Double.class,
-                valueDomain, null, Double.valueOf(defaultValue), required);
+        return create(Double.class, valueDomain, null, Double.valueOf(defaultValue));
     }
 
     /**
@@ -250,8 +469,7 @@ public class DescriptorBuilder {
         } else {
             valueDomain = NumberRange.create(0.0, false, Double.POSITIVE_INFINITY, false);
         }
-        return new DefaultParameterDescriptor<>(properties, Double.class,
-                valueDomain, null, Double.valueOf(defaultValue), required);
+        return create(Double.class, valueDomain, null, Double.valueOf(defaultValue));
     }
 
     /**
@@ -274,8 +492,7 @@ public class DescriptorBuilder {
         } else {
             valueDomain = null;
         }
-        return new DefaultParameterDescriptor<>(properties, Double.class,
-                valueDomain, null, Double.valueOf(defaultValue), required);
+        return create(Double.class, valueDomain, null, Double.valueOf(defaultValue));
     }
 
     /**
@@ -289,8 +506,7 @@ public class DescriptorBuilder {
     public ParameterDescriptor<Integer> createBounded(final int minimumValue, final
int maximumValue,
             final int defaultValue)
     {
-        return new DefaultParameterDescriptor<>(properties, Integer.class,
-                NumberRange.create(minimumValue, true, maximumValue, true), null, defaultValue,
required);
+        return create(Integer.class, NumberRange.create(minimumValue, true, maximumValue,
true), null, defaultValue);
     }
 
     /**
@@ -316,7 +532,7 @@ public class DescriptorBuilder {
         } else {
             valueDomain = new Range<>(valueClass, minimumValue, true, maximumValue,
true);
         }
-        return new DefaultParameterDescriptor<>(properties, valueClass, valueDomain,
null, defaultValue, required);
+        return create(valueClass, valueDomain, null, defaultValue);
     }
 
     /**
@@ -335,6 +551,21 @@ public class DescriptorBuilder {
      * @return The parameter descriptor for the given set of valid values.
      */
     public <T> ParameterDescriptor<T> createEnumerated(final Class<T> valueClass,
final T[] validValues, final T defaultValue) {
-        return new DefaultParameterDescriptor<>(properties, valueClass, null, validValues,
defaultValue, required);
+        return create(valueClass, null, validValues, defaultValue);
+    }
+
+    /**
+     * Invoked by all {@code createXXX(…)} method for creating the descriptor from the
properties currently set
+     * in this builder.
+     */
+    private <T> ParameterDescriptor<T> create(final Class<T> valueClass,
final Range<?> valueDomain,
+            final T[] validValues, final T defaultValue)
+    {
+        properties.put(ParameterDescriptor.ALIAS_KEY, aliases.toArray(new GenericName[aliases.size()]));
+        properties.put(ParameterDescriptor.IDENTIFIERS_KEY, identifiers.toArray(new ReferenceIdentifier[identifiers.size()]));
+        final ParameterDescriptor<T> descriptor = new DefaultParameterDescriptor<>(
+                properties, valueClass, valueDomain, validValues, defaultValue, required);
+        clearIdentification();
+        return descriptor;
     }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
[UTF-8] Tue Mar  4 22:02:37 2014
@@ -37,6 +37,8 @@ import org.apache.sis.metadata.iso.citat
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.util.collection.WeakValueHashMap;
 
+import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+
 // Related to JDK7
 import java.util.Objects;
 
@@ -58,13 +60,23 @@ import java.util.Objects;
  * {@linkplain AbstractIdentifiedObject#getAlias() aliases} and have those names used in
contexts
  * where {@code ReferenceIdentifier} instances are required, like GML marshalling time.
  *
- * {@section Name inference}
- * The generic name will be inferred from {@code ReferenceIdentifier} attributes.
- * More specifically, a {@link ScopedName} will be created using the shortest authority's
- * {@linkplain Citation#getAlternateTitles() alternate titles} (or the {@linkplain Citation#getTitle()
main title}
- * if there is no alternate titles) as the {@linkplain ScopedName#scope() scope}, and the
{@linkplain #getCode() code}
- * as the name {@linkplain ScopedName#tip() tip}. Note that according ISO 19115, citation
alternate titles often
- * contains abbreviation (for example "DCW" as an alternative title for "<cite>Digital
Chart of the World</cite>").
+ * {@section Name ↔ Identifier mapping}
+ * The {@code GenericName} attributes will be inferred from {@code ReferenceIdentifier} attributes
as below:
+ *
+ * <ul>
+ *   <li><b>{@linkplain #tip() Tip}:</b> derived from the identifier {@linkplain
#getCode() code}.</li>
+ *   <li><b>{@linkplain #head() Head}:</b> derived from the identifier
{@linkplain #getCodeSpace() code space}.</li>
+ *   <li><b>{@linkplain #scope() Scope}:</b> derived from the shortest
{@linkplain #getAuthority() authority}'s
+ *     {@linkplain Citation#getAlternateTitles() alternate titles}, or the {@linkplain Citation#getTitle()
main title}
+ *     if there is no alternate titles. Note that according ISO 19115, citation alternate
titles often contain
+ *     abbreviation (for example "DCW" as an alternative title for "<cite>Digital Chart
of the World</cite>").</li>
+ * </ul>
+ *
+ * <div class="note"><b>Example:</b>
+ * If the identifier attributes are {@code authority} = {@link Citations#OGP}, {@code codeSpace}
= {@code "EPSG"}
+ * and {@code code} = {@code "4326"}, then the name attributes will be {@code scope} = {@code
"OGP"},
+ * {@code head} = {@code "EPSG"}, {@code tip} = {@code "4326"} and {@link #toString()} =
{@code "EPSG:4326"}.
+ * Note that the scope does not appear in the string representation of names.</div>
  *
  *
  * {@section Immutability and thread safety}
@@ -84,7 +96,7 @@ public class NamedIdentifier extends Imm
     /**
      * Serial number for inter-operability with different versions.
      */
-    private static final long serialVersionUID = 8474731565582874497L;
+    private static final long serialVersionUID = -3982456534858346939L;
 
     /**
      * A pool of {@link NameSpace} values for given {@link InternationalString}.
@@ -92,8 +104,8 @@ public class NamedIdentifier extends Imm
     private static final Map<CharSequence,NameSpace> SCOPES = new WeakValueHashMap<>(CharSequence.class);
 
     /**
-     * The name of this identifier as a generic name. If {@code null}, will be constructed
-     * only when first needed.
+     * The name of this identifier as a generic name.
+     * If {@code null}, will be constructed only when first needed.
      */
     private transient GenericName name;
 
@@ -101,7 +113,7 @@ public class NamedIdentifier extends Imm
      * {@code true} if {@link #name} has been given explicitly by the user.
      * Consider this field as final - it is not only for constructors convenience.
      */
-    private boolean isNameSupplied;
+    private transient boolean isNameSupplied;
 
     /**
      * Creates a new identifier from the specified one. This is a copy constructor
@@ -109,7 +121,7 @@ public class NamedIdentifier extends Imm
      * available) from the given identifier.
      *
      * <p>If the given identifier implements the {@link GenericName} interface, then
calls to
-     * {@link #tip()}, {@link #head()}, {@link #scope()} and similar methods will delegates
+     * {@link #tip()}, {@link #head()}, {@link #scope()} and similar methods will delegate
      * to that name.</p>
      *
      * @param identifier The identifier to copy.
@@ -123,6 +135,19 @@ public class NamedIdentifier extends Imm
     }
 
     /**
+     * Creates a new identifier from the specified name. This constructor infers the identifier
attributes
+     * (code, codespace and authority) from the given name. Calls to name-related methods
like {@link #tip()},
+     * {@link #head()} and {@link #scope()} will delegate to the given name.
+     *
+     * @param name The name to wrap.
+     */
+    public NamedIdentifier(final GenericName name) {
+        super(name instanceof ReferenceIdentifier ? (ReferenceIdentifier) name : new Adapter(name));
+        this.name = name;
+        isNameSupplied = true;
+    }
+
+    /**
      * Constructs an identifier from the given properties. The content of the properties
map is used as
      * described in the {@linkplain ImmutableIdentifier#ImmutableIdentifier(Map) super-class
constructor}.
      *
@@ -215,25 +240,61 @@ public class NamedIdentifier extends Imm
      */
     private GenericName createName(final Citation authority, final CharSequence code) {
         final NameFactory factory = DefaultFactories.NAMES;
-        if (authority == null) {
-            return factory.createLocalName(null, code);
+        final String title = Citations.getIdentifier(authority); // Whitespaces trimed by
Citations.
+        NameSpace scope = null;
+        if (title != null) {
+            synchronized (SCOPES) {
+                scope = SCOPES.get(title);
+                if (scope == null) {
+                    scope = factory.createNameSpace(factory.createLocalName(null, title),
null);
+                    SCOPES.put(title, scope);
+                }
+            }
         }
-        final String title;
         final String codeSpace = super.getCodeSpace();
         if (codeSpace != null) {
-            title = codeSpace; // Whitespaces trimed by constructor.
+            return factory.createGenericName(scope, codeSpace, code);
         } else {
-            title = Citations.getIdentifier(authority); // Whitespaces trimed by Citations.
+            return factory.createLocalName(scope, code);
+        }
+    }
+
+    /**
+     * The converse of {@link NamedIdentifier#createName(Citation, CharSequence)}.
+     */
+    private static final class Adapter implements ReferenceIdentifier {
+        /** The name from which to infer the identifier attributes. */
+        private final GenericName name;
+
+        /** Infers the attributes from the given name. */
+        Adapter(final GenericName name) {
+            ensureNonNull("name", name);
+            this.name = name;
         }
-        NameSpace scope;
-        synchronized (SCOPES) {
-            scope = SCOPES.get(title);
-            if (scope == null) {
-                scope = factory.createNameSpace(factory.createLocalName(null, title), null);
-                SCOPES.put(title, scope);
+
+        /** Infers the authority from the scope. */
+        @Override public Citation getAuthority() {
+            final NameSpace scope = name.scope();
+            if (scope == null || scope.isGlobal()) {
+                return null;
             }
+            return Citations.fromName(scope.name().tip().toString());
+        }
+
+        /** Takes everything except the tip as the code space. */
+        @Override public String getCodeSpace() {
+            return (name instanceof ScopedName) ? ((ScopedName) name).path().toString() :
null;
+        }
+
+        /** Takes the last element as the code. */
+        @Override public String getCode() {
+            return name.tip().toString();
+        }
+
+        /** Names are not versioned. */
+        @Override public String getVersion() {
+            return null;
         }
-        return factory.createLocalName(scope, code).toFullyQualifiedName();
     }
 
     /**
@@ -265,10 +326,10 @@ public class NamedIdentifier extends Imm
 
     /**
      * Returns the scope (name space) in which this name is local.
-     * By default, this is the same value than {@link #getCodeSpace()} provided as a name
space.
+     * By default, this is the same value than the {@link #getAuthority() authority} provided
as a name space.
      *
      * @see #head()
-     * @see #getCodeSpace()
+     * @see #getAuthority()
      *
      * @return The scope of this name.
      */
@@ -402,9 +463,7 @@ public class NamedIdentifier extends Imm
      */
     private void writeObject(final ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
-        if (isNameSupplied) {
-            out.writeObject(name);
-        }
+        out.writeObject(isNameSupplied ? name : null);
     }
 
     /**
@@ -413,8 +472,7 @@ public class NamedIdentifier extends Imm
      */
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
{
         in.defaultReadObject();
-        if (isNameSupplied) {
-            name = (GenericName) in.readObject();
-        }
+        name = (GenericName) in.readObject();
+        isNameSupplied = (name != null);
     }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DescriptorBuilderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DescriptorBuilderTest.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DescriptorBuilderTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DescriptorBuilderTest.java
[UTF-8] Tue Mar  4 22:02:37 2014
@@ -47,8 +47,9 @@ public final strictfp class DescriptorBu
         final DescriptorBuilder builder = new DescriptorBuilder();
         builder.codespace(HardCodedCitations.OGP, "EPSG").mandatory();
         final ParameterDescriptor[] parameters = {
-            builder.name   ("Longitude of natural origin")
-                   .aliases("OGC:central_meridian", "GeoTIFF:NatOriginLong")
+            builder.name("Longitude of natural origin")
+                   .name(HardCodedCitations.OGC, "central_meridian")
+                   .name(HardCodedCitations.GEOTIFF, "NatOriginLong")
                    .remarks("Some remarks.")               .createBounded(-180, +180, 0,
NonSI.DEGREE_ANGLE),
             builder.name("Latitude of natural origin")     .createBounded( -80,  +80, 0,
NonSI.DEGREE_ANGLE),
             builder.name("Scale factor at natural origin") .createStrictlyPositive(1, Unit.ONE),

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/NamedIdentifierTest.java
[UTF-8] Tue Mar  4 22:02:37 2014
@@ -18,6 +18,8 @@ package org.apache.sis.referencing;
 
 import java.util.Locale;
 import org.opengis.referencing.ReferenceIdentifier;
+import org.opengis.util.InternationalString;
+import org.opengis.util.NameSpace;
 import org.opengis.util.GenericName;
 import org.opengis.test.Validators;
 import org.apache.sis.util.iso.DefaultInternationalString;
@@ -26,6 +28,8 @@ import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
+import static org.apache.sis.internal.system.DefaultFactories.SIS_NAMES;
+import static org.apache.sis.metadata.iso.citation.HardCodedCitations.OGP;
 import static org.apache.sis.metadata.iso.citation.HardCodedCitations.EPSG;
 
 
@@ -39,18 +43,49 @@ import static org.apache.sis.metadata.is
  */
 public final strictfp class NamedIdentifierTest extends TestCase {
     /**
-     * Tests the {@link NamedIdentifier#NamedIdentifier(Citation, String)} constructor.
+     * Tests the {@link NamedIdentifier#NamedIdentifier(Citation, String, String, String,
InternationalString)}
+     * constructor.
      */
     @Test
     public void testCreateFromCode() {
-        final NamedIdentifier identifier = new NamedIdentifier(EPSG, "4326");
+        final NamedIdentifier identifier = new NamedIdentifier(OGP, "EPSG", "4326", "8.3",
null);
         Validators.validate((ReferenceIdentifier) identifier);
         Validators.validate((GenericName) identifier);
 
         // ImmutableIdentifier properties
         assertEquals("code",      "4326", identifier.getCode());
         assertEquals("codeSpace", "EPSG", identifier.getCodeSpace());
-        assertSame  ("authority",  EPSG,  identifier.getAuthority());
+        assertSame  ("authority",  OGP,   identifier.getAuthority());
+        assertEquals("version",   "8.3",  identifier.getVersion());
+        assertNull  ("remarks",           identifier.getRemarks());
+        assertFalse ("isDeprecated",      identifier.isDeprecated());
+
+        // NamedIdentifier properties
+        assertEquals("depth",  2,          identifier.depth());
+        assertEquals("tip",   "4326",      identifier.tip().toString());
+        assertEquals("head",  "EPSG",      identifier.head().toString());
+        assertEquals("name",  "EPSG:4326", identifier.toString());
+
+        // Scope (derived from the autority)
+        final NameSpace scope = identifier.scope();
+        assertFalse ("scope",        scope.isGlobal());
+        assertEquals("scope", "OGP", scope.name().toString());
+    }
+
+    /**
+     * Tests the {@link NamedIdentifier#NamedIdentifier(GenericName)} constructor.
+     */
+    @Test
+    public void testCreateFromName() {
+        final NameSpace scope = SIS_NAMES.createNameSpace(SIS_NAMES.createLocalName(null,
"OGP"), null);
+        final NamedIdentifier identifier = new NamedIdentifier(SIS_NAMES.createGenericName(scope,
"EPSG", "4326"));
+        Validators.validate((ReferenceIdentifier) identifier);
+        Validators.validate((GenericName) identifier);
+
+        // ImmutableIdentifier properties
+        assertEquals("code",      "4326", identifier.getCode());
+        assertEquals("codeSpace", "EPSG", identifier.getCodeSpace());
+        assertEquals("authority", "OGP",  identifier.getAuthority().getTitle().toString());
         assertNull  ("version",           identifier.getVersion());
         assertNull  ("remarks",           identifier.getRemarks());
         assertFalse ("isDeprecated",      identifier.isDeprecated());
@@ -60,7 +95,7 @@ public final strictfp class NamedIdentif
         assertEquals("tip",   "4326",      identifier.tip().toString());
         assertEquals("head",  "EPSG",      identifier.head().toString());
         assertEquals("name",  "EPSG:4326", identifier.toString());
-        assertTrue  ("scope",              identifier.scope().isGlobal());
+        assertSame  ("scope", scope,       identifier.scope());
     }
 
     /**
@@ -101,7 +136,11 @@ public final strictfp class NamedIdentif
         assertEquals("name",  "EPSG:name", identifier.toInternationalString().toString(Locale.ENGLISH));
         assertEquals("name",  "EPSG:nom",  identifier.toInternationalString().toString(Locale.FRENCH));
         assertEquals("name",  "EPSG:名前",  identifier.toInternationalString().toString(Locale.JAPANESE));
-        assertTrue  ("scope",              identifier.scope().isGlobal());
+
+        // Scope (derived from the autority)
+        final NameSpace scope = identifier.scope();
+        assertFalse ("scope",         scope.isGlobal());
+        assertEquals("scope", "EPSG", scope.name().toString());
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
[UTF-8] Tue Mar  4 22:02:37 2014
@@ -245,8 +245,7 @@ public class DefaultNameFactory extends 
     /**
      * Constructs a generic name from a qualified name.
      * This method splits the given name around a separator inferred from the given scope,
or the
-     * {@value org.apache.sis.util.iso.DefaultNameSpace#DEFAULT_SEPARATOR} separator if the
given
-     * scope is null.
+     * {@link DefaultNameSpace#DEFAULT_SEPARATOR ':'} separator if the given scope is null.
      *
      * @param  scope The {@linkplain AbstractName#scope() scope} of the generic name to
      *         be created, or {@code null} for a global namespace.

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java
[UTF-8] Tue Mar  4 22:02:37 2014
@@ -21,7 +21,6 @@ import java.util.List;
 import java.util.ListIterator;
 import java.util.Locale;
 import java.io.Serializable;
-import java.io.ObjectStreamException;
 import org.opengis.util.NameSpace;
 import org.opengis.util.LocalName;
 import org.opengis.util.ScopedName;
@@ -64,8 +63,8 @@ public class DefaultNameSpace implements
     private static final long serialVersionUID = 8272640747799127007L;
 
     /**
-     * The default separator, which is {@value}. The separator is inserted between the
-     * namespace and any {@linkplain GenericName generic name} in that namespace.
+     * The default separator, which is {@code ':'}. The separator is inserted between
+     * the namespace and any {@linkplain GenericName generic name} in that namespace.
      */
     public static final char DEFAULT_SEPARATOR = ':';
 

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java?rev=1574221&r1=1574220&r2=1574221&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java [UTF-8]
(original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java [UTF-8]
Tue Mar  4 22:02:37 2014
@@ -710,10 +710,9 @@ public final class Types extends Static 
      * <ul>
      *   <li>{@link GenericName}, to be casted and returned as-is.</li>
      *   <li>{@link CharSequence} (usually a {@link String} or an {@link InternationalString}),
-     *       to be parsed as a generic name using the
-     *       {@value org.apache.sis.util.iso.DefaultNameSpace#DEFAULT_SEPARATOR} separator.</li>
+     *       to be parsed as a generic name using the {@link DefaultNameSpace#DEFAULT_SEPARATOR
':'} separator.</li>
      *   <li>{@link Identifier}, its {@linkplain Identifier#getCode() code} to be parsed
as a generic name
-     *       using the {@value org.apache.sis.util.iso.DefaultNameSpace#DEFAULT_SEPARATOR}
separator.</li>
+     *       using the {@link DefaultNameSpace#DEFAULT_SEPARATOR ':'} separator.</li>
      * </ul>
      *
      * If {@code value} is an array or a collection containing {@code null} elements,



Mime
View raw message