Author: desruisseaux
Date: Wed Oct 7 12:37:16 2015
New Revision: 1707278
URL: http://svn.apache.org/viewvc?rev=1707278&view=rev
Log:
Refactor the IdentifierMap internal implementations in order to allow construction of unmodifiable map (SIS-107).
Added:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java
- copied, changed from r1706793, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java
- copied, changed from r1706793, sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java
Removed:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCasesTest.java
Modified:
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/ISOMetadata.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/gco/PropertyTypeMock.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
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=1707278&r1=1707277&r2=1707278&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] Wed Oct 7 12:37:16 2015
@@ -656,12 +656,12 @@ public abstract class ModifiableMetadata
*/
@SuppressWarnings({"rawtypes","unchecked"})
protected <E> Class<? extends Collection<E>> collectionType(final Class<E> elementType) {
- return (Class) (CodeList.class.isAssignableFrom(elementType) ||
- Enum.class.isAssignableFrom(elementType) ||
- Charset.class.isAssignableFrom(elementType) ||
- String.class == elementType ||
- Locale.class == elementType ||
- Currency.class == elementType
+ return (Class) (CodeList.class.isAssignableFrom(elementType)
+ || Enum.class.isAssignableFrom(elementType)
+ || Charset.class.isAssignableFrom(elementType)
+ || String.class == elementType
+ || Locale.class == elementType
+ || Currency.class == elementType
? Set.class : List.class);
}
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -29,7 +29,8 @@ import org.apache.sis.xml.IdentifierSpac
import org.apache.sis.xml.IdentifiedObject;
import org.apache.sis.metadata.MetadataStandard;
import org.apache.sis.metadata.ModifiableMetadata;
-import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases;
+import org.apache.sis.internal.jaxb.IdentifierMapAdapter;
+import org.apache.sis.internal.jaxb.ModifiableIdentifierMap;
import org.apache.sis.internal.util.Utilities;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.CharSequences;
@@ -52,7 +53,7 @@ import static org.apache.sis.util.collec
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
@XmlTransient
@@ -151,13 +152,14 @@ public class ISOMetadata extends Modifia
*/
identifiers = nonNullCollection(identifiers, Identifier.class);
if (identifiers == null) {
- return IdentifierMapWithSpecialCases.EMPTY;
+ return IdentifierMapAdapter.EMPTY;
}
/*
* We do not cache (for now) the IdentifierMap because it is cheap to create, and if we were
* caching it we would need anyway to check if 'identifiers' still references the same list.
*/
- return new IdentifierMapWithSpecialCases(identifiers);
+ return isModifiable() ? new ModifiableIdentifierMap(identifiers)
+ : new IdentifierMapAdapter(identifiers);
}
@@ -204,7 +206,7 @@ public class ISOMetadata extends Modifia
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
private String getUUID() {
/*
- * IdentifierMapWithSpecialCases will take care of converting UUID to String,
+ * IdentifierMapAdapter will take care of converting UUID to String,
* or to return a previously stored String if it was an unparsable UUID.
*/
return isNullOrEmpty(identifiers) ? null : getIdentifierMap().get(IdentifierSpace.UUID);
@@ -216,9 +218,9 @@ public class ISOMetadata extends Modifia
*/
private void setUUID(final String id) {
/*
- * IdentifierMapWithSpecialCases will take care of converting the String to UUID if possible,
- * or will store the value as a plain String if it can not be converted. In the later case, a
- * warning will be emitted (logged or processed by listeners).
+ * IdentifierMapAdapter will take care of converting the String to UUID if possible, or
+ * will store the value as a plain String if it can not be converted. In the later case,
+ * a warning will be emitted (logged or processed by listeners).
*/
getIdentifierMap().put(IdentifierSpace.UUID, id);
}
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -26,7 +26,7 @@ import javax.xml.bind.annotation.adapter
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.identification.RepresentativeFraction;
-import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases;
+import org.apache.sis.internal.jaxb.ModifiableIdentifierMap;
import org.apache.sis.internal.jaxb.gco.GO_Integer64;
import org.apache.sis.internal.util.CheckedArrayList;
import org.apache.sis.measure.ValueRange;
@@ -326,7 +326,7 @@ public class DefaultRepresentativeFracti
*/
@Override
public IdentifierMap getIdentifierMap() {
- return new IdentifierMapWithSpecialCases(getIdentifiers());
+ return new ModifiableIdentifierMap(getIdentifiers());
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -16,8 +16,10 @@
*/
package org.apache.sis.internal.jaxb;
+import java.net.URI;
import java.util.Set;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
@@ -28,7 +30,7 @@ import java.io.Serializable;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.apache.sis.util.Debug;
-import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.xml.XLink;
import org.apache.sis.xml.IdentifierMap;
import org.apache.sis.xml.IdentifierSpace;
@@ -39,14 +41,21 @@ import java.util.Objects;
/**
- * A map of identifiers which can be used as a helper class for
- * {@link org.apache.sis.xml.IdentifiedObject} implementations.
+ * Implementation of the map of identifiers associated to {@link org.apache.sis.xml.IdentifiedObject} instances.
+ * This base class implements an unmodifiable map, but the {@link ModifiableIdentifierMap} subclass add write
+ * capabilities.
*
* <p>This class works as a wrapper around a collection of identifiers. Because all operations
* are performed by an iteration over the collection elements, this implementation is suitable
* only for small maps (less than 10 elements). Given that objects typically have only one or
* two identifiers, this is considered acceptable.</p>
*
+ * <div class="section">Special cases</div>
+ * The identifiers for the following authorities are handled in a special way:
+ * <ul>
+ * <li>{@link IdentifierSpace#HREF}: handled as a shortcut to {@link XLink#getHRef()}.</li>
+ * </ul>
+ *
* <div class="section">Handling of duplicated authorities</div>
* The collection shall not contain more than one identifier for the same
* {@linkplain Identifier#getAuthority() authority}. However duplications may happen if the user
@@ -65,22 +74,17 @@ import java.util.Objects;
* </ul>
*
* <div class="section">Handling of null identifiers</div>
- * The collection of identifiers shall not contains any null element. This is normally ensured by
+ * The collection of identifiers shall not contain any null element. This is normally ensured by
* the {@link org.apache.sis.metadata.ModifiableMetadata} internal collection implementations.
- * This class performs opportunist null checks as an additional safety. However because we perform
- * those checks only in opportunist ways, the following inconsistencies remain:
- *
- * <ul>
- * <li>{@link #isEmpty()} may return {@code false} when the more accurate {@link #size()}
- * method returns 0.</li>
- * </ul>
+ * This class performs opportunist null checks as an additional safety, but consistency is not
+ * guaranteed. See {@link #size()} for more information.
*
* <div class="section">Thread safety</div>
* This class is thread safe if the underlying identifier collection is thread safe.
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*
* @see org.apache.sis.xml.IdentifiedObject
@@ -106,46 +110,113 @@ public class IdentifierMapAdapter extend
*
* @param identifiers The identifiers to wrap in a map view.
*/
- IdentifierMapAdapter(final Collection<Identifier> identifiers) {
+ public IdentifierMapAdapter(final Collection<Identifier> identifiers) {
this.identifiers = identifiers;
}
/**
- * Removes every entries in the underlying collection.
+ * If the given authority is a special case, returns its {@link NonMarshalledAuthority} integer enum.
+ * Otherwise returns -1. See javadoc for more information about special cases.
*
- * @throws UnsupportedOperationException If the collection of identifiers is unmodifiable.
+ * @param authority A {@link Citation} constant. The type is relaxed to {@code Object}
+ * because the signature of some {@code Map} methods are that way.
*/
- @Override
- public void clear() throws UnsupportedOperationException {
- identifiers.clear();
+ static int specialCase(final Object authority) {
+ if (authority == IdentifierSpace.HREF) return NonMarshalledAuthority.HREF;
+ // A future Apache SIS version may add more special cases here.
+ return -1;
+ }
+
+ /**
+ * Extracts the {@code xlink:href} value from the {@link XLink} if presents.
+ * This method does not test if an explicit {@code xlink:href} identifier exists;
+ * this check must be done by the caller <strong>before</strong> to invoke this method.
+ *
+ * @see ModifiableIdentifierMap#setHRef(URI)
+ */
+ private URI getHRef() {
+ final Identifier identifier = getIdentifier(IdentifierSpace.XLINK);
+ if (identifier instanceof SpecializedIdentifier<?>) {
+ final Object link = ((SpecializedIdentifier<?>) identifier).value;
+ if (link instanceof XLink) {
+ return ((XLink) link).getHRef();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the string representation of the given value, or {@code null} if none.
+ *
+ * @param value The value returned be one of the above {@code getFoo()} methods.
+ */
+ private static String toString(final Object value) {
+ return (value != null) ? value.toString() : null;
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ //////// ////////
+ //////// END OF SPECIAL CASES. ////////
+ //////// ////////
+ //////// Implementation of IdentifierMap methods follow. Each method may ////////
+ //////// have a switch statement over the special cases declared above. ////////
+ //////// ////////
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Whether this map support {@code put} and {@code remove} operations.
+ */
+ boolean isModifiable() {
+ return false;
}
/**
* Returns {@code true} if the collection of identifiers contains at least one element.
* This method does not verify if the collection contains null element (it should not).
- * Consequently, this method may return {@code false} even if the {@link #size()} method
- * returns 0.
*/
@Override
- public boolean isEmpty() {
+ public final boolean isEmpty() {
return identifiers.isEmpty();
}
/**
- * Returns {@code true} if at least one identifier declares the given
- * {@linkplain Identifier#getCode() code}.
+ * Counts the number of entries, ignoring null elements and duplicated authorities.
+ *
+ * <p>Because {@code null} elements are ignored, this method may return 0 even if {@link #isEmpty()}
+ * returns {@code false}. However this inconsistency should not happen in practice because
+ * {@link org.apache.sis.metadata.ModifiableMetadata} internal collection implementations
+ * do not allow null values.</p>
+ */
+ @Override
+ public final int size() {
+ final HashSet<Citation> done = new HashSet<>(hashMapCapacity(identifiers.size()));
+ for (final Identifier identifier : identifiers) {
+ if (identifier != null) {
+ done.add(identifier.getAuthority());
+ }
+ }
+ return done.size();
+ }
+
+ /**
+ * Returns {@code true} if at least one identifier declares the given {@linkplain Identifier#getCode() code}.
*
* @param code The code to search, which should be an instance of {@link String}.
* @return {@code true} if at least one identifier uses the given code.
*/
@Override
- public boolean containsValue(final Object code) {
+ public final boolean containsValue(final Object code) {
if (code instanceof String) {
for (final Identifier identifier : identifiers) {
if (identifier != null && code.equals(identifier.getCode())) {
return true;
}
}
+ return code.equals(toString(getHRef()));
+ // A future Apache SIS version may add more special cases here.
}
return false;
}
@@ -158,14 +229,23 @@ public class IdentifierMapAdapter extend
* @return {@code true} if at least one identifier uses the given authority.
*/
@Override
- public boolean containsKey(final Object authority) {
- return (authority instanceof Citation) && getIdentifier((Citation) authority) != null;
+ public final boolean containsKey(final Object authority) {
+ if (authority instanceof Citation) {
+ if (getIdentifier((Citation) authority) != null) {
+ return true;
+ }
+ switch (specialCase(authority)) {
+ case NonMarshalledAuthority.HREF: return getHRef() != null;
+ // A future Apache SIS version may add more special cases here.
+ }
+ }
+ return false;
}
/**
* Returns the identifier for the given key, or {@code null} if none.
*/
- private Identifier getIdentifier(final Citation authority) {
+ final Identifier getIdentifier(final Citation authority) {
for (final Identifier identifier : identifiers) {
if (identifier != null && Objects.equals(authority, identifier.getAuthority())) {
return identifier;
@@ -180,148 +260,94 @@ public class IdentifierMapAdapter extend
*/
@Override
@SuppressWarnings("unchecked")
- public <T> T getSpecialized(final IdentifierSpace<T> authority) {
- final Identifier identifier = getIdentifier(authority);
- return (identifier instanceof SpecializedIdentifier<?>) ? ((SpecializedIdentifier<T>) identifier).value : null;
- }
-
- /**
- * Returns the code of the first identifier associated with the given authority only if
- * if is <strong>not</strong> a specialized identifier. Otherwise returns {@code null}.
- *
- * <p>This is a helper method for {@link IdentifierMapWithSpecialCases#put(Citation, String)},
- * in order to be able to return the old value if that value was a {@link String} rather than
- * the specialized type. We do not return the string for the specialized case in order to avoid
- * the cost of invoking {@code toString()} on the specialized object (some may be costly). Such
- * call would be useless because {@code IdentifierMapWithSpecialCase} discard the value of this
- * method when it found a specialized type.</p>
- */
- final String getUnspecialized(final Citation authority) {
+ public final <T> T getSpecialized(final IdentifierSpace<T> authority) {
final Identifier identifier = getIdentifier(authority);
- if (identifier != null && !(identifier instanceof SpecializedIdentifier<?>)) {
- return identifier.getCode();
+ if (identifier instanceof SpecializedIdentifier<?>) {
+ return ((SpecializedIdentifier<T>) identifier).value;
+ }
+ switch (specialCase(authority)) {
+ case NonMarshalledAuthority.HREF: return (T) getHRef();
+ // A future Apache SIS version may add more special cases here.
}
return null;
}
/**
* Returns the code of the first identifier associated with the given
- * {@linkplain Identifier#getAuthority() authority}, or {@code null}
- * if no identifier was found.
+ * {@linkplain Identifier#getAuthority() authority}, or {@code null} if no identifier was found.
*
* @param authority The authority to search, which should be an instance of {@link Citation}.
* @return The code of the identifier for the given authority, or {@code null} if none.
*/
@Override
- public String get(final Object authority) {
+ public final String get(final Object authority) {
if (authority instanceof Citation) {
final Identifier identifier = getIdentifier((Citation) authority);
if (identifier != null) {
return identifier.getCode();
}
+ switch (specialCase(authority)) {
+ case NonMarshalledAuthority.HREF: return toString(getHRef());
+ // A future Apache SIS version may add more special cases here.
+ }
}
return null;
}
/**
* Removes all identifiers associated with the given {@linkplain Identifier#getAuthority() authority}.
- * The default implementation delegates to {@link #put(Citation, String)} with a {@code null} value.
*
* @param authority The authority to search, which should be an instance of {@link Citation}.
* @return The code of the identifier for the given authority, or {@code null} if none.
+ * @throws UnsupportedOperationException if the collection of identifiers is unmodifiable.
*/
@Override
- public String remove(final Object authority) {
- return (authority instanceof Citation) ? put((Citation) authority, null) : null;
+ public String remove(Object authority) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Removes every entries in the underlying collection.
+ *
+ * @throws UnsupportedOperationException if the collection of identifiers is unmodifiable.
+ */
+ @Override
+ public void clear() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
}
/**
* Sets the code of the identifier having the given authority to the given value.
- * If no identifier is found for the given authority, a new one is created. If
- * more than one identifier is found for the given authority, then all previous
- * identifiers may be removed in order to ensure that the new entry will be the
- * first entry, so it can be find by the {@code get} method.
+ * If no identifier is found for the given authority, a new one is created.
+ * If more than one identifier is found for the given authority, then all previous identifiers may be removed
+ * in order to ensure that the new entry will be the first entry, so it can be find by the {@code get} method.
*
* @param authority The authority for which to set the code.
* @param code The new code for the given authority, or {@code null} for removing the entry.
* @return The previous code for the given authority, or {@code null} if none.
+ * @throws UnsupportedOperationException if the collection of identifiers is unmodifiable.
*/
@Override
- public String put(final Citation authority, final String code)
- throws UnsupportedOperationException
- {
- ArgumentChecks.ensureNonNull("authority", authority);
- String old = null;
- final Iterator<? extends Identifier> it = identifiers.iterator();
- while (it.hasNext()) {
- final Identifier identifier = it.next();
- if (identifier == null) {
- it.remove(); // Opportunist cleaning, but should not happen.
- } else if (Objects.equals(authority, identifier.getAuthority())) {
- if (code != null && identifier instanceof IdentifierMapEntry) {
- return ((IdentifierMapEntry) identifier).setValue(code);
- // No need to suppress other occurrences of the key (if any)
- // because we made a replacement in the first entry, so the
- // new value will be visible by the getter methods.
- }
- if (old == null) {
- old = identifier.getCode();
- }
- it.remove();
- // Continue the iteration in order to remove all other occurrences,
- // in order to ensure that the getter methods will see the new value.
- }
- }
- if (code != null) {
- identifiers.add(SpecializedIdentifier.parse(authority, code));
- }
- return old;
+ public String put(Citation authority, String code) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
}
/**
* Sets the identifier associated with the given authority, and returns the previous value.
*/
@Override
- public <T> T putSpecialized(final IdentifierSpace<T> authority, final T value)
- throws UnsupportedOperationException
- {
- ArgumentChecks.ensureNonNull("authority", authority);
- T old = null;
- final Iterator<? extends Identifier> it = identifiers.iterator();
- while (it.hasNext()) {
- final Identifier identifier = it.next();
- if (identifier == null) {
- it.remove(); // Opportunist cleaning, but should not happen.
- } else if (Objects.equals(authority, identifier.getAuthority())) {
- if (identifier instanceof SpecializedIdentifier<?>) {
- @SuppressWarnings("unchecked")
- final SpecializedIdentifier<T> id = (SpecializedIdentifier<T>) identifier;
- if (old == null) {
- old = id.value;
- }
- if (value != null) {
- id.value = value;
- return old;
- // No need to suppress other occurrences of the key (if any)
- // because we made a replacement in the first entry, so the
- // new value will be visible by the getter methods.
- }
- }
- it.remove();
- // Continue the iteration in order to remove all other occurrences,
- // in order to ensure that the getter methods will see the new value.
- }
- }
- if (value != null) {
- identifiers.add(new SpecializedIdentifier<>(authority, value));
- }
- return old;
+ public <T> T putSpecialized(IdentifierSpace<T> authority, T value) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
}
/**
* Returns a view over the collection of identifiers. This view supports removal operation
* if the underlying collection of identifiers supports the {@link Iterator#remove()} method.
*
+ * <p>If the backing identifier collection contains null entries, those entries will be ignored.
+ * If the backing collection contains many entries for the same authority, then only the first
+ * occurrence is included.</p>
+ *
* @return A view over the collection of identifiers.
*/
@Override
@@ -332,83 +358,35 @@ public class IdentifierMapAdapter extend
* fields if the underlying list is thread-safe. Furthermore, IdentifierMapAdapter are temporary
* objects anyway in the current ISOMetadata implementation.
*/
- return new Entries(identifiers);
- }
-
- /**
- * The view returned by {@link IdentifierMapAdapter#entrySet()}. If the backing identifier collection
- * contains null entries, those entries will be ignored. If the backing collection contains many entries
- * for the same authority, then only the first occurrence is retained.
- *
- * @author Martin Desruisseaux (Geomatys)
- * @since 0.3
- * @version 0.3
- * @module
- */
- private static final class Entries extends AbstractSet<Entry<Citation,String>> {
- /**
- * The identifiers to wrap in a set of entries view. This is a reference
- * to the same collection than {@link IdentifierMapAdapter#identifiers}.
- */
- private final Collection<? extends Identifier> identifiers;
-
- /**
- * Creates a new view over the collection of identifiers.
- *
- * @param identifiers The identifiers to wrap in a set of entries view.
- */
- Entries(final Collection<? extends Identifier> identifiers) {
- this.identifiers = identifiers;
- }
-
- /**
- * Same implementation than {@link IdentifierMapAdapter#clear()}.
- */
- @Override
- public void clear() throws UnsupportedOperationException {
- identifiers.clear();
- }
+ return new AbstractSet<Entry<Citation,String>>() {
+ /** Delegates to the enclosing class. */
+ @Override public void clear() throws UnsupportedOperationException {
+ IdentifierMapAdapter.this.clear();
+ }
- /**
- * Same implementation than {@link IdentifierMapAdapter#isEmpty()}.
- */
- @Override
- public boolean isEmpty() {
- return identifiers.isEmpty();
- }
+ /** Delegates to the enclosing class. */
+ @Override public boolean isEmpty() {
+ return IdentifierMapAdapter.this.isEmpty();
+ }
- /**
- * Counts the number of entries, ignoring null elements and duplicated authorities.
- * Because {@code null} elements are ignored, this method may return 0 even if
- * {@link #isEmpty()} returns {@code false}.
- */
- @Override
- public int size() {
- final HashMap<Citation,Boolean> done = new HashMap<>(hashMapCapacity(identifiers.size()));
- for (final Identifier identifier : identifiers) {
- if (identifier != null) {
- done.put(identifier.getAuthority(), null);
- }
+ /** Delegates to the enclosing class. */
+ @Override public int size() {
+ return IdentifierMapAdapter.this.size();
}
- return done.size();
- }
- /**
- * Returns an iterator over the (<var>citation</var>, <var>code</var>) entries.
- */
- @Override
- public Iterator<Entry<Citation, String>> iterator() {
- return new Iter(identifiers);
- }
+ /** Returns an iterator over the (<var>citation</var>, <var>code</var>) entries. */
+ @Override public Iterator<Entry<Citation, String>> iterator() {
+ return new Iter(identifiers, isModifiable());
+ }
+ };
}
/**
* The iterator over the (<var>citation</var>, <var>code</var>) entries. This iterator is created by
* the {@link IdentifierMapAdapter.Entries} collection. It extends {@link HashMap} as an opportunist
- * implementation strategy, but users don't need to know this detail.
+ * implementation strategy, but users does not need to know this detail.
*
- * <p>This iterator supports the {@link #remove()} operation if the underlying collection
- * supports it.</p>
+ * <p>This iterator supports the {@link #remove()} operation if the underlying collection supports it.</p>
*
* <p>The map entries are used as a safety against duplicated authority values. The map values
* are non-null only after we iterated over an authority. Then the value is {@link Boolean#TRUE}
@@ -416,7 +394,7 @@ public class IdentifierMapAdapter extend
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
@SuppressWarnings("serial") // Not intended to be serialized.
@@ -439,11 +417,17 @@ public class IdentifierMapAdapter extend
private transient Citation authority;
/**
+ * {@code true} if the iterator should support the {@link #remove()} operation.
+ */
+ private final boolean isModifiable;
+
+ /**
* Creates a new iterator for the given collection of identifiers.
*/
- Iter(final Collection<? extends Identifier> identifiers) {
+ Iter(final Collection<? extends Identifier> identifiers, final boolean isModifiable) {
super(hashMapCapacity(identifiers.size()));
this.identifiers = identifiers.iterator();
+ this.isModifiable = isModifiable;
}
/**
@@ -518,6 +502,9 @@ public class IdentifierMapAdapter extend
*/
@Override
public void remove() throws IllegalStateException {
+ if (!isModifiable) {
+ throw new UnsupportedOperationException();
+ }
final Iterator<? extends Identifier> it = identifiers;
if (it == null || next != null) {
throw new IllegalStateException();
Copied: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java (from r1706793, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java?p2=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java&p1=sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java&r1=1706793&r2=1707278&rev=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -16,211 +16,108 @@
*/
package org.apache.sis.internal.jaxb;
-import java.util.Set;
-import java.util.HashMap;
+import java.net.URI;
import java.util.Iterator;
import java.util.Collection;
-import java.util.Collections;
-import java.util.AbstractSet;
-import java.util.AbstractMap;
-import java.util.NoSuchElementException;
-import java.io.Serializable;
+import java.net.URISyntaxException;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
-import org.apache.sis.util.Debug;
import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.xml.IdentifierMap;
import org.apache.sis.xml.IdentifierSpace;
-
-import static org.apache.sis.util.collection.Containers.hashMapCapacity;
+import org.apache.sis.xml.ValueConverter;
+import org.apache.sis.xml.XLink;
// Branch-dependent imports
import java.util.Objects;
/**
- * A map of identifiers which can be used as a helper class for
- * {@link org.apache.sis.xml.IdentifiedObject} implementations.
- *
- * <p>This class works as a wrapper around a collection of identifiers. Because all operations
- * are performed by an iteration over the collection elements, this implementation is suitable
- * only for small maps (less than 10 elements). Given that objects typically have only one or
- * two identifiers, this is considered acceptable.</p>
- *
- * <div class="section">Handling of duplicated authorities</div>
- * The collection shall not contain more than one identifier for the same
- * {@linkplain Identifier#getAuthority() authority}. However duplications may happen if the user
- * has direct access to the list, for example through {@link Citation#getIdentifiers()}. If such
- * duplication is found, then this map implementation applies the following rules:
- *
- * <ul>
- * <li>All getter methods (including the iterators and the values returned by the {@code put}
- * and {@code remove} methods) return only the identifier code associated to the first
- * occurrence of each authority. Any subsequent occurrences of the same authorities are
- * silently ignored.</li>
- * <li>All setter methods <em>may</em> affect <em>all</em> identifiers previously associated to
- * the given authority, not just the first occurrence. The only guarantee is that the list
- * is update in such a way that the effect of setter methods are visible to subsequent calls
- * to getter methods.</li>
- * </ul>
- *
- * <div class="section">Handling of null identifiers</div>
- * The collection of identifiers shall not contains any null element. This is normally ensured by
- * the {@link org.apache.sis.metadata.ModifiableMetadata} internal collection implementations.
- * This class performs opportunist null checks as an additional safety. However because we perform
- * those checks only in opportunist ways, the following inconsistencies remain:
- *
- * <ul>
- * <li>{@link #isEmpty()} may return {@code false} when the more accurate {@link #size()}
- * method returns 0.</li>
- * </ul>
+ * A map of identifiers which support {@code put} and {@code remove} operations.
*
* <div class="section">Thread safety</div>
* This class is thread safe if the underlying identifier collection is thread safe.
*
* @author Martin Desruisseaux (Geomatys)
- * @since 0.3
- * @version 0.3
+ * @since 0.7
+ * @version 0.7
* @module
*
* @see org.apache.sis.xml.IdentifiedObject
*/
-public class IdentifierMapAdapter extends AbstractMap<Citation,String> implements IdentifierMap, Serializable {
+public final class ModifiableIdentifierMap extends IdentifierMapAdapter {
/**
* For cross-version compatibility.
*/
- private static final long serialVersionUID = -1445849218952061605L;
-
- /**
- * An immutable empty instance.
- */
- public static final IdentifierMap EMPTY = new IdentifierMapAdapter(Collections.<Identifier>emptySet());
-
- /**
- * The identifiers to wrap in a map view.
- */
- public final Collection<Identifier> identifiers;
+ private static final long serialVersionUID = -80325787192055778L;
/**
* Creates a new map which will be a view over the given identifiers.
*
* @param identifiers The identifiers to wrap in a map view.
*/
- IdentifierMapAdapter(final Collection<Identifier> identifiers) {
- this.identifiers = identifiers;
+ public ModifiableIdentifierMap(final Collection<Identifier> identifiers) {
+ super(identifiers);
}
/**
- * Removes every entries in the underlying collection.
+ * Sets the {@code xlink:href} value, which may be null. If an explicit {@code xlink:href} identifier exists,
+ * then it will removed before to set the new {@code href} in the {@link XLink} object. The intend is to give
+ * precedence to the {@link XLink#getHRef()} property in every cases where the {@code href} is parsable as a
+ * {@link URI}, and use the value associated to the {@code HREF} key only as a fallback when the string can not
+ * be parsed.
*
- * @throws UnsupportedOperationException If the collection of identifiers is unmodifiable.
- */
- @Override
- public void clear() throws UnsupportedOperationException {
- identifiers.clear();
- }
-
- /**
- * Returns {@code true} if the collection of identifiers contains at least one element.
- * This method does not verify if the collection contains null element (it should not).
- * Consequently, this method may return {@code false} even if the {@link #size()} method
- * returns 0.
- */
- @Override
- public boolean isEmpty() {
- return identifiers.isEmpty();
- }
-
- /**
- * Returns {@code true} if at least one identifier declares the given
- * {@linkplain Identifier#getCode() code}.
+ * @param href The new value, or {@code null} for removing the value.
+ * @return The previous value, or {@code null} if none.
*
- * @param code The code to search, which should be an instance of {@link String}.
- * @return {@code true} if at least one identifier uses the given code.
+ * @see #getHRef()
*/
- @Override
- public boolean containsValue(final Object code) {
- if (code instanceof String) {
- for (final Identifier identifier : identifiers) {
- if (identifier != null && code.equals(identifier.getCode())) {
- return true;
+ private URI setHRef(final URI href) {
+ URI old = store(IdentifierSpace.HREF, null);
+ final Identifier identifier = getIdentifier(IdentifierSpace.XLINK);
+ if (identifier instanceof SpecializedIdentifier<?>) {
+ final Object link = ((SpecializedIdentifier<?>) identifier).value;
+ if (link instanceof XLink) {
+ if (old == null) {
+ old = ((XLink) link).getHRef();
}
+ ((XLink) link).setHRef(href);
+ return old;
}
}
- return false;
+ if (href != null) {
+ final XLink link = new XLink();
+ link.setHRef(href);
+ store(IdentifierSpace.XLINK, link);
+ }
+ return old;
}
- /**
- * Returns {@code true} if at least one identifier declares the given
- * {@linkplain Identifier#getAuthority() authority}.
- *
- * @param authority The authority to search, which should be an instance of {@link Citation}.
- * @return {@code true} if at least one identifier uses the given authority.
- */
- @Override
- public boolean containsKey(final Object authority) {
- return (authority instanceof Citation) && getIdentifier((Citation) authority) != null;
- }
- /**
- * Returns the identifier for the given key, or {@code null} if none.
- */
- private Identifier getIdentifier(final Citation authority) {
- for (final Identifier identifier : identifiers) {
- if (identifier != null && Objects.equals(authority, identifier.getAuthority())) {
- return identifier;
- }
- }
- return null;
- }
- /**
- * Returns the identifier associated with the given authority,
- * or {@code null} if no specialized identifier was found.
- */
- @Override
- @SuppressWarnings("unchecked")
- public <T> T getSpecialized(final IdentifierSpace<T> authority) {
- final Identifier identifier = getIdentifier(authority);
- return (identifier instanceof SpecializedIdentifier<?>) ? ((SpecializedIdentifier<T>) identifier).value : null;
- }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ //////// ////////
+ //////// END OF SPECIAL CASES. ////////
+ //////// ////////
+ //////// Implementation of IdentifierMap methods follow. Each method may ////////
+ //////// have a switch statement over the special cases declared above. ////////
+ //////// ////////
+ ////////////////////////////////////////////////////////////////////////////////////////
/**
- * Returns the code of the first identifier associated with the given authority only if
- * if is <strong>not</strong> a specialized identifier. Otherwise returns {@code null}.
- *
- * <p>This is a helper method for {@link IdentifierMapWithSpecialCases#put(Citation, String)},
- * in order to be able to return the old value if that value was a {@link String} rather than
- * the specialized type. We do not return the string for the specialized case in order to avoid
- * the cost of invoking {@code toString()} on the specialized object (some may be costly). Such
- * call would be useless because {@code IdentifierMapWithSpecialCase} discard the value of this
- * method when it found a specialized type.</p>
+ * Returns {@code true} since this map support {@code put} and {@code remove} operations.
*/
- final String getUnspecialized(final Citation authority) {
- final Identifier identifier = getIdentifier(authority);
- if (identifier != null && !(identifier instanceof SpecializedIdentifier<?>)) {
- return identifier.getCode();
- }
- return null;
+ @Override
+ final boolean isModifiable() {
+ return true;
}
/**
- * Returns the code of the first identifier associated with the given
- * {@linkplain Identifier#getAuthority() authority}, or {@code null}
- * if no identifier was found.
- *
- * @param authority The authority to search, which should be an instance of {@link Citation}.
- * @return The code of the identifier for the given authority, or {@code null} if none.
+ * Removes every entries in the underlying collection.
*/
@Override
- public String get(final Object authority) {
- if (authority instanceof Citation) {
- final Identifier identifier = getIdentifier((Citation) authority);
- if (identifier != null) {
- return identifier.getCode();
- }
- }
- return null;
+ public void clear() {
+ identifiers.clear();
}
/**
@@ -237,21 +134,53 @@ public class IdentifierMapAdapter extend
/**
* Sets the code of the identifier having the given authority to the given value.
- * If no identifier is found for the given authority, a new one is created. If
- * more than one identifier is found for the given authority, then all previous
- * identifiers may be removed in order to ensure that the new entry will be the
- * first entry, so it can be find by the {@code get} method.
+ * If no identifier is found for the given authority, a new one is created.
+ * If more than one identifier is found for the given authority, then all previous identifiers may be removed
+ * in order to ensure that the new entry will be the first entry, so it can be find by the {@code get} method.
+ *
+ * <p>If the given {@code authority} is {@code HREF} and if the given string is parsable as a {@link URI},
+ * then this method will actually store the value as the {@link XLink#getHRef()} property of the {@code XLink}
+ * associated to the {@code XLINK} key. Only if the given string can not be parsed, then the value is stored
+ * <cite>as-is</cite> under the {@code HREF} key.</p>
*
* @param authority The authority for which to set the code.
* @param code The new code for the given authority, or {@code null} for removing the entry.
* @return The previous code for the given authority, or {@code null} if none.
*/
@Override
- public String put(final Citation authority, final String code)
- throws UnsupportedOperationException
- {
+ public String put(final Citation authority, final String code) {
ArgumentChecks.ensureNonNull("authority", authority);
- String old = null;
+ String previous = null;
+ Object discarded = null;
+ switch (specialCase(authority)) {
+ case NonMarshalledAuthority.HREF: {
+ URI uri = null;
+ if (code != null) {
+ final Context context = Context.current();
+ final ValueConverter converter = Context.converter(context);
+ try {
+ uri = converter.toURI(context, code);
+ } catch (URISyntaxException e) {
+ SpecializedIdentifier.parseFailure(context, code, URI.class, e);
+ discarded = setHRef(null);
+ break; // Fallback on generic code below.
+ }
+ }
+ final Identifier identifier = getIdentifier(authority);
+ uri = setHRef(uri);
+ if (uri != null) {
+ previous = uri.toString();
+ } else if (identifier != null) {
+ previous = identifier.getCode();
+ }
+ return previous;
+ }
+ // A future Apache SIS version may add more special cases here.
+ }
+ /*
+ * Generic code to be executed when the given authority is not one of the special case,
+ * or when it was a special case but parsing of the given string failed.
+ */
final Iterator<? extends Identifier> it = identifiers.iterator();
while (it.hasNext()) {
final Identifier identifier = it.next();
@@ -264,8 +193,8 @@ public class IdentifierMapAdapter extend
// because we made a replacement in the first entry, so the
// new value will be visible by the getter methods.
}
- if (old == null) {
- old = identifier.getCode();
+ if (previous == null) {
+ previous = identifier.getCode();
}
it.remove();
// Continue the iteration in order to remove all other occurrences,
@@ -275,16 +204,45 @@ public class IdentifierMapAdapter extend
if (code != null) {
identifiers.add(SpecializedIdentifier.parse(authority, code));
}
- return old;
+ if (previous == null && discarded != null) {
+ previous = discarded.toString();
+ }
+ return previous;
}
/**
* Sets the identifier associated with the given authority, and returns the previous value.
+ *
+ * <p>If the given {@code authority} is {@code HREF}, then this method will actually store the value
+ * as the {@link XLink#getHRef()} property of the {@code XLink} associated to the {@code XLINK} key.
+ * The previous {@code HREF} value, if any, is discarded.</p>
+ *
+ * @param <T> The identifier type.
+ * @param authority The namespace with which the given identifier is to be associated.
+ * @param value The identifier to be associated with the given namespace.
+ * @return The previous identifier associated with {@code authority}, or {@code null}
+ * if there was no mapping of the specialized type for {@code authority}.
*/
@Override
- public <T> T putSpecialized(final IdentifierSpace<T> authority, final T value)
- throws UnsupportedOperationException
- {
+ @SuppressWarnings("unchecked")
+ public <T> T putSpecialized(final IdentifierSpace<T> authority, final T value) {
+ switch (specialCase(authority)) {
+ default: return store(authority, value);
+ case NonMarshalledAuthority.HREF: return (T) setHRef((URI) value);
+ // A future Apache SIS version may add more special cases here.
+ }
+ }
+
+ /**
+ * Sets the identifier associated with the given authority, without processing for special cases.
+ *
+ * @param <T> The identifier type.
+ * @param authority The namespace with which the given identifier is to be associated.
+ * @param value The identifier to be associated with the given namespace.
+ * @return The previous identifier associated with {@code authority}, or {@code null}
+ * if there was no mapping of the specialized type for {@code authority}.
+ */
+ private <T> T store(final IdentifierSpace<T> authority, final T value) {
ArgumentChecks.ensureNonNull("authority", authority);
T old = null;
final Iterator<? extends Identifier> it = identifiers.iterator();
@@ -317,240 +275,4 @@ public class IdentifierMapAdapter extend
}
return old;
}
-
- /**
- * Returns a view over the collection of identifiers. This view supports removal operation
- * if the underlying collection of identifiers supports the {@link Iterator#remove()} method.
- *
- * @return A view over the collection of identifiers.
- */
- @Override
- public Set<Entry<Citation,String>> entrySet() {
- /*
- * Do not cache the entries set because if is very cheap to create and not needed very often.
- * Not caching allows this implementation to be thread-safe without synchronization or volatile
- * fields if the underlying list is thread-safe. Furthermore, IdentifierMapAdapter are temporary
- * objects anyway in the current ISOMetadata implementation.
- */
- return new Entries(identifiers);
- }
-
- /**
- * The view returned by {@link IdentifierMapAdapter#entrySet()}. If the backing identifier collection
- * contains null entries, those entries will be ignored. If the backing collection contains many entries
- * for the same authority, then only the first occurrence is retained.
- *
- * @author Martin Desruisseaux (Geomatys)
- * @since 0.3
- * @version 0.3
- * @module
- */
- private static final class Entries extends AbstractSet<Entry<Citation,String>> {
- /**
- * The identifiers to wrap in a set of entries view. This is a reference
- * to the same collection than {@link IdentifierMapAdapter#identifiers}.
- */
- private final Collection<? extends Identifier> identifiers;
-
- /**
- * Creates a new view over the collection of identifiers.
- *
- * @param identifiers The identifiers to wrap in a set of entries view.
- */
- Entries(final Collection<? extends Identifier> identifiers) {
- this.identifiers = identifiers;
- }
-
- /**
- * Same implementation than {@link IdentifierMapAdapter#clear()}.
- */
- @Override
- public void clear() throws UnsupportedOperationException {
- identifiers.clear();
- }
-
- /**
- * Same implementation than {@link IdentifierMapAdapter#isEmpty()}.
- */
- @Override
- public boolean isEmpty() {
- return identifiers.isEmpty();
- }
-
- /**
- * Counts the number of entries, ignoring null elements and duplicated authorities.
- * Because {@code null} elements are ignored, this method may return 0 even if
- * {@link #isEmpty()} returns {@code false}.
- */
- @Override
- public int size() {
- final HashMap<Citation,Boolean> done = new HashMap<>(hashMapCapacity(identifiers.size()));
- for (final Identifier identifier : identifiers) {
- if (identifier != null) {
- done.put(identifier.getAuthority(), null);
- }
- }
- return done.size();
- }
-
- /**
- * Returns an iterator over the (<var>citation</var>, <var>code</var>) entries.
- */
- @Override
- public Iterator<Entry<Citation, String>> iterator() {
- return new Iter(identifiers);
- }
- }
-
- /**
- * The iterator over the (<var>citation</var>, <var>code</var>) entries. This iterator is created by
- * the {@link IdentifierMapAdapter.Entries} collection. It extends {@link HashMap} as an opportunist
- * implementation strategy, but users don't need to know this detail.
- *
- * <p>This iterator supports the {@link #remove()} operation if the underlying collection
- * supports it.</p>
- *
- * <p>The map entries are used as a safety against duplicated authority values. The map values
- * are non-null only after we iterated over an authority. Then the value is {@link Boolean#TRUE}
- * if the identifier has been removed, of {@code Boolean#FALSE} otherwise.</p>
- *
- * @author Martin Desruisseaux (Geomatys)
- * @since 0.3
- * @version 0.3
- * @module
- */
- @SuppressWarnings("serial") // Not intended to be serialized.
- private static final class Iter extends HashMap<Citation,Boolean> implements Iterator<Entry<Citation,String>> {
- /**
- * An iterator over the {@link IdentifierMapAdapter#identifiers} collection,
- * or (@code null} if we have reached the iteration end.
- */
- private Iterator<? extends Identifier> identifiers;
-
- /**
- * The next entry to be returned by {@link #next()}, or {@code null} if not yet computed.
- * This field will be computed only when {@link #next()} or {@link #hasNext()} is invoked.
- */
- private transient Entry<Citation,String> next;
-
- /**
- * The current authority. Used only for removal operations.
- */
- private transient Citation authority;
-
- /**
- * Creates a new iterator for the given collection of identifiers.
- */
- Iter(final Collection<? extends Identifier> identifiers) {
- super(hashMapCapacity(identifiers.size()));
- this.identifiers = identifiers.iterator();
- }
-
- /**
- * Advances to the next non-null identifier, skips duplicated authorities, wraps the
- * identifier in an entry if needed and stores the result in the {@link #next} field.
- * If we reach the iteration end, then this method set the {@link #identifiers}
- * iterator to {@code null}.
- */
- private void toNext() {
- final Iterator<? extends Identifier> it = identifiers;
- if (it != null) {
- while (it.hasNext()) {
- final Identifier identifier = it.next();
- if (identifier != null) {
- final Citation authority = identifier.getAuthority();
- final Boolean state = put(authority, Boolean.FALSE);
- if (state == null) {
- if (identifier instanceof IdentifierMapEntry) {
- next = (IdentifierMapEntry) identifier;
- } else {
- next = new IdentifierMapEntry.Immutable(authority, identifier.getCode());
- }
- this.authority = authority;
- return;
- }
- if (state) {
- // Found a duplicated entry, and user asked for the
- // removal of that authority.
- it.remove();
- }
- }
- }
- identifiers = null;
- }
- }
-
- /**
- * If we need to search for the next element, fetches it now.
- * Then returns {@code true} if we didn't reached the iteration end.
- */
- @Override
- public boolean hasNext() {
- if (next == null) {
- toNext();
- }
- return identifiers != null;
- }
-
- /**
- * If we need to search for the next element, searches it now. Then set {@link #next}
- * to {@code null} as a flag meaning that next invocations will need to search again
- * for an element, and returns the element that we got.
- */
- @Override
- public Entry<Citation,String> next() throws NoSuchElementException {
- Entry<Citation,String> entry = next;
- if (entry == null) {
- toNext();
- entry = next;
- }
- next = null;
- if (identifiers == null) {
- throw new NoSuchElementException();
- }
- return entry;
- }
-
- /**
- * Removes the last element returned by {@link #next()}. Note that if the {@link #next}
- * field is non-null, that would mean that the iteration has moved since the last call
- * to the {@link #next()} method, in which case the iterator is invalid.
- */
- @Override
- public void remove() throws IllegalStateException {
- final Iterator<? extends Identifier> it = identifiers;
- if (it == null || next != null) {
- throw new IllegalStateException();
- }
- it.remove();
- put(authority, Boolean.TRUE);
- }
- }
-
- /**
- * Overrides the string representation in order to use only the authority title as keys.
- * We do that because the string representations of {@code DefaultCitation} objects are
- * very big.
- *
- * <p>String examples:</p>
- * <ul>
- * <li>{gml:id=“myID”}</li>
- * <li>{gco:uuid=“42924124-032a-4dfe-b06e-113e3cb81cf0”}</li>
- * <li>{xlink:href=“http://www.mydomain.org/myHREF”}</li>
- * </ul>
- *
- * @see SpecializedIdentifier#toString()
- */
- @Debug
- @Override
- public String toString() {
- final StringBuilder buffer = new StringBuilder(50).append('{');
- for (final Entry<Citation,String> entry : entrySet()) {
- if (buffer.length() != 1) {
- buffer.append(", ");
- }
- SpecializedIdentifier.format(buffer, entry.getKey(), entry.getValue());
- }
- return buffer.append('}').toString();
- }
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -206,7 +206,7 @@ public abstract class PropertyType<Value
* </gmd:CI_Citation>
*
* We do not try to parse UUID or XLink objects from String because it should be the job of
- * org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases.put(Citation, String).
+ * org.apache.sis.internal.jaxb.ModifiableIdentifierMap.put(Citation, String).
*/
final IdentifierMap map = ((IdentifiedObject) value).getIdentifierMap();
XLink link = map.getSpecialized(IdentifierSpace.XLINK);
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -26,7 +26,7 @@
* <div class="section">Main content</div>
* {@link org.apache.sis.internal.jaxb.IdentifierMapAdapter} is our internal implementation of
* the public {@link org.apache.sis.xml.IdentifierMap} interface. The actual implementation is
- * usually the {@code IdentifierMapWithSpecialCases} subclass.
+ * usually the {@code ModifiableIdentifierMap} subclass.
*
* <p>{@link org.apache.sis.internal.jaxb.SpecializedIdentifier} wraps {@link org.apache.sis.xml.XLink},
* {@link java.net.URI} and {@link java.util.UUID} as {@link org.opengis.metadata.Identifier} instances.
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -66,6 +66,5 @@ public interface IdentifierMap extends M
* if there was no mapping of the specialized type for {@code authority}.
* @throws UnsupportedOperationException If the identifier map is unmodifiable.
*/
- <T> T putSpecialized(IdentifierSpace<T> authority, T value)
- throws UnsupportedOperationException;
+ <T> T putSpecialized(IdentifierSpace<T> authority, T value) throws UnsupportedOperationException;
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -31,7 +31,7 @@ import org.apache.sis.util.ComparisonMod
import org.apache.sis.util.LenientComparable;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.internal.jaxb.IdentifierMapAdapter;
-import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases;
+import org.apache.sis.internal.jaxb.ModifiableIdentifierMap;
// Branch-dependent imports
import java.util.Objects;
@@ -73,7 +73,7 @@ final class NilObjectHandler implements
asList.add(identifier);
}
}
- attribute = new IdentifierMapWithSpecialCases(asList);
+ attribute = new ModifiableIdentifierMap(asList);
}
/**
Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java?rev=1707278&r1=1707277&r2=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -16,19 +16,14 @@
*/
package org.apache.sis.internal.jaxb;
-import java.net.URI;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Collection;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
-import org.apache.sis.xml.IdentifierMap;
import org.apache.sis.test.TestCase;
import org.junit.Test;
-import static java.util.UUID.fromString;
import static org.apache.sis.test.Assert.*;
import static org.apache.sis.xml.IdentifierSpace.*;
@@ -38,198 +33,11 @@ import static org.apache.sis.xml.Identif
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
public strictfp class IdentifierMapAdapterTest extends TestCase {
/**
- * Creates the {@link IdentifierMapAdapter} instance to test for the given identifiers.
- * This {@code IdentifierMapAdapterTest} class creates {@link IdentifierMapAdapter} instances.
- * Subclasses will override this method in order to create instances of the class to test.
- *
- * @param identifiers The identifiers to wrap in an {@code IdentifierMapAdapter}.
- * @return The {@code IdentifierMapAdapter} to test.
- */
- IdentifierMapAdapter create(final Collection<Identifier> identifiers) {
- return new IdentifierMapAdapter(identifiers);
- }
-
- /**
- * Asserts that the content of the given map is equals to the given content, represented
- * as a string. Subclasses can override this method in order to alter the expected string.
- * This is needed when, using the "special case rules", {@code "href"} have been replaced
- * be {@code "xlink:href"}.
- *
- * @param expected The expected content.
- * @return The map to compare with the expected content.
- */
- void assertMapEquals(final String expected, final Map<Citation,String> map) {
- assertEquals(expected, map.toString());
- }
-
- /**
- * Returns a string representation of the given {@code href} value.
- * The default implementation returns the value unchanged.
- */
- String toHRefString(final String href) {
- return href;
- }
-
- /**
- * Tests read and write operations on an {@link IdentifierMapAdapter}, using a well-formed
- * identifier collection (no null values, no duplicated authorities).
- *
- * <p>This test does not use the {@link IdentifierMap}-specific API.</p>
- */
- @Test
- public void testGetAndPut() {
- final List<Identifier> identifiers = new ArrayList<>();
- final Map<Citation,String> map = create(identifiers);
- assertTrue ("Newly created map shall be empty.", map.isEmpty());
- assertEquals("Newly created map shall be empty.", 0, map.size());
- /*
- * Add two entries, then verify the map content.
- */
- assertTrue(identifiers.add(new IdentifierMapEntry(ID, "myID")));
- assertTrue(identifiers.add(new IdentifierMapEntry(UUID, "myUUID")));
- assertFalse ("After add, map shall not be empty.", map.isEmpty());
- assertEquals("After add, map shall not be empty.", 2, map.size());
- assertEquals("After add, map shall not be empty.", 2, identifiers.size());
- assertTrue ("Shall contain the entry we added.", map.containsKey(ID));
- assertTrue ("Shall contain the entry we added.", map.containsKey(UUID));
- assertFalse ("Shall not contain entry we didn't added.", map.containsKey(HREF));
- assertTrue ("Shall contain the entry we added.", map.containsValue("myID"));
- assertTrue ("Shall contain the entry we added.", map.containsValue("myUUID"));
- assertFalse ("Shall not contain entry we didn't added.", map.containsValue("myHREF"));
- assertEquals("Shall contain the entry we added.", "myID", map.get(ID));
- assertEquals("Shall contain the entry we added.", "myUUID", map.get(UUID));
- assertNull ("Shall not contain entry we didn't added.", map.get(HREF));
- assertMapEquals("{gml:id=“myID”, gco:uuid=“myUUID”}", map);
- /*
- * Alter one entry (no new entry added).
- */
- assertEquals("Shall get the old value.", "myUUID", map.put(UUID, "myNewUUID"));
- assertFalse ("Shall not contain anymore the old value.", map.containsValue("myUUID"));
- assertTrue ("Shall contain the new value.", map.containsValue("myNewUUID"));
- assertMapEquals("{gml:id=“myID”, gco:uuid=“myNewUUID”}", map);
- assertEquals("Map size shall be unchanged.", 2, map.size());
- assertEquals("Map size shall be unchanged.", 2, identifiers.size());
- /*
- * Add a third identifier.
- */
- assertNull ("Shall not contain entry we didn't added.", map.put(HREF, "myHREF"));
- assertTrue ("Shall contain the entry we added.", map.containsValue("myHREF"));
- assertTrue ("Shall contain the entry we added.", map.containsKey(HREF));
- assertMapEquals("{gml:id=“myID”, gco:uuid=“myNewUUID”, xlink:href=“myHREF”}", map);
- assertEquals("Map size shall be updated.", 3, map.size());
- assertEquals("Map size shall be updated.", 3, identifiers.size());
- /*
- * Remove an identifier using the Map.remove(…) API.
- */
- assertEquals("Shall get the old value.", "myNewUUID", map.remove(UUID));
- assertFalse ("Shall not contain the entry we removed.", map.containsValue("myNewUUID"));
- assertFalse ("Shall not contain the entry we removed.", map.containsKey(UUID));
- assertMapEquals("{gml:id=“myID”, xlink:href=“myHREF”}", map);
- assertEquals("Map size shall be updated.", 2, map.size());
- assertEquals("Map size shall be updated.", 2, identifiers.size());
- /*
- * Remove an identifier using the Set.remove(…) API on values.
- */
- assertTrue (map.values().remove(toHRefString("myHREF")));
- assertFalse ("Shall not contain the entry we removed.", map.containsValue("myHREF"));
- assertFalse ("Shall not contain the entry we removed.", map.containsKey(HREF));
- assertMapEquals("{gml:id=“myID”}", map);
- assertEquals("Map size shall be updated.", 1, map.size());
- assertEquals("Map size shall be updated.", 1, identifiers.size());
- /*
- * Remove an identifier using the Set.remove(…) API on keys.
- */
- assertTrue (map.keySet().remove(ID));
- assertFalse ("Shall not contain the entry we removed.", map.containsValue("myID"));
- assertFalse ("Shall not contain the entry we removed.", map.containsKey(ID));
- assertMapEquals("{}", map);
- assertEquals("Map size shall be updated.", 0, map.size());
- assertEquals("Map size shall be updated.", 0, identifiers.size());
- }
-
- /**
- * Tests write operations on an {@link IdentifierMap} using specific API.
- */
- @Test
- public void testPutSpecialized() {
- final List<Identifier> identifiers = new ArrayList<>();
- final IdentifierMap map = create(identifiers);
- final String myID = "myID";
- final java.util.UUID myUUID = fromString("a1eb6e53-93db-4942-84a6-d9e7fb9db2c7");
- final URI myURI = URI.create("http://mylink");
-
- assertNull(map.putSpecialized(ID, myID));
- assertNull(map.putSpecialized(UUID, myUUID));
- assertNull(map.putSpecialized(HREF, myURI));
- assertMapEquals("{gml:id=“myID”,"
- + " gco:uuid=“a1eb6e53-93db-4942-84a6-d9e7fb9db2c7”,"
- + " xlink:href=“http://mylink”}", map);
-
- assertSame(myID, map.getSpecialized(ID));
- assertSame(myUUID, map.getSpecialized(UUID));
- assertSame(myURI, map.getSpecialized(HREF));
- assertEquals("myID", map.get(ID));
- assertEquals("a1eb6e53-93db-4942-84a6-d9e7fb9db2c7", map.get(UUID));
- assertEquals("http://mylink", map.get(HREF));
- }
-
- /**
- * Tests read operations on an {@link IdentifierMap} using specific API.
- */
- @Test
- public void testGetSpecialized() {
- final List<Identifier> identifiers = new ArrayList<>();
- final IdentifierMap map = create(identifiers);
-
- assertNull(map.put(ID, "myID"));
- assertNull(map.put(UUID, "a1eb6e53-93db-4942-84a6-d9e7fb9db2c7"));
- assertNull(map.put(HREF, "http://mylink"));
- assertMapEquals("{gml:id=“myID”,"
- + " gco:uuid=“a1eb6e53-93db-4942-84a6-d9e7fb9db2c7”,"
- + " xlink:href=“http://mylink”}", map);
-
- assertEquals("myID", map.get (ID));
- assertEquals("a1eb6e53-93db-4942-84a6-d9e7fb9db2c7", map.get (UUID));
- assertEquals("http://mylink", map.get (HREF));
- assertEquals("myID", map.getSpecialized(ID));
- assertEquals(URI.create("http://mylink"), map.getSpecialized(HREF));
- assertEquals(fromString("a1eb6e53-93db-4942-84a6-d9e7fb9db2c7"), map.getSpecialized(UUID));
- }
-
- /**
- * Tests the handling of duplicated authorities.
- */
- @Test
- public void testDuplicatedAuthorities() {
- final List<Identifier> identifiers = new ArrayList<>();
- assertTrue(identifiers.add(new IdentifierMapEntry(ID, "myID1")));
- assertTrue(identifiers.add(new IdentifierMapEntry(UUID, "myUUID")));
- assertTrue(identifiers.add(new IdentifierMapEntry(ID, "myID2")));
-
- final IdentifierMap map = create(identifiers);
- assertEquals("Duplicated authorities shall be filtered.", 2, map.size());
- assertEquals("Duplicated authorities shall still exist.", 3, identifiers.size());
- assertEquals("myID1", map.get(ID));
- assertEquals("myUUID", map.get(UUID));
-
- final Iterator<Citation> it = map.keySet().iterator();
- assertTrue(it.hasNext());
- assertSame(ID, it.next());
- it.remove();
- assertTrue(it.hasNext());
- assertSame(UUID, it.next());
- assertFalse("Duplicated authority shall have been removed.", it.hasNext());
-
- assertEquals(1, identifiers.size());
- assertEquals(1, map.size());
- }
-
- /**
* Tests serialization.
*/
@Test
@@ -239,7 +47,7 @@ public strictfp class IdentifierMapAdapt
assertSame(HREF, assertSerializedEquals(HREF));
final List<Identifier> identifiers = new ArrayList<>();
- final Map<Citation,String> map = create(identifiers);
+ final Map<Citation,String> map = new IdentifierMapAdapter(identifiers);
assertTrue(identifiers.add(new IdentifierMapEntry(ID, "myID")));
assertTrue(identifiers.add(new IdentifierMapEntry(UUID, "myUUID")));
Copied: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java (from r1706793, sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java?p2=sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java&p1=sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java&r1=1706793&r2=1707278&rev=1707278&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java [UTF-8] Wed Oct 7 12:37:16 2015
@@ -21,61 +21,59 @@ import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
-import java.util.Collection;
+import java.util.UUID;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.apache.sis.xml.IdentifierMap;
+import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
+import org.apache.sis.xml.XLink;
import org.junit.Test;
import static java.util.UUID.fromString;
import static org.apache.sis.test.Assert.*;
+import static org.apache.sis.test.TestUtilities.getSingleton;
import static org.apache.sis.xml.IdentifierSpace.*;
/**
- * Tests {@link IdentifierMapAdapter}.
+ * Tests {@link ModifiableIdentifierMap}.
*
* @author Martin Desruisseaux (Geomatys)
- * @since 0.3
- * @version 0.3
+ * @since 0.7
+ * @version 0.7
* @module
*/
-public strictfp class IdentifierMapAdapterTest extends TestCase {
+@DependsOn(IdentifierMapAdapterTest.class)
+public final strictfp class ModifiableIdentifierMapTest extends TestCase {
/**
- * Creates the {@link IdentifierMapAdapter} instance to test for the given identifiers.
- * This {@code IdentifierMapAdapterTest} class creates {@link IdentifierMapAdapter} instances.
- * Subclasses will override this method in order to create instances of the class to test.
- *
- * @param identifiers The identifiers to wrap in an {@code IdentifierMapAdapter}.
- * @return The {@code IdentifierMapAdapter} to test.
+ * The HREF string to replace by {@link XLink#toString()}.
*/
- IdentifierMapAdapter create(final Collection<Identifier> identifiers) {
- return new IdentifierMapAdapter(identifiers);
- }
+ private static final String TO_REPLACE = "xlink:href=“";
/**
- * Asserts that the content of the given map is equals to the given content, represented
- * as a string. Subclasses can override this method in order to alter the expected string.
- * This is needed when, using the "special case rules", {@code "href"} have been replaced
- * be {@code "xlink:href"}.
+ * Asserts that the content of the given map is equals to the given content, represented as a string.
+ * This method replaces the {@code xlink:href} value by the {@link XLink#toString()} value before to
+ * compare with the map content. This is needed because the "special case rules" cause the {@code "href"}
+ * identifier to be replaced by {@code "xlink:href"}.
*
* @param expected The expected content.
* @return The map to compare with the expected content.
*/
- void assertMapEquals(final String expected, final Map<Citation,String> map) {
+ private static void assertMapEquals(String expected, final Map<Citation,String> map) {
+ final int start = expected.indexOf(TO_REPLACE);
+ if (start >= 0) {
+ final int end = start + TO_REPLACE.length();
+ final int close = expected.indexOf('”', end);
+ final StringBuilder buffer = new StringBuilder(expected);
+ buffer.replace(close, close+1, "\"]");
+ buffer.replace(start, end, "xlink=XLink[href=\"");
+ expected = buffer.toString();
+ }
assertEquals(expected, map.toString());
}
/**
- * Returns a string representation of the given {@code href} value.
- * The default implementation returns the value unchanged.
- */
- String toHRefString(final String href) {
- return href;
- }
-
- /**
* Tests read and write operations on an {@link IdentifierMapAdapter}, using a well-formed
* identifier collection (no null values, no duplicated authorities).
*
@@ -84,7 +82,7 @@ public strictfp class IdentifierMapAdapt
@Test
public void testGetAndPut() {
final List<Identifier> identifiers = new ArrayList<>();
- final Map<Citation,String> map = create(identifiers);
+ final Map<Citation,String> map = new ModifiableIdentifierMap(identifiers);
assertTrue ("Newly created map shall be empty.", map.isEmpty());
assertEquals("Newly created map shall be empty.", 0, map.size());
/*
@@ -135,7 +133,7 @@ public strictfp class IdentifierMapAdapt
/*
* Remove an identifier using the Set.remove(…) API on values.
*/
- assertTrue (map.values().remove(toHRefString("myHREF")));
+ assertTrue (map.values().remove("XLink[href=\"myHREF\"]"));
assertFalse ("Shall not contain the entry we removed.", map.containsValue("myHREF"));
assertFalse ("Shall not contain the entry we removed.", map.containsKey(HREF));
assertMapEquals("{gml:id=“myID”}", map);
@@ -158,7 +156,7 @@ public strictfp class IdentifierMapAdapt
@Test
public void testPutSpecialized() {
final List<Identifier> identifiers = new ArrayList<>();
- final IdentifierMap map = create(identifiers);
+ final IdentifierMap map = new ModifiableIdentifierMap(identifiers);
final String myID = "myID";
final java.util.UUID myUUID = fromString("a1eb6e53-93db-4942-84a6-d9e7fb9db2c7");
final URI myURI = URI.create("http://mylink");
@@ -184,7 +182,7 @@ public strictfp class IdentifierMapAdapt
@Test
public void testGetSpecialized() {
final List<Identifier> identifiers = new ArrayList<>();
- final IdentifierMap map = create(identifiers);
+ final IdentifierMap map = new ModifiableIdentifierMap(identifiers);
assertNull(map.put(ID, "myID"));
assertNull(map.put(UUID, "a1eb6e53-93db-4942-84a6-d9e7fb9db2c7"));
@@ -211,7 +209,7 @@ public strictfp class IdentifierMapAdapt
assertTrue(identifiers.add(new IdentifierMapEntry(UUID, "myUUID")));
assertTrue(identifiers.add(new IdentifierMapEntry(ID, "myID2")));
- final IdentifierMap map = create(identifiers);
+ final IdentifierMap map = new ModifiableIdentifierMap(identifiers);
assertEquals("Duplicated authorities shall be filtered.", 2, map.size());
assertEquals("Duplicated authorities shall still exist.", 3, identifiers.size());
assertEquals("myID1", map.get(ID));
@@ -230,21 +228,39 @@ public strictfp class IdentifierMapAdapt
}
/**
- * Tests serialization.
+ * Tests explicitely the special handling of {@code href} values.
*/
@Test
- public void testSerialization() {
- assertSame(ID, assertSerializedEquals(ID));
- assertSame(UUID, assertSerializedEquals(UUID));
- assertSame(HREF, assertSerializedEquals(HREF));
+ public void testHRefSubstitution() {
+ final List<Identifier> identifiers = new ArrayList<>();
+ final IdentifierMap map = new ModifiableIdentifierMap(identifiers);
+ assertNull(map.put(HREF, "myHREF"));
+ assertEquals("Shall contain the entry we added.", "myHREF", map.get(HREF));
+
+ // Check the XLink object
+ final XLink link = map.getSpecialized(XLINK);
+ assertEquals("Added href shall be stored as XLink attribute.", "myHREF", String.valueOf(link.getHRef()));
+ assertEquals("Identifier list shall contain the XLink.", link.toString(), getSingleton(identifiers).getCode());
+ // Modidfy the XLink object directly
+ link.setHRef(URI.create("myNewHREF"));
+ assertEquals("Change in XLink shall be reflected in href.", "myNewHREF", map.get(HREF));
+ }
+
+ /**
+ * Tests with UUIDs.
+ */
+ @Test
+ public void testUUIDs() {
final List<Identifier> identifiers = new ArrayList<>();
- final Map<Citation,String> map = create(identifiers);
- assertTrue(identifiers.add(new IdentifierMapEntry(ID, "myID")));
- assertTrue(identifiers.add(new IdentifierMapEntry(UUID, "myUUID")));
+ final IdentifierMap map = new ModifiableIdentifierMap(identifiers);
+ final java.util.UUID id1 = fromString("434f3107-c6d2-4c8c-bb25-553f68641c5c");
+ final java.util.UUID id2 = fromString("42924124-032a-4dfe-b06e-113e3cb81cf0");
+
+ // Add first UUID.
+ assertNull(map.putSpecialized(UUID, id1));
- final Map<Citation,String> copy = assertSerializedEquals(map);
- assertNotSame(map, copy);
- assertEquals(2, copy.size());
+ // Replace UUID by a new one.
+ assertSame(id1, map.putSpecialized(UUID, id2));
}
}
|