Author: desruisseaux
Date: Thu Jan 9 15:31:01 2014
New Revision: 1556837
URL: http://svn.apache.org/r1556837
Log:
AbstractIdentifier.getNames() collection should be live, because JAXB
implementation
writes directly in that collection at unmarshalling time.
Modified:
sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/Code.java
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.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/referencing/AbstractIdentifiedObjectTest.java
Modified:
sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1556837&r1=1556836&r2=1556837&view=diff
==============================================================================
---
sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] (original)
+++
sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] Thu Jan 9 15:31:01 2014
@@ -282,7 +282,7 @@ public abstract class ModifiableMetadata
List<E> target, final Class<E> elementType)
throws UnmodifiableMetadataException
{
- // See the comments in writeCollection(...) for implementation notes.
+ // See the comments in writeCollection(…) for implementation notes.
if (source != target) {
if (unmodifiable == FREEZING) {
return (List<E>) source;
@@ -330,7 +330,7 @@ public abstract class ModifiableMetadata
Set<E> target, final Class<E> elementType)
throws UnmodifiableMetadataException
{
- // See the comments in writeCollection(...) for implementation notes.
+ // See the comments in writeCollection(…) for implementation notes.
if (source != target) {
if (unmodifiable == FREEZING) {
return (Set<E>) source;
@@ -387,7 +387,8 @@ public abstract class ModifiableMetadata
/*
* It is not worth to copy the content if the current and the new
instance are the
* same. This is safe only using the != operator, not the
!equals(Object) method.
- * This optimization is required for efficient working of
PropertyAccessor.set(...).
+ * This optimization is required for efficient working of
PropertyAccessor.set(…)
+ * and JAXB unmarshalling.
*/
if (source != target) {
if (unmodifiable == FREEZING) {
Modified:
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/Code.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/Code.java?rev=1556837&r1=1556836&r2=1556837&view=diff
==============================================================================
---
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/Code.java
[UTF-8] (original)
+++
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/Code.java
[UTF-8] Thu Jan 9 15:31:01 2014
@@ -22,7 +22,7 @@ import javax.xml.bind.annotation.XmlAttr
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.ReferenceIdentifier;
import org.apache.sis.internal.util.DefinitionURI;
-import org.apache.sis.metadata.iso.ImmutableIdentifier;
+import org.apache.sis.referencing.NamedIdentifier;
import org.apache.sis.metadata.iso.citation.Citations;
import static
org.apache.sis.internal.referencing.ReferencingUtilities.toURNType;
@@ -114,7 +114,7 @@ public final class Code {
}
authority = Citations.fromName(cs);
}
- return new ImmutableIdentifier(authority, cs, c, version, null);
+ return new NamedIdentifier(authority, cs, c, version, null);
}
/**
Modified:
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1556837&r1=1556836&r2=1556837&view=diff
==============================================================================
---
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
[UTF-8] (original)
+++
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
[UTF-8] Thu Jan 9 15:31:01 2014
@@ -21,6 +21,7 @@ import java.util.Set;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.Locale;
import java.io.Serializable;
@@ -137,7 +138,7 @@ public class AbstractIdentifiedObject ex
* The name for this object or code. Shall never be {@code null}.
*
* <p><b>Consider this field as final!</b>
- * This field is modified only at unmarshalling time by {@link
#setNames(Collection)}</p>
+ * This field is modified only at unmarshalling time by {@link
Names#add(ReferenceIdentifier)}.</p>
*
* @see #getName()
* @see #getNames()
@@ -150,7 +151,7 @@ public class AbstractIdentifiedObject ex
* we may get both on unmarshalling.
*
* <p><b>Consider this field as final!</b>
- * This field is modified only at unmarshalling time by {@link
#setNames(Collection)}</p>
+ * This field is modified only at unmarshalling time by {@link
Names#add(ReferenceIdentifier)}.</p>
*/
private Collection<GenericName> alias;
@@ -455,41 +456,99 @@ public class AbstractIdentifiedObject ex
}
/**
+ * A writable view over the {@linkplain AbstractIdentifiedObject#getName()
name} of the enclosing object followed by
+ * all {@linkplain AbstractIdentifiedObject#getAlias() aliases} which are
instance of {@link ReferenceIdentifier}.
+ * Used by JAXB only at (un)marshalling time because GML merges the name
and aliases in a single {@code <gml:name>}
+ * property.
+ */
+ private final class Names extends AbstractCollection<ReferenceIdentifier> {
+ /**
+ * Invoked by JAXB before to write in the collection at unmarshalling
time.
+ * Do nothing since our object is already empty.
+ */
+ @Override
+ public void clear() {
+ }
+
+ /**
+ * Returns the number of name and aliases that are instance of {@link
ReferenceIdentifier}.
+ */
+ @Override
+ public int size() {
+ return NameIterator.count(AbstractIdentifiedObject.this);
+ }
+
+ /**
+ * Returns an iterator over the name and aliases that are instance of
{@link ReferenceIdentifier}.
+ */
+ @Override
+ public Iterator<ReferenceIdentifier> iterator() {
+ return new NameIterator(AbstractIdentifiedObject.this);
+ }
+
+ /**
+ * Invoked by JAXB at unmarshalling time for each identifier. The
first identifier will be taken
+ * as the name and all other identifiers (if any) as aliases.
+ *
+ * <p>Some JAXB implementations never invoke {@link
AbstractIdentifiedObject#setNames(Collection)}.
+ * Instead they invoke {@link AbstractIdentifiedObject#getNames()} and
add directly the identifiers
+ * in the returned collection. Consequently this method must writes
directly in the enclosing object.
+ * See <a href="https://java.net/jira/browse/JAXB-488">JAXB-488</a>
for more information.</p>
+ */
+ @Override
+ public boolean add(final ReferenceIdentifier id) {
+ if (name == null) {
+ name = id;
+ return true;
+ }
+ if (alias == null) {
+ alias = new ArrayList<>(4);
+ }
+ /*
+ * Our Code and RS_Identifier implementations should always create
NamedIdentifier instance,
+ * so the 'instanceof' check should not be necessary. But do a
paranoiac check anyway.
+ */
+ return alias.add(id instanceof GenericName ? (GenericName) id :
new NamedIdentifier(id));
+ }
+ }
+
+ /**
* Returns the {@link #name} and all aliases which are also instance of
{@lik ReferenceIdentifier}.
* The later happen often in SIS implementation since many aliases are
instance of {@link NamedIdentifier}.
*/
@XmlElement(name = "name", required = true)
final Collection<ReferenceIdentifier> getNames() {
- // Unconditionally creates a modifiable list because some JAXB
implementations modify it.
- final Collection<ReferenceIdentifier> names = new
ArrayList<>(nonNull(alias).size() + 1);
- names.add(name);
- if (alias != null) {
- for (final GenericName c : alias) {
- if (c != name && (c instanceof ReferenceIdentifier)) {
- names.add((ReferenceIdentifier) c);
- }
- }
- }
- return names;
+ return new Names();
}
/**
* Sets the first element as the {@link #name} and all remaining elements
as {@link #alias}.
- * This method is invoked by JAXB at unmarshalling time. It should not be
invoked anymore
- * after the object has been made available to the user.
+ * This method is invoked by some implementations of JAXB (not all of
them) at unmarshalling time.
+ * It should not be invoked anymore after the object has been made
available to the user.
+ *
+ * <p>Some JAXB implementations never invoke this setter method. Instead
they invoke {@link #getNames()}
+ * and add directly the identifiers in the returned collection. Whether
JAXB will perform a final call to
+ * {@code setNames(…)} is JAXB-implementation dependent (JDK7 does but
JDK6 and JDK8 early access do not).
+ * Consequently we can not rely on this method to be invoked. It is better
if this method is invoked, but
+ * we will not lost data if it is not.</p>
+ *
+ * @see <a href="https://java.net/jira/browse/JAXB-488">JAXB-488</a>
*/
private void setNames(final Collection<ReferenceIdentifier> names) {
- if (names != null && canSetProperty("name", name != null)) {
- final Iterator<ReferenceIdentifier> it = names.iterator();
- if (it.hasNext()) {
- name = it.next();
- if (it.hasNext() && canSetProperty("alias", alias != null)) {
- alias = new ArrayList<>(4); // There is generally few
aliases.
- do {
- alias.add(new NamedIdentifier(it.next()));
- } while (it.hasNext());
- }
- }
+ /*
+ * If the collection is an instance of Names, then assume that the
collection is the instance obtained
+ * by getNames(), in which case this IdentifiedObject already contains
the content of that collection.
+ * This behavior is necessary for working around the JAXB-488 issue.
+ */
+ if (!(names instanceof Names)) {
+ getNames().addAll(names);
+ }
+ /*
+ * Froze aliases in an unmodifiable set. In JAXB implementations that
do not invoke the setter method,
+ * the aliases list is left modifiable. This is a hole in our object
immutability.
+ */
+ if (alias != null) {
+ alias = immutableSet(true, alias.toArray(new
GenericName[alias.size()]));
}
}
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=1556837&r1=1556836&r2=1556837&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] Thu Jan 9 15:31:01 2014
@@ -137,8 +137,6 @@ public class NamedIdentifier extends Imm
/**
* Constructs an identifier from an authority and localizable code.
* This is a convenience constructor for commonly-used parameters.
- * If more control are wanted (for example adding remarks), use the
- * {@linkplain #NamedIdentifier(Map) constructor with a properties map}.
*
* @param authority The authority (e.g. {@link Citations#OGC} or {@link
Citations#EPSG}),
* or {@code null} if not available.
@@ -155,30 +153,38 @@ public class NamedIdentifier extends Imm
/**
* Constructs an identifier from an authority and code.
* This is a convenience constructor for commonly-used parameters.
- * If more control are wanted (for example adding remarks), use the
- * {@linkplain #NamedIdentifier(Map) constructor with a properties map}.
*
* @param authority The authority (e.g. {@link Citations#OGC} or {@link
Citations#EPSG}),
* or {@code null} if not available.
* @param code The code. This parameter is mandatory.
*/
public NamedIdentifier(final Citation authority, final String code) {
- this(authority, code, null);
+ super(authority, Citations.getIdentifier(authority), code);
}
/**
- * Constructs an identifier from an authority, code and version.
- * This is a convenience constructor for commonly-used parameters.
- * If more control are wanted (for example adding remarks), use the
- * {@linkplain #NamedIdentifier(Map) constructor with a properties map}.
+ * Creates an identifier from the specified code and authority,
+ * with an optional version number and remarks.
*
- * @param authority The authority (e.g. {@link Citations#OGC} or {@link
Citations#EPSG}),
- * or {@code null} if not available.
- * @param code The code. This parameter is mandatory.
- * @param version The version, or {@code null} if none.
- */
- public NamedIdentifier(final Citation authority, final String code, final
String version) {
- super(authority, Citations.getIdentifier(authority), code, version,
null);
+ * @param authority
+ * Organization or party responsible for definition and
maintenance of the code
+ * space or code, or {@code null} if not available.
+ * @param codeSpace
+ * Name or identifier of the person or organization responsible
for namespace, or
+ * {@code null} if not available. This is often an abbreviation
of the authority name.
+ * @param code
+ * Identifier code or name, optionally from a controlled list or
pattern defined by
+ * a code space. The code can not be null.
+ * @param version
+ * The version of the associated code space or code as specified
by the code authority,
+ * or {@code null} if none.
+ * @param remarks
+ * Comments on or information about this identifier, or {@code
null} if none.
+ */
+ public NamedIdentifier(final Citation authority, final String codeSpace,
+ final String code, final String version, final InternationalString
remarks)
+ {
+ super(authority, codeSpace, code, version, remarks);
}
/**
Modified:
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java?rev=1556837&r1=1556836&r2=1556837&view=diff
==============================================================================
---
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
[UTF-8] (original)
+++
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/AbstractIdentifiedObjectTest.java
[UTF-8] Thu Jan 9 15:31:01 2014
@@ -85,7 +85,7 @@ public final strictfp class AbstractIden
assertEquals("codespace", "EPSG",
name.getCodeSpace());
assertEquals("version", "8.3",
name.getVersion());
assertEquals("aliases", "International 1979",
getSingleton(object.getAlias()).toString());
- assertEquals("names", Collections.singletonList(name),
object.getNames());
+ assertEquals("names", name,
getSingleton(object.getNames()));
assertEquals("identifiers", identifiers,
object.getIdentifiers());
assertEquals("ID", gmlID,
object.getID());
assertEquals("remarks", "Adopted by IUGG 1979 Canberra",
object.getRemarks().toString(Locale.ENGLISH));