You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2015/10/16 11:05:12 UTC
svn commit: r1708922 - in
/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing:
AbstractIdentifiedObject.java NameIterator.java
Author: desruisseaux
Date: Fri Oct 16 09:05:11 2015
New Revision: 1708922
URL: http://svn.apache.org/viewvc?rev=1708922&view=rev
Log:
Clarification on the use of Names at (un)marshalling time.
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/NameIterator.java
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1708922&r1=1708921&r2=1708922&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Fri Oct 16 09:05:11 2015
@@ -40,7 +40,6 @@ import org.opengis.metadata.citation.Cit
import org.opengis.referencing.ObjectFactory;
import org.opengis.referencing.AuthorityFactory;
import org.opengis.referencing.IdentifiedObject;
-import org.apache.sis.internal.metadata.NameMeaning;
import org.apache.sis.internal.jaxb.Context;
import org.apache.sis.internal.jaxb.referencing.Code;
import org.apache.sis.internal.util.Numerics;
@@ -48,7 +47,6 @@ import org.apache.sis.internal.util.Unmo
import org.apache.sis.internal.metadata.NameToIdentifier;
import org.apache.sis.internal.referencing.WKTUtilities;
import org.apache.sis.internal.referencing.ReferencingUtilities;
-import org.apache.sis.internal.referencing.NilReferencingObject;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.io.wkt.FormattableObject;
import org.apache.sis.io.wkt.Formatter;
@@ -67,7 +65,6 @@ import static org.apache.sis.util.Utilit
import static org.apache.sis.internal.util.CollectionsExt.nonNull;
import static org.apache.sis.internal.util.CollectionsExt.nonEmpty;
import static org.apache.sis.internal.util.CollectionsExt.immutableSet;
-import static org.apache.sis.internal.util.Utilities.appendUnicodeIdentifier;
// Branch-dependent imports
import java.util.Objects;
@@ -205,9 +202,13 @@ public class AbstractIdentifiedObject ex
/**
* Comments on or information about this object, or {@code null} if none.
+ *
+ * <p><b>Consider this field as final!</b>
+ * This field is modified only at unmarshalling time by {@link #setRemarks(InternationalString)}</p>
+ *
+ * @see #getRemarks()
*/
- @XmlElement
- private final InternationalString remarks;
+ private InternationalString remarks;
/**
* {@code true} if this object is deprecated.
@@ -536,7 +537,8 @@ public class AbstractIdentifiedObject ex
* @return The remarks, or {@code null} if none.
*/
@Override
- public InternationalString getRemarks(){
+ @XmlElement
+ public InternationalString getRemarks() {
return remarks;
}
@@ -890,17 +892,16 @@ public class AbstractIdentifiedObject ex
* reserved to JAXB, which will assign values to the fields using reflexion.
*/
AbstractIdentifiedObject() {
- remarks = null;
deprecated = false;
}
/**
* The {@code gml:id}, which is mandatory. The current implementation searches for the first identifier,
- * regardless its authority. If no identifier is found, then the name is used.
- * If no name is found (which should not occur for valid objects), then this method returns {@code null}.
+ * regardless its authority. If no identifier is found, then the name or aliases are used. If none of the
+ * above is found (which should not occur for valid objects), then this method returns {@code null}.
*
- * <p>If an identifier has been found, this method returns the concatenation of the following elements
- * separated by hyphens:</p>
+ * <p>If an identifier or a name has been found, this method returns the concatenation of the following
+ * elements separated by hyphens:</p>
* <ul>
* <li>The code space in lower case, retaining only characters that are valid for Unicode identifiers.</li>
* <li>The object type as defined in OGC's URN (see {@link org.apache.sis.internal.util.DefinitionURI})</li>
@@ -918,59 +919,9 @@ public class AbstractIdentifiedObject ex
@XmlAttribute(name = "id", namespace = Namespaces.GML, required = true)
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
final String getID() {
- final Context context = Context.current();
- String candidate = Context.getObjectID(context, this);
- if (candidate == null) {
- final StringBuilder id = new StringBuilder();
- /*
- * We will iterate over the identifiers first. Only after the iteration is over,
- * if we found no suitable ID, then we will use the primary name as a last resort.
- */
- if (identifiers != null) {
- for (final Identifier identifier : identifiers) {
- if (appendUnicodeIdentifier(id, '-', identifier.getCodeSpace(), ":", true) | // Really |, not ||
- appendUnicodeIdentifier(id, '-', NameMeaning.toObjectType(getClass()), ":", false) |
- appendUnicodeIdentifier(id, '-', identifier.getCode(), ":", true))
- {
- /*
- * Check for ID uniqueness. If the ID is rejected, then we just need to clear
- * the buffer and let the iteration continue the search for another ID.
- */
- candidate = id.toString();
- if (Context.setObjectForID(context, this, candidate)) {
- return candidate;
- }
- }
- id.setLength(0); // Clear the buffer for another try.
- }
- }
- /*
- * In last ressort, use the name or an alias. The name will be used without codespace since
- * names are often verbose. If that name is also used, append a number until we find a free ID.
- */
- if (name == null || !appendUnicodeIdentifier(id, '-', name.getCode(), ":", false)) {
- if (alias != null) {
- for (final GenericName a : alias) {
- if (appendUnicodeIdentifier(id, '-', a.toString(), ":", false)) {
- break;
- }
- }
- }
- }
- if (id.length() != 0) {
- candidate = id.toString();
- if (!Context.setObjectForID(context, this, candidate)) {
- final int s = id.append('-').length();
- int n = 0;
- do {
- if (++n == 100) return null; // Arbitrary limit.
- candidate = id.append(n).toString();
- id.setLength(s);
- } while (!Context.setObjectForID(context, this, candidate));
- }
- }
- }
- return candidate;
+ // Implementation is provided in the NameIterator class for reducing the size of
+ // AbstractIdentifiedObject.class file in the common case where XML is not needed.
+ return NameIterator.getID(Context.current(), this, name, alias, identifiers);
}
/**
@@ -999,7 +950,7 @@ public class AbstractIdentifiedObject ex
* <li>The first identifier, converted to the {@code "urn:} syntax if possible.</li>
* </ul>
*/
- @XmlElement(name = "identifier")
+ @XmlElement(required = true)
final Code getIdentifier() {
return Code.forIdentifiedObject(getClass(), identifiers);
}
@@ -1047,6 +998,13 @@ public class AbstractIdentifiedObject ex
* by all {@linkplain AbstractIdentifiedObject#getAlias() aliases} which are instance of {@link Identifier}.
* Used by JAXB only at (un)marshalling time because GML merges the name and aliases in a single {@code <gml:name>}
* property.
+ *
+ * <div class="section">Why we do not use {@code Identifier[]} array instead</div>
+ * It would be easier to define a {@code getNames()} method returning all identifiers in an array, and let JAXB
+ * invoke {@code setNames(Identifier[])} at unmarshalling time. But methods expecting an array in argument are
+ * invoked by JAXB only after the full element has been unmarshalled. For some {@code AbstractIdentifiedObject}
+ * subclasses, this is too late. For example {@code DefaultOperationMethod} may need to know the operation name
+ * before to parse the parameters.
*/
private final class Names extends AbstractCollection<Identifier> {
/**
@@ -1084,46 +1042,47 @@ public class AbstractIdentifiedObject ex
*/
@Override
public boolean add(final Identifier id) {
- addName(id);
+ if (NameIterator.isUnnamed(name)) {
+ name = id;
+ } else {
+ /*
+ * Our Code and RS_Identifier implementations should always create NamedIdentifier instance,
+ * so the 'instanceof' check should not be necessary. But we do a paranoiac check anyway.
+ */
+ final GenericName n = id instanceof GenericName ? (GenericName) id : new NamedIdentifier(id);
+ if (alias == null) {
+ alias = Collections.singleton(n);
+ } else {
+ /*
+ * This implementation is inefficient since each addition copies the array, but we rarely
+ * have more than two aliases. This implementation is okay for a small number of aliases
+ * and ensures that the enclosing AbstractIdentifiedObject is unmodifiable except by this
+ * add(…) method.
+ *
+ * Note about alternative approaches
+ * ---------------------------------
+ * An alternative approach could be to use an ArrayList and replace it by an unmodifiable
+ * list only after unmarshalling (using an afterUnmarshal(Unmarshaller, Object) method),
+ * but we want to avoid Unmarshaller dependency (for reducing classes loading for users
+ * who are not interrested in XML) and it may actually be less efficient for the vast
+ * majority of cases where there is less than 3 aliases.
+ */
+ final int size = alias.size();
+ final GenericName[] names = alias.toArray(new GenericName[size + 1]);
+ names[size] = n;
+ alias = UnmodifiableArrayList.wrap(names);
+ }
+ }
return true;
}
}
/**
- * Implementation of {@link Names#add(Identifier)}, defined in the enclosing class
- * for access to private fields without compiler-generated bridge methods.
+ * Invoked by JAXB for setting the remarks.
+ *
+ * @see #getRemarks()
*/
- final void addName(final Identifier id) {
- if (name == NilReferencingObject.UNNAMED) {
- name = id;
- } else {
- /*
- * Our Code and RS_Identifier implementations should always create NamedIdentifier instance,
- * so the 'instanceof' check should not be necessary. But we do a paranoiac check anyway.
- */
- final GenericName n = id instanceof GenericName ? (GenericName) id : new NamedIdentifier(id);
- if (alias == null) {
- alias = Collections.singleton(n);
- } else {
- /*
- * This implementation is inefficient since each addition copies the array, but we rarely
- * have more than two aliases. This implementation is okay for a small number of aliases
- * and ensures that the enclosing AbstractIdentifiedObject is unmodifiable except by this
- * add(…) method.
- *
- * Note about alternative approaches
- * ---------------------------------
- * An alternative approach could be to use an ArrayList and replace it by an unmodifiable
- * list only after unmarshalling (using an afterUnmarshal(Unmarshaller, Object) method),
- * but we want to avoid Unmarshaller dependency (for reducing classes loading for users
- * who are not interrested in XML) and it may actually be less efficient for the vast
- * majority of cases where there is less than 3 aliases.
- */
- final int size = alias.size();
- final GenericName[] names = alias.toArray(new GenericName[size + 1]);
- names[size] = n;
- alias = UnmodifiableArrayList.wrap(names);
- }
- }
+ private void setRemarks(final InternationalString remarks) {
+ this.remarks = remarks;
}
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/NameIterator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/NameIterator.java?rev=1708922&r1=1708921&r2=1708922&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/NameIterator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/NameIterator.java [UTF-8] Fri Oct 16 09:05:11 2015
@@ -17,24 +17,29 @@
package org.apache.sis.referencing;
import java.util.Iterator;
+import java.util.Collection;
import org.opengis.util.GenericName;
import org.opengis.metadata.Identifier;
import org.opengis.referencing.IdentifiedObject;
+import org.apache.sis.internal.jaxb.Context;
+import org.apache.sis.internal.metadata.NameMeaning;
import org.apache.sis.internal.referencing.NilReferencingObject;
+import static org.apache.sis.internal.util.Utilities.appendUnicodeIdentifier;
+
/**
* An iterator over the {@linkplain IdentifiedObject#getName() name} of an identified object followed by
* {@linkplain IdentifiedObject#getAlias() aliases} which are instance of {@link Identifier}.
- * This iterator is used for {@link AbstractIdentifiedObject} marshalling because GML merges the name and
- * aliases in a single {@code <gml:name>} property.
+ * This iterator is used for {@link AbstractIdentifiedObject} XML marshalling because GML merges the name
+ * and aliases in a single {@code <gml:name>} property. However this iterator is useful only if the aliases
+ * are instances of {@link NamedIdentifier}, or any other implementation which is both a name and an identifier.
*
- * <p>Note that this iterator is useful only if the aliases are instances of {@link NamedIdentifier},
- * or any other implementation which is both a name and an identifier.</p>
+ * <p>This class also opportunistically provide helper methods for {@link AbstractIdentifiedObject} marshalling.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.4
- * @version 0.5
+ * @version 0.7
* @module
*/
final class NameIterator implements Iterator<Identifier> {
@@ -55,12 +60,19 @@ final class NameIterator implements Iter
alias = object.getAlias().iterator();
next = object.getName();
// Should never be null in a well-formed IdentifiedObject, but let be safe.
- if (next == null || next == NilReferencingObject.UNNAMED) {
+ if (isUnnamed(next)) {
next();
}
}
/**
+ * Returns {@code true} if the given identifier is null or the {@link NilReferencingObject#UNNAMED} instance.
+ */
+ static boolean isUnnamed(final Identifier name) {
+ return (name == null) || (name == NilReferencingObject.UNNAMED);
+ }
+
+ /**
* Returns {@code true} if there is an other name or alias to return.
*/
@Override
@@ -105,4 +117,87 @@ final class NameIterator implements Iter
}
return c;
}
+
+ /**
+ * Implementation of {@link AbstractIdentifiedObject#getID()}, provided here for reducing the amount of code
+ * to load in the common case where XML support is not needed.
+ *
+ * <p>The current implementation searches for the first identifier, regardless its authority.
+ * If no identifier is found, then the name and aliases are used.
+ * Then, this method returns the concatenation of the following elements separated by hyphens:</p>
+ * <ul>
+ * <li>The code space in lower case, retaining only characters that are valid for Unicode identifiers.</li>
+ * <li>The object type as defined in OGC's URN (see {@link org.apache.sis.internal.util.DefinitionURI})</li>
+ * <li>The object code, retaining only characters that are valid for Unicode identifiers.</li>
+ * </ul>
+ *
+ * Example: {@code "epsg-crs-4326"}.
+ *
+ * <p>The returned ID needs to be unique only in the XML document being marshalled.
+ * Consecutive invocations of this method do not need to return the same value,
+ * since it may depends on the marshalling context.</p>
+ *
+ * @param context The (un)marshalling context.
+ * @param object The object for which to get a {@code gml:id}.
+ * @param name The identified object name, or {@code null} if none.
+ * @param alias The identified object aliases, or {@code null} if none.
+ * @param identifiers The identifiers, or {@code null} if none.
+ * @return Proposed value for {@code gml:id} attribute, or {@code null} if none.
+ */
+ static String getID(final Context context, final IdentifiedObject object, final Identifier name,
+ final Collection<? extends GenericName> alias, final Collection<? extends Identifier> identifiers)
+ {
+ String candidate = Context.getObjectID(context, object);
+ if (candidate == null) {
+ final StringBuilder id = new StringBuilder();
+ /*
+ * We will iterate over the identifiers first. Only after the iteration is over,
+ * if we found no suitable ID, then we will use the primary name as a last resort.
+ */
+ if (identifiers != null) {
+ for (final Identifier identifier : identifiers) {
+ if (appendUnicodeIdentifier(id, '-', identifier.getCodeSpace(), ":", true) | // Really |, not ||
+ appendUnicodeIdentifier(id, '-', NameMeaning.toObjectType(object.getClass()), ":", false) |
+ appendUnicodeIdentifier(id, '-', identifier.getCode(), ":", true))
+ {
+ /*
+ * Check for ID uniqueness. If the ID is rejected, then we just need to clear
+ * the buffer and let the iteration continue the search for another ID.
+ */
+ candidate = id.toString();
+ if (Context.setObjectForID(context, object, candidate)) {
+ return candidate;
+ }
+ }
+ id.setLength(0); // Clear the buffer for another try.
+ }
+ }
+ /*
+ * In last ressort, use the name or an alias. The name will be used without codespace since
+ * names are often verbose. If that name is also used, append a number until we find a free ID.
+ */
+ if (isUnnamed(name) || !appendUnicodeIdentifier(id, '-', name.getCode(), ":", false)) {
+ if (alias != null) {
+ for (final GenericName a : alias) {
+ if (appendUnicodeIdentifier(id, '-', a.toString(), ":", false)) {
+ break;
+ }
+ }
+ }
+ }
+ if (id.length() != 0) {
+ candidate = id.toString();
+ if (!Context.setObjectForID(context, object, candidate)) {
+ final int s = id.append('-').length();
+ int n = 0;
+ do {
+ if (++n == 100) return null; // Arbitrary limit.
+ candidate = id.append(n).toString();
+ id.setLength(s);
+ } while (!Context.setObjectForID(context, object, candidate));
+ }
+ }
+ }
+ return candidate;
+ }
}