Author: desruisseaux
Date: Tue Oct 6 22:31:21 2015
New Revision: 1707160
URL: http://svn.apache.org/viewvc?rev=1707160&view=rev
Log:
Partial fix of holes in metadata immutability (SIS-107).
Modified:
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java
sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -18,11 +18,13 @@ package org.apache.sis.metadata;
import java.util.Map;
import java.util.Set;
+import java.util.EnumSet;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.collection.CodeListSet;
import org.apache.sis.internal.util.CollectionsExt;
@@ -33,7 +35,7 @@ import org.apache.sis.internal.util.Coll
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
final class Cloner extends org.apache.sis.internal.util.Cloner {
@@ -53,6 +55,15 @@ final class Cloner extends org.apache.si
}
/**
+ * Recursively clones all elements in the given array.
+ */
+ private void clones(final Object[] array) throws CloneNotSupportedException {
+ for (int i=0; i<array.length; i++) {
+ array[i] = clone(array[i]);
+ }
+ }
+
+ /**
* Returns an unmodifiable copy of the specified object.
* This method performs the following heuristic tests:
*
@@ -87,29 +98,39 @@ final class Cloner extends org.apache.si
if (object instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) object;
final boolean isSet = (collection instanceof Set<?>);
- if (collection.isEmpty()) {
- if (isSet) {
- collection = Collections.EMPTY_SET;
- } else {
- collection = Collections.EMPTY_LIST;
+ final Object[] array = collection.toArray();
+ switch (array.length) {
+ case 0: {
+ collection = isSet ? Collections.EMPTY_SET
+ : Collections.EMPTY_LIST;
+ break;
}
- } else {
- final Object[] array = collection.toArray();
- for (int i=0; i<array.length; i++) {
- array[i] = clone(array[i]);
+ case 1: {
+ final Object value = clone(array[0]);
+ collection = isSet ? Collections.singleton(value)
+ : Collections.singletonList(value);
+ break;
}
- // Do not use the SIS Checked* classes since
- // we don't need type checking anymore.
- if (isSet) {
- collection = CollectionsExt.immutableSet(false, array);
- } else {
- // Conservatively assumes a List if we are not sure to have a Set,
- // since the list is less destructive (no removal of duplicated).
- switch (array.length) {
- case 0: collection = Collections.EMPTY_LIST; break; // Redundant
with isEmpty(), but we are paranoiac.
- case 1: collection = Collections.singletonList(array[0]); break;
- default: collection = Containers.unmodifiableList(array); break;
+ default: {
+ if (isSet) {
+ if (collection instanceof EnumSet<?>) {
+ collection = Collections.unmodifiableSet(((EnumSet<?>)
collection).clone());
+ } else if (collection instanceof CodeListSet<?>) {
+ collection = Collections.unmodifiableSet(((CodeListSet<?>)
collection).clone());
+ } else {
+ clones(array);
+ collection = CollectionsExt.immutableSet(false, array);
+ }
+ } else {
+ /*
+ * Do not use the SIS Checked* classes since we don't need type checking
anymore.
+ * Conservatively assumes a List if we are not sure to have a Set
since the list
+ * is less destructive (no removal of duplicated values).
+ */
+ clones(array);
+ collection = Containers.unmodifiableList(array);
}
+ break;
}
}
return collection;
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -104,8 +104,7 @@ public abstract class ModifiableMetadata
}
/**
- * A flag used for {@link #unmodifiable} in order to specify that
- * {@link #freeze()} is under way.
+ * A sentinel value used for {@link #unmodifiable} in order to specify that {@link #freeze()}
is under way.
*/
private static final ModifiableMetadata FREEZING = new Null();
@@ -134,7 +133,7 @@ public abstract class ModifiableMetadata
* @see #checkWritePermission()
*/
public final boolean isModifiable() {
- return unmodifiable != this;
+ return unmodifiable != this && unmodifiable != FREEZING;
}
/**
@@ -240,10 +239,13 @@ public abstract class ModifiableMetadata
* @see #freeze()
*/
protected void checkWritePermission() throws UnmodifiableMetadataException {
- if (!isModifiable()) {
- throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
+ if (unmodifiable != null) {
+ if (unmodifiable == this) {
+ throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
+ } else if (unmodifiable != FREEZING) {
+ unmodifiable = null;
+ }
}
- unmodifiable = null;
}
/**
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -48,7 +48,7 @@ import org.apache.sis.internal.jaxb.NonM
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
@XmlType(name = "MI_Objective_Type", propOrder = {
@@ -175,23 +175,20 @@ public class DefaultObjective extends IS
@Override
@XmlElement(name = "identifier", required = true)
public Collection<Identifier> getIdentifiers() {
- return NonMarshalledAuthority.excludeOnMarshalling(super.getIdentifiers());
+ return NonMarshalledAuthority.filterOnMarshalling(super.getIdentifiers());
}
/**
* Sets the code used to identify the objective.
*
- * <p>This method overwrites all previous identifiers with the given new values,
- * <strong>except</strong> the XML identifiers ({@linkplain IdentifierSpace#ID
ID},
- * {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>), if any. We do not
overwrite
- * the XML identifiers because they are usually associated to object identity.</p>
+ * <p>XML identifiers ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID
UUID}, <i>etc.</i>),
+ * are not affected by this method, unless they are explicitely provided in the given
collection.</p>
*
* @param newValues The new identifiers values.
*/
- public void setIdentifiers(final Collection<? extends Identifier> newValues) {
- final Collection<Identifier> oldIds = NonMarshalledAuthority.filteredCopy(identifiers);
+ public void setIdentifiers(Collection<? extends Identifier> newValues) {
+ newValues = NonMarshalledAuthority.setMarshallables(identifiers, newValues);
identifiers = writeCollection(newValues, identifiers, Identifier.class);
- NonMarshalledAuthority.replace(identifiers, oldIds);
}
/**
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -63,7 +63,7 @@ import static org.apache.sis.internal.me
* @author Cédric Briançon (Geomatys)
* @author Rémi Maréchal (Geomatys)
* @since 0.3
- * @version 0.5
+ * @version 0.7
* @module
*/
@XmlType(name = "CI_Citation_Type", propOrder = {
@@ -364,29 +364,25 @@ public class DefaultCitation extends ISO
@Override
@XmlElement(name = "identifier")
public Collection<Identifier> getIdentifiers() {
- return NonMarshalledAuthority.excludeOnMarshalling(super.getIdentifiers());
+ return NonMarshalledAuthority.filterOnMarshalling(super.getIdentifiers());
}
/**
* Sets the unique identifier for the resource.
* Example: Universal Product Code (UPC), National Stock Number (NSN).
*
- * <p>This method overwrites all previous identifiers with the given new values,
- * <strong>except</strong> the XML identifiers ({@linkplain IdentifierSpace#ID
ID},
- * {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>), ISBN and ISSN codes,
if any.
- * We do not overwrite the XML identifiers because they are usually associated to object
- * identity, and we do not overwrite ISBN/ISSN codes because they have dedicated setters
- * for compliance with the ISO 19115 model.</p>
+ * <p>XML identifiers ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID
UUID}, <i>etc.</i>),
+ * {@linkplain #getISBN() ISBN} and {@linkplain #getISSN() ISSN} codes are not affected
by this method, unless
+ * they are explicitely provided in the given collection.</p>
*
* @param newValues The new identifiers, or {@code null} if none.
*
* @see #setISBN(String)
* @see #setISSN(String)
*/
- public void setIdentifiers(final Collection<? extends Identifier> newValues) {
- final Collection<Identifier> oldIds = NonMarshalledAuthority.filteredCopy(identifiers);
+ public void setIdentifiers(Collection<? extends Identifier> newValues) {
+ newValues = NonMarshalledAuthority.setMarshallables(identifiers, newValues);
identifiers = writeCollection(newValues, identifiers, Identifier.class);
- NonMarshalledAuthority.replace(identifiers, oldIds);
}
/**
Modified: sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -20,14 +20,13 @@ import java.util.Arrays;
import javax.measure.unit.SI;
import org.opengis.metadata.citation.Role;
import org.opengis.metadata.citation.PresentationForm;
-import org.opengis.util.InternationalString;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTableFormat;
import org.apache.sis.util.iso.SimpleInternationalString;
import org.apache.sis.metadata.iso.content.DefaultBand;
import org.apache.sis.metadata.iso.content.DefaultImageDescription;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
-import org.apache.sis.metadata.iso.citation.DefaultIndividual;
+import org.apache.sis.metadata.iso.citation.DefaultCitationTest;
import org.apache.sis.metadata.iso.citation.DefaultResponsibility;
import org.apache.sis.metadata.iso.content.DefaultAttributeGroup;
import org.apache.sis.metadata.iso.identification.DefaultDataIdentification;
@@ -76,53 +75,41 @@ public final strictfp class TreeTableFor
}
/**
- * Creates the citation to use for testing purpose.
- */
- private static DefaultCitation createCitation() {
- final DefaultCitation citation = new DefaultCitation();
- final InternationalString title = new SimpleInternationalString("Undercurrent");
- citation.setTitle(title);
- citation.setISBN("9782505004509");
- citation.setPresentationForms(asList(
- PresentationForm.DOCUMENT_HARDCOPY,
- PresentationForm.IMAGE_HARDCOPY));
- citation.setAlternateTitles(asList(
- new SimpleInternationalString("Alt A"),
- new SimpleInternationalString("Alt B")));
- citation.setCitedResponsibleParties(asList(
- new DefaultResponsibility(Role.AUTHOR, null, new DefaultIndividual("Testsuya
Toyoda", null, null)),
- new DefaultResponsibility(null, null, new DefaultIndividual("A japanese author",
null, null))));
- return citation;
- }
-
- /**
* Tests the formatting of a {@link DefaultCitation} object.
*/
@Test
public void testCitation() {
- final DefaultCitation citation = createCitation();
+ final DefaultCitation citation = DefaultCitationTest.create();
final String text = format.format(citation.asTreeTable());
assertMultilinesEquals(
"Citation\n" +
- " ├─Title………………………………………………………………………
Undercurrent\n" +
- " ├─Alternate title (1 of 2)…………………… Alt A\n" +
- " ├─Alternate title (2 of 2)…………………… Alt B\n" +
+ " ├─Title……………………………………………………………………………
Undercurrent\n" +
+ " ├─Alternate title…………………………………………………
Andākarento\n" +
" ├─Identifier\n" +
- " │ ├─Code………………………………………………………………
9782505004509\n" +
+ " │ ├─Code……………………………………………………………………
9782505004509\n" +
" │ ├─Authority\n" +
- " │ │ ├─Title…………………………………………………
International Standard Book Number\n" +
- " │ │ └─Alternate title……………………… ISBN\n"
+
- " │ └─Code space………………………………………………
ISBN\n"+
+ " │ │ ├─Title………………………………………………………
International Standard Book Number\n" +
+ " │ │ └─Alternate title……………………………
ISBN\n" +
+ " │ └─Code space……………………………………………………
ISBN\n"+
" ├─Cited responsible party (1 of 2)\n" +
" │ ├─Party\n" +
- " │ │ └─Name……………………………………………………
Testsuya Toyoda\n" +
- " │ └─Role………………………………………………………………
Author\n" +
+ " │ │ └─Name…………………………………………………………
Testsuya Toyoda\n" +
+ " │ └─Role……………………………………………………………………
Author\n" +
" ├─Cited responsible party (2 of 2)\n" +
- " │ └─Party\n" +
- " │ └─Name……………………………………………………
A japanese author\n" +
- " ├─Presentation form (1 of 2)……………… Document hardcopy\n"
+
- " ├─Presentation form (2 of 2)……………… Image hardcopy\n" +
- " └─ISBN…………………………………………………………………………
9782505004509\n", text);
+ " │ ├─Party\n" +
+ " │ │ └─Name…………………………………………………………
Kōdansha\n" +
+ " │ ├─Role……………………………………………………………………
Editor\n" +
+ " │ └─Extent\n" +
+ " │ ├─Description………………………………………
World\n" +
+ " │ └─Geographic element\n" +
+ " │ ├─West bound longitude…… 180°W\n" +
+ " │ ├─East bound longitude…… 180°E\n" +
+ " │ ├─South bound latitude…… 90°S\n" +
+ " │ ├─North bound latitude…… 90°N\n" +
+ " │ └─Extent type code……………… true\n"
+
+ " ├─Presentation form (1 of 2)…………………… Document digital\n"
+
+ " ├─Presentation form (2 of 2)…………………… Document hardcopy\n"
+
+ " └─ISBN………………………………………………………………………………
9782505004509\n", text);
}
/**
Modified: sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -18,12 +18,22 @@ package org.apache.sis.metadata.iso.cita
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
import org.opengis.metadata.Identifier;
+import org.opengis.metadata.citation.Role;
+import org.opengis.metadata.citation.Responsibility;
+import org.opengis.metadata.citation.PresentationForm;
+import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.xml.IdentifierMap;
+import org.apache.sis.metadata.iso.extent.Extents;
import org.apache.sis.metadata.iso.DefaultIdentifier;
+import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.util.iso.DefaultInternationalString;
import org.apache.sis.test.TestCase;
import org.junit.Test;
+import static org.apache.sis.test.TestUtilities.getSingleton;
import static org.junit.Assert.*;
@@ -32,11 +42,36 @@ import static org.junit.Assert.*;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
public final strictfp class DefaultCitationTest extends TestCase {
/**
+ * Creates a citation with an arbitrary title, presentation form and other properties.
+ *
+ * @return An arbitrary citation.
+ *
+ * @since 0.7
+ */
+ public static DefaultCitation create() {
+ final DefaultCitation citation = new DefaultCitation();
+ final DefaultInternationalString title = new DefaultInternationalString();
+ title.add(Locale.JAPANESE, "アンダーカレント");
+ title.add(Locale.ENGLISH, "Undercurrent");
+ citation.setTitle(title);
+ citation.setISBN("9782505004509");
+ citation.setPresentationForms(Arrays.asList(
+ PresentationForm.DOCUMENT_HARDCOPY,
+ PresentationForm.DOCUMENT_DIGITAL));
+ citation.setAlternateTitles(Collections.singleton(
+ new SimpleInternationalString("Andākarento"))); // Actually a different
script of the Japanese title.
+ citation.setCitedResponsibleParties(Arrays.asList(
+ new DefaultResponsibility(Role.AUTHOR, null, new DefaultIndividual("Testsuya
Toyoda", null, null)),
+ new DefaultResponsibility(Role.EDITOR, Extents.WORLD, new DefaultOrganisation("Kōdansha",
null, null, null))));
+ return citation;
+ }
+
+ /**
* Tests the identifier map, which handles ISBN and ISSN codes in a special way.
*/
@Test
@@ -61,11 +96,67 @@ public final strictfp class DefaultCitat
citation.setIdentifiers(Arrays.asList(
new DefaultIdentifier(Citations.NETCDF, "MyNetCDF"),
new DefaultIdentifier(Citations.EPSG, "MyEPSG"),
- new DefaultIdentifier(Citations.ISBN, "MyIgnored"),
+ new DefaultIdentifier(Citations.ISBN, "NewISBN"),
new DefaultIdentifier(Citations.ISSN, "MyISSN")));
- assertEquals("The ISBN value shall not have been overwritten.", "MyISBN", citation.getISBN());
+ assertEquals("The ISBN value shall have been overwritten.", "NewISBN", citation.getISBN());
assertEquals("The ISSN value shall have been added, because new.", "MyISSN", citation.getISSN());
- assertEquals("{NetCDF=“MyNetCDF”, EPSG=“MyEPSG”, ISSN=“MyISSN”, ISBN=“MyISBN”}",
identifierMap.toString());
+ assertEquals("{NetCDF=“MyNetCDF”, EPSG=“MyEPSG”, ISBN=“NewISBN”, ISSN=“MyISSN”}",
identifierMap.toString());
+ }
+
+ /**
+ * Tests {@link DefaultCitation#freeze()}, which is needed for the constants defined
in {@link Citations}.
+ */
+ @Test
+ public void testFreeze() {
+ final DefaultCitation original = create();
+ final DefaultCitation clone = (DefaultCitation) original.unmodifiable(); // This
will invoke 'freeze()'.
+ assertNotSame(original, clone);
+ assertTrue ("original.isModifiable", original.isModifiable());
+ assertFalse( "clone.isModifiable", clone.isModifiable());
+ assertSame ("original.unmodifiable", clone, original.unmodifiable());
+ assertSame ( "clone.unmodifiable", clone, clone.unmodifiable());
+
+ assertSame ("ISBN", original.getISBN(), clone.getISBN());
+ assertSame ("title", original.getTitle(), clone.getTitle());
+ assertSame ("alternateTitle", getSingleton(original.getAlternateTitles()),
+ getSingleton(clone.getAlternateTitles()));
+
+ assertCopy(original.getIdentifiers(), clone.getIdentifiers());
+ assertCopy(original.getCitedResponsibleParties(), clone.getCitedResponsibleParties());
+ assertCopy(original.getPresentationForms(), clone.getPresentationForms());
+ /*
+ * Verify the unique identifier, which is the ISBN code. ISBN and ISSN codes are
handled
+ * in a special way by DefaultCitation (they are instances of SpecializedIdentifier),
but
+ * the should nevertheless be cloned.
+ */
+ final Identifier ide = getSingleton(original.getIdentifiers());
+ final Identifier ida = getSingleton( clone.getIdentifiers());
+ assertNotSame("identifier", ide, ida);
+ assertSame("code", ide.getCode(), ida.getCode());
+ assertSame("authority", ide.getAuthority(), ida.getAuthority());
+ /*
+ * Verify the author metadata.
+ */
+ final Responsibility re = CollectionsExt.first(original.getCitedResponsibleParties());
+ final Responsibility ra = CollectionsExt.first(clone .getCitedResponsibleParties());
+ assertNotSame("citedResponsibleParty", re, ra);
+ assertSame("role", re.getRole(), ra.getRole());
+ assertSame("name", getSingleton(re.getParties()).getName(),
+ getSingleton(ra.getParties()).getName());
+ }
+
+ /**
+ * Verifies that {@code actual} is an unmodifiable copy of {@code expected}.
+ */
+ private static <T> void assertCopy(final Collection<T> expected, final Collection<T>
actual) {
+ assertNotSame("ModifiableMetadata.freeze() shall have copied the collection.", expected,
actual);
+ assertEquals("The copied collection shall have the same content than the original.",
expected, actual);
+ try {
+ actual.add(null);
+ fail("The copied collection shall be unmodifiable.");
+ } catch (UnsupportedOperationException e) {
+ // This is the expected exception.
+ }
}
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -17,12 +17,18 @@
package org.apache.sis.internal.jaxb;
import java.util.Iterator;
+import java.util.List;
import java.util.ArrayList;
+import java.util.Map;
+import java.util.IdentityHashMap;
import java.util.Collection;
+import java.util.Collections;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.apache.sis.internal.simple.CitationConstant;
+import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.internal.util.UnmodifiableArrayList;
+import org.apache.sis.util.collection.Containers;
import org.apache.sis.xml.IdentifierSpace;
@@ -57,7 +63,7 @@ import org.apache.sis.xml.IdentifierSpac
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.6
+ * @version 0.7
* @module
*
* @see IdentifierSpace
@@ -73,6 +79,7 @@ public final class NonMarshalledAuthorit
* mirror the constants defined in the {@link IdentifierSpace} interface
* and {@link org.apache.sis.metadata.iso.citation.DefaultCitation} class.
*/
+ @SuppressWarnings("FieldNameHidesFieldInSuperclass")
public static final byte ID=0, UUID=1, HREF=2, XLINK=3, ISSN=4, ISBN=5;
// If more codes are added, please update readResolve() below.
@@ -103,6 +110,9 @@ public final class NonMarshalledAuthorit
* "special" identifiers (ISO 19139 attributes, ISBN codes...), which are recognized
by
* the implementation class of their authority.
*
+ * <p>This method is used for implementation of {@code getIdentifier()} methods
(singular form)
+ * in public metadata objects.</p>
+ *
* @param <T> The type of object used as identifier values.
* @param identifiers The collection from which to get identifiers, or {@code null}.
* @return The first identifier, or {@code null} if none.
@@ -124,23 +134,26 @@ public final class NonMarshalledAuthorit
* method is used when the given collection is expected to contains only one ISO 19115
* identifier.
*
- * @param <T> The type of object used as identifier values.
+ * <p>This method is used for implementation of {@code setIdentifier(Identifier)}
methods
+ * in public metadata objects.</p>
+ *
+ * @param <T> The type of object used as identifier values.
* @param identifiers The collection in which to add the identifier.
- * @param id The identifier to add, or {@code null}.
+ * @param newValue The identifier to add, or {@code null}.
+ *
+ * @see #setMarshallables(Collection, Collection)
*/
- public static <T extends Identifier> void setMarshallable(final Collection<T>
identifiers, final T id) {
+ public static <T extends Identifier> void setMarshallable(final Collection<T>
identifiers, final T newValue) {
final Iterator<T> it = identifiers.iterator();
while (it.hasNext()) {
final T old = it.next();
- if (old != null) {
- if (old.getAuthority() instanceof NonMarshalledAuthority<?>) {
- continue; // Don't touch this identifier.
- }
+ if (old != null && old.getAuthority() instanceof NonMarshalledAuthority<?>)
{
+ continue; // Don't touch this identifier.
}
it.remove();
}
- if (id != null) {
- identifiers.add(id);
+ if (newValue != null) {
+ identifiers.add(newValue);
}
}
@@ -149,10 +162,15 @@ public final class NonMarshalledAuthorit
* for which the authority is an instance of {@code NonMarshalledAuthority}. This should
exclude
* all {@link org.apache.sis.xml.IdentifierSpace} constants.
*
+ * <p>This method is used for implementation of {@code getIdentifiers()} methods
(plural form)
+ * in public metadata objects. Note that those methods override
+ * {@link org.apache.sis.xml.IdentifiedObject#getIdentifiers()}, which is expected to
return
+ * all identifiers in normal (non-marshalling) usage.</p>
+ *
* @param identifiers The identifiers to filter, or {@code null}.
* @return The identifiers to marshal, or {@code null} if none.
*/
- public static Collection<Identifier> excludeOnMarshalling(Collection<Identifier>
identifiers) {
+ public static Collection<Identifier> filterOnMarshalling(Collection<Identifier>
identifiers) {
if (identifiers != null && Context.isFlagSet(Context.current(), Context.MARSHALLING))
{
int count = identifiers.size();
if (count != 0) {
@@ -170,69 +188,72 @@ public final class NonMarshalledAuthorit
}
/**
- * Returns a collection containing only the identifiers having a {@code NonMarshalledAuthority}.
- * This method is invoked for saving the identifiers that are conceptually stored in
distinct fields
- * (XML identifier, UUID, ISBN, ISSN) before to overwrite the collection of all identifiers
in
- * a metadata object.
- *
- * <p>This method is invoked from {@code setIdentifiers(Collection<Identifier>)}
implementation
- * in {@link org.apache.sis.metadata.iso.ISOMetadata} subclasses as below:</p>
- *
- * {@preformat java
- * final Collection<Identifier> oldIds = NonMarshalledAuthority.filteredCopy(identifiers);
- * identifiers = writeCollection(newValues, identifiers, Identifier.class);
- * NonMarshalledAuthority.replace(identifiers, oldIds);
- * }
+ * Returns a collection containing all marshallable values of {@code newValues}, together
with unmarshallable
+ * values of {@code identifiers}. This method is invoked for preserving the identifiers
that are conceptually
+ * stored in distinct fields (XML identifier, UUID, ISBN, ISSN) when setting the collection
of all identifiers
+ * in a metadata object.
+ *
+ * <p>This method is used for implementation of {@code setIdentifiers(Collection)}
methods
+ * in public metadata objects.</p>
*
- * @param <T> The type of object used as identifier values.
* @param identifiers The metadata internal identifiers collection, or {@code null}
if none.
- * @return The new list containing the filtered identifiers, or {@code null} if none.
+ * @param newValues The identifiers to add, or {@code null}.
+ * @return The collection to set (may be {@code newValues}.
+ *
+ * @see #setMarshallable(Collection, Identifier)
*/
- public static <T extends Identifier> Collection<T> filteredCopy(final Collection<T>
identifiers) {
- Collection<T> filtered = null;
- if (identifiers != null) {
- int remaining = identifiers.size();
- for (final T candidate : identifiers) {
- if (candidate != null && candidate.getAuthority() instanceof NonMarshalledAuthority<?>)
{
- if (filtered == null) {
- filtered = new ArrayList<>(remaining);
- }
- filtered.add(candidate);
+ @SuppressWarnings("null")
+ public static Collection<? extends Identifier> setMarshallables(
+ final Collection<Identifier> identifiers, final Collection<? extends
Identifier> newValues)
+ {
+ int remaining;
+ if (identifiers == null || (remaining = identifiers.size()) == 0) {
+ return newValues;
+ }
+ /*
+ * If there is any identifiers that need to be preserved (XML identifier, UUID, ISBN,
etc.),
+ * remember them. Otherwise there is nothing special to do and we can return the
new values directly.
+ */
+ List<Identifier> toPreserve = null;
+ for (final Identifier id : identifiers) {
+ if (id != null && id.getAuthority() instanceof NonMarshalledAuthority<?>)
{
+ if (toPreserve == null) {
+ toPreserve = new ArrayList<>(remaining);
}
- remaining--;
+ toPreserve.add(id);
}
+ remaining--;
}
- return filtered;
- }
-
- /**
- * Replaces all identifiers in the {@code identifiers} collection having the same
- * {@linkplain Identifier#getAuthority() authority} than the ones in {@code oldIds}.
- * More specifically:
- *
- * <ul>
- * <li>First, remove all {@code identifiers} elements having the same authority
- * than one of the elements in {@code oldIds}.</li>
- * <li>Next, add all {@code oldIds} elements to {@code identifiers}.</li>
- * </ul>
- *
- * @param <T> The type of object used as identifier values.
- * @param identifiers The metadata internal identifiers collection, or {@code null} if
none.
- * @param oldIds The previous filtered identifiers returned by {@link #filteredCopy(Collection)},
- * or {@code null} if none.
- */
- public static <T extends Identifier> void replace(final Collection<T> identifiers,
final Collection<T> oldIds) {
- if (oldIds != null && identifiers != null) {
- for (final T old : oldIds) {
- final Citation authority = old.getAuthority();
- for (final Iterator<T> it=identifiers.iterator(); it.hasNext();) {
- final T id = it.next();
- if (id == null || id.getAuthority() == authority) {
- it.remove();
- }
+ if (toPreserve == null) {
+ return newValues;
+ }
+ /*
+ * We find at least one identifier that may need to be preserved.
+ * We need to create a combination of the two collections.
+ */
+ final Map<Citation,Identifier> authorities = new IdentityHashMap<>(4);
+ final List<Identifier> merged = new ArrayList<>(newValues.size());
+ for (final Identifier id : newValues) {
+ merged.add(id);
+ if (id != null) {
+ final Citation authority = id.getAuthority();
+ if (authority instanceof NonMarshalledAuthority<?>) {
+ authorities.put(authority, id);
}
}
- identifiers.addAll(oldIds);
+ }
+ for (final Identifier id : toPreserve) {
+ if (!authorities.containsKey(id.getAuthority())) {
+ merged.add(id);
+ }
+ }
+ /*
+ * Wraps in an unmodifiable list in case the caller is creating an unmodifiable metadata.
+ */
+ switch (merged.size()) {
+ case 0: return Collections.emptyList();
+ case 1: return Collections.singletonList(merged.get(0));
+ default: return Containers.unmodifiableList(CollectionsExt.toArray(merged, Identifier.class));
}
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -45,10 +45,10 @@ import java.util.Objects;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.5
+ * @version 0.7
* @module
*/
-public final class SpecializedIdentifier<T> implements Identifier, Serializable {
+public final class SpecializedIdentifier<T> implements Identifier, Cloneable, Serializable
{
/**
* For cross-version compatibility.
*/
@@ -247,6 +247,20 @@ public final class SpecializedIdentifier
}
/**
+ * Returns a clone of this identifier.
+ *
+ * @return A shallow clone of this identifier.
+ */
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e); // Should never happen, since we are cloneable.
+ }
+ }
+
+ /**
* Returns a string representation of this identifier.
* Example: {@code Identifier[gco:uuid=“42924124-032a-4dfe-b06e-113e3cb81cf0”]}.
*
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -547,7 +547,6 @@ public class CodeListSet<E extends CodeL
@Override
@SuppressWarnings("unchecked")
public CodeListSet<E> clone() {
- @SuppressWarnings("unchecked")
final CodeListSet<E> clone;
try {
clone = (CodeListSet<E>) super.clone();
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java?rev=1707160&r1=1707159&r2=1707160&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java
[UTF-8] Tue Oct 6 22:31:21 2015
@@ -89,8 +89,7 @@ public final class Containers extends St
*
* @param <E> The base type of elements in the list.
* @param array The array to wrap, or {@code null} if none.
- * @return The given array wrapped in an unmodifiable list, or {@code null} if the given
- * array was null.
+ * @return The given array wrapped in an unmodifiable list, or {@code null} if the given
array was null.
*
* @see java.util.Arrays#asList(Object[])
*/
@@ -149,10 +148,9 @@ public final class Containers extends St
* @param <S> The type of elements in the storage (original) set.
* @param <E> The type of elements in the derived set.
* @param storage The storage set containing the original elements, or {@code null}.
- * @param converter The converter from the elements in the storage set to the elements
- * in the derived set.
- * @return A view over the {@code storage} set containing all elements converted by the
given
- * converter, or {@code null} if {@code storage} was null.
+ * @param converter The converter from the elements in the storage set to the elements
in the derived set.
+ * @return A view over the {@code storage} set containing all elements converted by the
given converter,
+ * or {@code null} if {@code storage} was null.
*
* @see org.apache.sis.util.ObjectConverters#derivedSet(Set, ObjectConverter)
*/
@@ -196,8 +194,8 @@ public final class Containers extends St
* @param storage The storage map containing the original entries, or {@code null}.
* @param keyConverter The converter from the keys in the storage map to the keys in
the derived map.
* @param valueConverter The converter from the values in the storage map to the values
in the derived map.
- * @return A view over the {@code storage} map containing all entries converted by the
given
- * converters, or {@code null} if {@code storage} was null.
+ * @return A view over the {@code storage} map containing all entries converted by the
given converters,
+ * or {@code null} if {@code storage} was null.
*
* @see org.apache.sis.util.ObjectConverters#derivedMap(Map, ObjectConverter, ObjectConverter)
* @see org.apache.sis.util.ObjectConverters#derivedKeys(Map, ObjectConverter, Class)
|