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,