sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1707160 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/metadata/ sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/ sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/ sis-metadata/src...
Date Tue, 06 Oct 2015 22:31:22 GMT
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)




Mime
View raw message