sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1480602 [3/15] - in /sis/branches/Android: ./ ide-project/ ide-project/NetBeans/ ide-project/NetBeans/nbproject/ ide-project/eclipse/ sis-app/src/main/java/org/apache/sis/cli/ sis-build-helper/ sis-build-helper/src/main/java/org/apache/sis...
Date Thu, 09 May 2013 12:24:20 GMT
Modified: sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1480602&r1=1480601&r2=1480602&view=diff
==============================================================================
--- sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java [iso-8859-1] (original)
+++ sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java [UTF-8] Thu May  9 12:24:13 2013
@@ -23,65 +23,99 @@ import java.util.Collections;
 import java.util.NoSuchElementException;
 import net.jcip.annotations.ThreadSafe;
 import org.opengis.util.CodeList;
-import org.apache.sis.util.collection.CheckedHashSet;
-import org.apache.sis.util.collection.CheckedArrayList;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.CheckedHashSet;
+import org.apache.sis.internal.util.CheckedArrayList;
 
-import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
-import static org.apache.sis.util.collection.Collections.isNullOrEmpty;
-import static org.apache.sis.util.collection.Collections.hashMapCapacity;
+import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
+import static org.apache.sis.internal.jaxb.Context.isMarshalling;
 
 
 /**
- * Base class for metadata that may (or may not) be modifiable. Implementations will typically
- * provide {@code set*(...)} methods for each corresponding {@code get*()} method. An initially
- * modifiable metadata may become unmodifiable at a later stage (typically after its construction
- * is completed) by the call to the {@link #freeze()} method.
- *
- * {@section Guidline for implementors}
- * Subclasses should follow the pattern below for every {@code get} and {@code set} methods,
+ * Provides convenience methods for support of modifiable properties in metadata implementations.
+ * Implementations typically provide {@code set*(…)} methods for each corresponding {@code get*()}
+ * method. Subclasses can follow the pattern below for every {@code get} and {@code set} methods,
  * with a different processing for singleton value or for {@linkplain Collection collections}.
  *
  * <p>For singleton value:</p>
  *
  * {@preformat java
- *     private Foo property;
+ *     public class MyMetadata {
+ *         private Foo property;
  *
- *     public synchronized Foo getProperty() {
- *         return property;
- *     }
+ *         public Foo getProperty() {
+ *             return property;
+ *         }
  *
- *     public synchronized void setProperty(Foo newValue) {
- *         checkWritePermission();
- *         property = newValue;
+ *         public void setProperty(Foo newValue) {
+ *             checkWritePermission();
+ *             property = newValue;
+ *         }
  *     }
  * }
  *
  * For collections (note that the call to {@link #checkWritePermission()} is implicit):
  *
  * {@preformat java
- *     private Collection<Foo> properties;
+ *     public class MyMetadata {
+ *         private Collection<Foo> properties;
  *
- *     public synchronized Collection<Foo> getProperties() {
- *         return properties = nonNullCollection(properties, Foo.class);
- *     }
+ *         public Collection<Foo> getProperties() {
+ *             return properties = nonNullCollection(properties, Foo.class);
+ *         }
  *
- *     public synchronized void setProperties(Collection<Foo> newValues) {
- *         properties = copyCollection(newValues, properties, Foo.class);
+ *         public void setProperties(Collection<Foo> newValues) {
+ *             properties = writeCollection(newValues, properties, Foo.class);
+ *         }
  *     }
  * }
  *
+ * An initially modifiable metadata may become unmodifiable at a later stage
+ * (typically after its construction is completed) by the call to the
+ * {@link #freeze()} method.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.1)
  * @version 0.3
  * @module
  */
 @ThreadSafe
-public abstract class ModifiableMetadata {
+public abstract class ModifiableMetadata extends AbstractMetadata implements Cloneable {
+    /**
+     * Initial capacity of lists and sets. We use a small value because those
+     * collections will typically contain few elements (often just a singleton).
+     */
+    private static final int INITIAL_CAPACITY = 4;
+
+    /**
+     * A null implementation for the {@link #FREEZING} constant.
+     */
+    private static final class Null extends ModifiableMetadata {
+        @Override public MetadataStandard getStandard() {
+            return null;
+        }
+    }
+
+    /**
+     * A flag used for {@link #unmodifiable} in order to specify that
+     * {@link #freeze()} is under way.
+     */
+    private static final ModifiableMetadata FREEZING = new Null();
+
+    /**
+     * An unmodifiable copy of this metadata, created only when first needed.
+     * If {@code null}, then no unmodifiable entity is available.
+     * If {@code this}, then this entity is itself unmodifiable.
+     *
+     * @see #unmodifiable()
+     */
+    private transient ModifiableMetadata unmodifiable;
+
     /**
      * Constructs an initially empty metadata.
      */
     protected ModifiableMetadata() {
-        super();
     }
 
     /**
@@ -94,7 +128,89 @@ public abstract class ModifiableMetadata
      * @see #checkWritePermission()
      */
     public final boolean isModifiable() {
-        return true; // To be implemented later.
+        return unmodifiable != this;
+    }
+
+    /**
+     * Returns an unmodifiable copy of this metadata. Any attempt to modify a property of the
+     * returned object will throw an {@link UnmodifiableMetadataException}. The state of this
+     * object is not modified.
+     *
+     * <p>This method is useful for reusing the same metadata object as a template.
+     * For example:</p>
+     *
+     * {@preformat java
+     *     DefaultCitation myCitation = new DefaultCitation();
+     *     myCitation.setTitle(new SimpleInternationalString("The title of my book"));
+     *     myCitation.setEdition(new SimpleInternationalString("First edition"));
+     *     final Citation firstEdition = (Citation) myCitation.unmodifiable();
+     *
+     *     myCitation.setEdition(new SimpleInternationalString("Second edition"));
+     *     final Citation secondEdition = (Citation) myCitation.unmodifiable();
+     *     // The title of the second edition is unchanged compared to the first edition.
+     * }
+     *
+     * The default implementation makes the following choice:
+     *
+     * <ul>
+     *   <li>If this metadata is itself unmodifiable, then this method returns {@code this}
+     *       unchanged.</li>
+     *   <li>Otherwise this method {@linkplain #clone() clone} this metadata and
+     *       {@linkplain #freeze() freeze} the clone before to return it.</li>
+     * </ul>
+     *
+     * @return An unmodifiable copy of this metadata.
+     */
+    public AbstractMetadata unmodifiable() {
+        // Reminder: 'unmodifiable' is reset to null by checkWritePermission().
+        if (unmodifiable == null) {
+            final ModifiableMetadata candidate;
+            try {
+                /*
+                 * Need a SHALLOW copy of this metadata, because some properties
+                 * may already be unmodifiable and we don't want to clone them.
+                 */
+                candidate = clone();
+            } catch (CloneNotSupportedException exception) {
+                /*
+                 * The metadata is not cloneable for some reason left to the user
+                 * (for example it may be backed by some external database).
+                 * Assumes that the metadata is unmodifiable.
+                 */
+                Logging.unexpectedException(LOGGER, getClass(), "unmodifiable", exception);
+                return this;
+            }
+            candidate.freeze();
+            // Set the field only after success. The 'unmodifiable' field must
+            // stay null if an exception occurred during clone() or freeze().
+            unmodifiable = candidate;
+        }
+        assert !unmodifiable.isModifiable();
+        return unmodifiable;
+    }
+
+    /**
+     * Declares this metadata and all its properties as unmodifiable. Any attempt to modify a
+     * property after this method call will throw an {@link UnmodifiableMetadataException}.
+     * If this metadata is already unmodifiable, then this method does nothing.
+     *
+     * <p>Subclasses usually don't need to override this method since the default implementation
+     * performs its work using Java reflection.</p>
+     *
+     * @see #isModifiable()
+     * @see #checkWritePermission()
+     */
+    public void freeze() {
+        if (isModifiable()) {
+            ModifiableMetadata success = null;
+            try {
+                unmodifiable = FREEZING;
+                getStandard().freeze(this);
+                success = this;
+            } finally {
+                unmodifiable = success;
+            }
+        }
     }
 
     /**
@@ -108,42 +224,45 @@ public abstract class ModifiableMetadata
      * @see #freeze()
      */
     protected void checkWritePermission() throws UnmodifiableMetadataException {
-        assert Thread.holdsLock(this);
         if (!isModifiable()) {
-            throw new UnmodifiableMetadataException("Unmodifiable metadata"); // TODO: localize
+            throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
         }
+        unmodifiable = null;
     }
 
     /**
-     * Copies the content of one list ({@code source}) into an other ({@code target}).
-     * This method performs the following steps:
+     * Writes the content of the {@code source} collection into the {@code target} list,
+     * creating it if needed. This method performs the following steps:
      *
      * <ul>
      *   <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is
      *       modifiable.</li>
      *   <li>If {@code source} is null or empty, returns {@code null}
-     *       (meaning that the metadata attribute is not provided).</li>
-     *   <li>If {@code target} is null, creates a new {@link List}.</li>
+     *       (meaning that the metadata property is not provided).</li>
+     *   <li>If {@code target} is null, creates a new {@link List}.</li>
      *   <li>Copies the content of the given {@code source} into the target.</li>
      * </ul>
      *
-     * @param  <E>         The type of elements in the list.
+     * @param  <E>         The type represented by the {@code Class} argument.
      * @param  source      The source list, or {@code null}.
      * @param  target      The target list, or {@code null} if not yet created.
      * @param  elementType The base type of elements to put in the list.
-     * @return A list (possibly the {@code target} instance) containing the {@code source}
+     * @return A list (possibly the {@code target} instance) containing the {@code source}
      *         elements, or {@code null} if the source was null.
      * @throws UnmodifiableMetadataException if this metadata is unmodifiable.
      *
      * @see #nonNullList(List, Class)
      */
     @SuppressWarnings("unchecked")
-    protected final <E> List<E> copyList(final Collection<? extends E> source,
+    protected final <E> List<E> writeList(final Collection<? extends E> source,
             List<E> target, final Class<E> elementType)
             throws UnmodifiableMetadataException
     {
-        // See the comments in copyCollection(...) for implementation notes.
+        // See the comments in writeCollection(...) for implementation notes.
         if (source != target) {
+            if (unmodifiable == FREEZING) {
+                return (List<E>) source;
+            }
             checkWritePermission();
             if (isNullOrEmpty(source)) {
                 target = null;
@@ -151,7 +270,7 @@ public abstract class ModifiableMetadata
                 if (target != null) {
                     target.clear();
                 } else {
-                    target = new MutableList<E>(elementType, source.size());
+                    target = new CheckedArrayList<E>(elementType, source.size());
                 }
                 target.addAll(source);
             }
@@ -160,35 +279,38 @@ public abstract class ModifiableMetadata
     }
 
     /**
-     * Copies the content of one Set ({@code source}) into an other ({@code target}).
-     * This method performs the following steps:
+     * Writes the content of the {@code source} collection into the {@code target} set,
+     * creating it if needed. This method performs the following steps:
      *
      * <ul>
      *   <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is
      *       modifiable.</li>
      *   <li>If {@code source} is null or empty, returns {@code null}
-     *       (meaning that the metadata attribute is not provided).</li>
-     *   <li>If {@code target} is null, creates a new {@link Set}.</li>
+     *       (meaning that the metadata property is not provided).</li>
+     *   <li>If {@code target} is null, creates a new {@link Set}.</li>
      *   <li>Copies the content of the given {@code source} into the target.</li>
      * </ul>
      *
-     * @param  <E>         The type of elements in the set.
+     * @param  <E>         The type represented by the {@code Class} argument.
      * @param  source      The source set, or {@code null}.
      * @param  target      The target set, or {@code null} if not yet created.
      * @param  elementType The base type of elements to put in the set.
-     * @return A set (possibly the {@code target} instance) containing the {@code source}
+     * @return A set (possibly the {@code target} instance) containing the {@code source}
      *         elements, or {@code null} if the source was null.
      * @throws UnmodifiableMetadataException if this metadata is unmodifiable.
      *
      * @see #nonNullSet(Set, Class)
      */
     @SuppressWarnings("unchecked")
-    protected final <E> Set<E> copySet(final Collection<? extends E> source,
+    protected final <E> Set<E> writeSet(final Collection<? extends E> source,
             Set<E> target, final Class<E> elementType)
             throws UnmodifiableMetadataException
     {
-        // See the comments in copyCollection(...) for implementation notes.
+        // See the comments in writeCollection(...) for implementation notes.
         if (source != target) {
+            if (unmodifiable == FREEZING) {
+                return (Set<E>) source;
+            }
             checkWritePermission();
             if (isNullOrEmpty(source)) {
                 target = null;
@@ -196,7 +318,7 @@ public abstract class ModifiableMetadata
                 if (target != null) {
                     target.clear();
                 } else {
-                    target = new MutableSet<E>(elementType, source.size());
+                    target = new CheckedHashSet<E>(elementType, source.size());
                 }
                 target.addAll(source);
             }
@@ -205,36 +327,36 @@ public abstract class ModifiableMetadata
     }
 
     /**
-     * Copies the content of one collection ({@code source}) into an other ({@code target}).
-     * This method performs the following steps:
+     * Writes the content of the {@code source} collection into the {@code target} list or set,
+     * creating it if needed. This method performs the following steps:
      *
      * <ul>
      *   <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is
      *       modifiable.</li>
      *   <li>If {@code source} is null or empty, returns {@code null}
-     *       (meaning that the metadata attribute is not provided).</li>
-     *   <li>If {@code target} is null, creates a new {@link Set} or a new {@link List}
+     *       (meaning that the metadata property is not provided).</li>
+     *   <li>If {@code target} is null, creates a new {@link Set} or a new {@link List}
      *       depending on the value returned by {@link #collectionType(Class)}.</li>
      *   <li>Copies the content of the given {@code source} into the target.</li>
      * </ul>
      *
      * {@section Choosing a collection type}
-     * Implementations shall invoke {@link #copyList copyList} or {@link #copySet copySet} methods
+     * Implementations shall invoke {@link #writeList writeList} or {@link #writeSet writeSet} methods
      * instead than this method when the collection type is enforced by ISO specification.
      * When the type is not enforced by the specification, some freedom are allowed at
      * implementor choice. The default implementation invokes {@link #collectionType(Class)}
      * in order to get a hint about whether a {@link List} or a {@link Set} should be used.
      *
-     * @param  <E>         The type of elements in the collection.
+     * @param  <E>         The type represented by the {@code Class} argument.
      * @param  source      The source collection, or {@code null}.
      * @param  target      The target collection, or {@code null} if not yet created.
      * @param  elementType The base type of elements to put in the collection.
-     * @return A collection (possibly the {@code target} instance) containing the {@code source}
+     * @return A collection (possibly the {@code target} instance) containing the {@code source}
      *         elements, or {@code null} if the source was null.
      * @throws UnmodifiableMetadataException if this metadata is unmodifiable.
      */
     @SuppressWarnings("unchecked")
-    protected final <E> Collection<E> copyCollection(final Collection<? extends E> source,
+    protected final <E> Collection<E> writeCollection(final Collection<? extends E> source,
             Collection<E> target, final Class<E> elementType)
             throws UnmodifiableMetadataException
     {
@@ -244,6 +366,14 @@ public abstract class ModifiableMetadata
          * This optimization is required for efficient working of PropertyAccessor.set(...).
          */
         if (source != target) {
+            if (unmodifiable == FREEZING) {
+                /*
+                 * freeze() method is under progress. The source collection is already
+                 * an unmodifiable instance created by unmodifiable(Object).
+                 */
+                assert collectionType(elementType).isInstance(source);
+                return (Collection<E>) source;
+            }
             checkWritePermission();
             if (isNullOrEmpty(source)) {
                 target = null;
@@ -253,9 +383,9 @@ public abstract class ModifiableMetadata
                 } else {
                     final int capacity = source.size();
                     if (useSet(elementType)) {
-                        target = new MutableSet<E>(elementType, capacity);
+                        target = new CheckedHashSet<E>(elementType, capacity);
                     } else {
-                        target = new MutableList<E>(elementType, capacity);
+                        target = new CheckedArrayList<E>(elementType, capacity);
                     }
                 }
                 target.addAll(source);
@@ -265,85 +395,180 @@ public abstract class ModifiableMetadata
     }
 
     /**
+     * Creates a list with the content of the {@code source} collection,
+     * or returns {@code null} if the source is {@code null} or empty.
+     * This is a convenience method for copying fields in subclass copy constructors.
+     *
+     * @param  <E>         The type represented by the {@code Class} argument.
+     * @param  source      The source collection, or {@code null}.
+     * @param  elementType The base type of elements to put in the list.
+     * @return A list containing the {@code source} elements,
+     *         or {@code null} if the source was null or empty.
+     */
+    protected final <E> List<E> copyList(final Collection<? extends E> source, final Class<E> elementType) {
+        if (isNullOrEmpty(source)) {
+            return null;
+        }
+        final List<E> target = new CheckedArrayList<E>(elementType, source.size());
+        target.addAll(source);
+        return target;
+    }
+
+    /**
+     * Creates a set with the content of the {@code source} collection,
+     * or returns {@code null} if the source is {@code null} or empty.
+     * This is a convenience method for copying fields in subclass copy constructors.
+     *
+     * @param  <E>         The type represented by the {@code Class} argument.
+     * @param  source      The source collection, or {@code null}.
+     * @param  elementType The base type of elements to put in the set.
+     * @return A set containing the {@code source} elements,
+     *         or {@code null} if the source was null or empty.
+     */
+    protected final <E> Set<E> copySet(final Collection<? extends E> source, final Class<E> elementType) {
+        if (isNullOrEmpty(source)) {
+            return null;
+        }
+        final Set<E> target = new CheckedHashSet<E>(elementType, source.size());
+        target.addAll(source);
+        return target;
+    }
+
+    /**
+     * Creates a list or set with the content of the {@code source} collection,
+     * or returns {@code null} if the source is {@code null} or empty.
+     * This is a convenience method for copying fields in subclass copy constructors.
+     *
+     * <p>The collection type is selected as described in the
+     * {@link #nonNullCollection(Collection, Class)}.</p>
+     *
+     * @param  <E>         The type represented by the {@code Class} argument.
+     * @param  source      The source collection, or {@code null}.
+     * @param  elementType The base type of elements to put in the collection.
+     * @return A collection containing the {@code source} elements,
+     *         or {@code null} if the source was null or empty.
+     */
+    protected final <E> Collection<E> copyCollection(final Collection<? extends E> source, final Class<E> elementType) {
+        if (isNullOrEmpty(source)) {
+            return null;
+        }
+        final Collection<E> target;
+        final int capacity = source.size();
+        if (useSet(elementType)) {
+            target = new CheckedHashSet<E>(elementType, capacity);
+        } else {
+            target = new CheckedArrayList<E>(elementType, capacity);
+        }
+        target.addAll(source);
+        return target;
+    }
+
+    /**
+     * Creates a singleton list or set containing only the given value, if non-null.
+     * This is a convenience method for initializing fields in subclass constructors.
+     *
+     * <p>The collection type is selected as described in the
+     * {@link #nonNullCollection(Collection, Class)}.</p>
+     *
+     * @param  <E>         The type represented by the {@code Class} argument.
+     * @param  value       The singleton value to put in the returned collection, or {@code null}.
+     * @param  elementType The element type (used only if {@code value} is non-null).
+     * @return A new modifiable collection containing the given value,
+     *         or {@code null} if the given value was null.
+     */
+    protected final <E> Collection<E> singleton(final E value, final Class<E> elementType) {
+        if (value == null) {
+            return null;
+        }
+        final Collection<E> collection;
+        if (useSet(elementType)) {
+            collection = new CheckedHashSet<E>(elementType, INITIAL_CAPACITY);
+        } else {
+            collection = new CheckedArrayList<E>(elementType, INITIAL_CAPACITY);
+        }
+        collection.add(value);
+        return collection;
+    }
+
+    /**
      * Returns the specified list, or a new one if {@code c} is null.
-     * This is a convenience method for implementation of {@code getFoo()}
-     * methods.
+     * This is a convenience method for implementation of {@code getFoo()} methods.
      *
-     * @param  <E> The type of elements in the list.
-     * @param  c The list to checks.
+     * @param  <E> The type represented by the {@code Class} argument.
+     * @param  c The existing list, or {@code null} if the list has not yet been created.
      * @param  elementType The element type (used only if {@code c} is null).
      * @return {@code c}, or a new list if {@code c} is null.
      */
-    // See the comments in nonNullCollection(...) for implementation notes.
     protected final <E> List<E> nonNullList(final List<E> c, final Class<E> elementType) {
-        assert Thread.holdsLock(this);
         if (c != null) {
-            return c;
+            return c.isEmpty() && isMarshalling() ? null : c;
+        }
+        if (isMarshalling()) {
+            return null;
         }
         if (isModifiable()) {
-            return new MutableList<E>(elementType);
+            return new CheckedArrayList<E>(elementType, INITIAL_CAPACITY);
         }
         return Collections.emptyList();
     }
 
     /**
      * Returns the specified set, or a new one if {@code c} is null.
-     * This is a convenience method for implementation of {@code getFoo()}
-     * methods.
+     * This is a convenience method for implementation of {@code getFoo()} methods.
      *
-     * @param  <E> The type of elements in the set.
-     * @param  c The set to checks.
+     * @param  <E> The type represented by the {@code Class} argument.
+     * @param  c The existing set, or {@code null} if the set has not yet been created.
      * @param  elementType The element type (used only if {@code c} is null).
      * @return {@code c}, or a new set if {@code c} is null.
      */
-    // See the comments in nonNullCollection(...) for implementation notes.
     protected final <E> Set<E> nonNullSet(final Set<E> c, final Class<E> elementType) {
-        assert Thread.holdsLock(this);
         if (c != null) {
-            return c;
+            return c.isEmpty() && isMarshalling() ? null : c;
+        }
+        if (isMarshalling()) {
+            return null;
         }
         if (isModifiable()) {
-            return new MutableSet<E>(elementType);
+            return new CheckedHashSet<E>(elementType, INITIAL_CAPACITY);
         }
         return Collections.emptySet();
     }
 
     /**
      * Returns the specified collection, or a new one if {@code c} is null.
-     * This is a convenience method for implementation of {@code getFoo()}
-     * methods.
+     * This is a convenience method for implementation of {@code getFoo()} methods.
      *
      * {@section Choosing a collection type}
-     * Implementations shall invoke {@link #nonNullList nonNullList} or {@link #nonNullSet
-     * nonNullSet} instead than this method when the collection type is enforced by ISO
+     * Implementations shall invoke {@link #nonNullList nonNullList(…)} or {@link #nonNullSet
+     * nonNullSet(…)} instead than this method when the collection type is enforced by ISO
      * specification. When the type is not enforced by the specification, some freedom are
      * allowed at implementor choice. The default implementation invokes
      * {@link #collectionType(Class)} in order to get a hint about whether a {@link List}
      * or a {@link Set} should be used.
      *
-     * @param  <E> The type of elements in the collection.
-     * @param  c The collection to checks.
+     * @param  <E> The type represented by the {@code Class} argument.
+     * @param  c The existing collection, or {@code null} if the collection has not yet been created.
      * @param  elementType The element type (used only if {@code c} is null).
      * @return {@code c}, or a new collection if {@code c} is null.
      */
-    // Despite the javadoc claims, we do not yet return null during copy operations.
-    // However a future version may do so if it appears worth on a performance point of view.
     protected final <E> Collection<E> nonNullCollection(final Collection<E> c, final Class<E> elementType) {
-        assert Thread.holdsLock(this);
         if (c != null) {
-            assert collectionType(elementType).isAssignableFrom(c.getClass());
-            return c;
+            assert collectionType(elementType).isInstance(c);
+            return c.isEmpty() && isMarshalling() ? null : c;
+        }
+        if (isMarshalling()) {
+            return null;
         }
         final boolean isModifiable = isModifiable();
         if (useSet(elementType)) {
             if (isModifiable) {
-                return new MutableSet<E>(elementType);
+                return new CheckedHashSet<E>(elementType, INITIAL_CAPACITY);
             } else {
                 return Collections.emptySet();
             }
         } else {
             if (isModifiable) {
-                return new MutableList<E>(elementType);
+                return new CheckedArrayList<E>(elementType, INITIAL_CAPACITY);
             } else {
                 return Collections.emptyList();
             }
@@ -351,70 +576,6 @@ public abstract class ModifiableMetadata
     }
 
     /**
-     * A checked set synchronized on the enclosing {@link ModifiableMetadata}.
-     * Used for mutable sets only.
-     */
-    private final class MutableSet<E> extends CheckedHashSet<E> {
-        private static final long serialVersionUID = 2337350768744454264L;
-
-        public MutableSet(Class<E> type) {
-            super(type, 4); // Use a small capacity because we typically have few elements.
-        }
-
-        public MutableSet(Class<E> type, int capacity) {
-            super(type, hashMapCapacity(capacity));
-        }
-
-        @Override
-        protected Object getLock() {
-            return ModifiableMetadata.this;
-        }
-
-        @Override
-        protected void checkWritePermission() throws UnsupportedOperationException {
-            ModifiableMetadata.this.checkWritePermission();
-        }
-
-        @Override
-        protected void ensureValid(final E element) throws IllegalArgumentException {
-            ensureNonNull("element", element);
-            super.ensureValid(element);
-        }
-    }
-
-    /**
-     * A checked list synchronized on the enclosing {@link ModifiableMetadata}.
-     * Used for mutable lists only.
-     */
-    private final class MutableList<E> extends CheckedArrayList<E> {
-        private static final long serialVersionUID = -5016778173550153002L;
-
-        public MutableList(Class<E> type) {
-            super(type, 4); // Use a small capacity because we typically have few elements.
-        }
-
-        public MutableList(Class<E> type, int capacity) {
-            super(type, capacity);
-        }
-
-        @Override
-        protected Object getLock() {
-            return ModifiableMetadata.this;
-        }
-
-        @Override
-        protected void checkWritePermission() throws UnsupportedOperationException {
-            ModifiableMetadata.this.checkWritePermission();
-        }
-
-        @Override
-        protected void ensureValid(final E element) throws IllegalArgumentException {
-            ensureNonNull("element", element);
-            super.ensureValid(element);
-        }
-    }
-
-    /**
      * Returns {@code true} if we should use a {@link Set} instead than a {@link List}
      * for elements of the given type.
      */
@@ -426,12 +587,12 @@ public abstract class ModifiableMetadata
         if (List.class.isAssignableFrom(type)) {
             return false;
         }
-        throw new NoSuchElementException("Unsupported data type");
+        throw new NoSuchElementException(Errors.format(Errors.Keys.UnsupportedType_1, type));
     }
 
     /**
      * Returns the type of collection to use for the given type. The current implementation can
-     * return only two values: <code>{@linkplain Set}.class</code> if the attribute should not
+     * return only two values: <code>{@linkplain Set}.class</code> if the property should not
      * accept duplicated values, or <code>{@linkplain List}.class</code> otherwise. Future SIS
      * versions may accept other types.
      *
@@ -444,11 +605,27 @@ public abstract class ModifiableMetadata
      * @param  <E> The type of elements in the collection to be created.
      * @param  elementType The type of elements in the collection to be created.
      * @return {@code List.class} or {@code Set.class} depending on whether the
-     *         attribute shall accept duplicated values or not.
+     *         property shall accept duplicated values or not.
      */
     @SuppressWarnings({"rawtypes","unchecked"})
     protected <E> Class<? extends Collection<E>> collectionType(final Class<E> elementType) {
         return (Class) (CodeList.class.isAssignableFrom(elementType) ||
                             Enum.class.isAssignableFrom(elementType) ? Set.class : List.class);
     }
+
+    /**
+     * Returns a shallow copy of this metadata.
+     * While {@linkplain Cloneable cloneable}, this class does not provides the {@code clone()}
+     * operation as part of the public API. The clone operation is required for the internal
+     * working of the {@link #unmodifiable()} method, which needs <em>shallow</em>
+     * copies of metadata entities. The default {@link Object#clone()} implementation is
+     * sufficient in most cases.
+     *
+     * @return A <em>shallow</em> copy of this metadata.
+     * @throws CloneNotSupportedException if the clone is not supported.
+     */
+    @Override
+    protected ModifiableMetadata clone() throws CloneNotSupportedException {
+        return (ModifiableMetadata) super.clone();
+    }
 }

Propchange: sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Modified: sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/UnmodifiableMetadataException.java
URL: http://svn.apache.org/viewvc/sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/UnmodifiableMetadataException.java?rev=1480602&r1=1480601&r2=1480602&view=diff
==============================================================================
--- sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/UnmodifiableMetadataException.java [iso-8859-1] (original)
+++ sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/UnmodifiableMetadataException.java [UTF-8] Thu May  9 12:24:13 2013
@@ -18,9 +18,15 @@ package org.apache.sis.metadata;
 
 
 /**
- * Thrown when a setter method is invoked on an initially
- * {@linkplain org.apache.sis.metadata.ModifiableMetadata modifiable metadata},
- * but this metadata has since be declared unmodifiable.
+ * Thrown on attempt to set a read-only value in a metadata object.
+ * This exception may happen in the following scenarios:
+ *
+ * <ul>
+ *   <li>A metadata instance was initially {@linkplain org.apache.sis.metadata.ModifiableMetadata
+ *       modifiable}, but that instance has since be declared unmodifiable.</li>
+ *   <li>A write operation has been attempted on the {@linkplain AbstractMetadata#asMap() map view},
+ *       but the metadata object has no corresponding setter methods.</li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.4)
@@ -31,7 +37,7 @@ public class UnmodifiableMetadataExcepti
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = -1885135341334523675L;
+    private static final long serialVersionUID = 286569086054839096L;
 
     /**
      * Creates a new exception with the specified detail message.

Propchange: sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/UnmodifiableMetadataException.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Modified: sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
URL: http://svn.apache.org/viewvc/sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java?rev=1480602&r1=1480601&r2=1480602&view=diff
==============================================================================
--- sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java [iso-8859-1] (original)
+++ sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java [UTF-8] Thu May  9 12:24:13 2013
@@ -18,7 +18,9 @@ package org.apache.sis.metadata.iso.cita
 
 import java.util.Date;
 import java.util.Collection;
-
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.citation.CitationDate;
@@ -26,24 +28,73 @@ import org.opengis.metadata.citation.Pre
 import org.opengis.metadata.citation.ResponsibleParty;
 import org.opengis.metadata.citation.Series;
 import org.opengis.util.InternationalString;
-
-import org.apache.sis.metadata.ModifiableMetadata;
+import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.internal.jaxb.NonMarshalledAuthority;
+import org.apache.sis.metadata.iso.ISOMetadata;
+import org.apache.sis.xml.IdentifierSpace;
+
+import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
+import static org.apache.sis.internal.metadata.MetadataUtilities.toDate;
+import static org.apache.sis.internal.metadata.MetadataUtilities.toMilliseconds;
 
 
 /**
  * Standardized resource reference.
  *
+ * {@section Unified identifiers view}
+ * The ISO 19115 model provides specific attributes for the {@linkplain #getISBN() ISBN} and
+ * {@linkplain #getISSN() ISSN} codes. However the SIS library handles those codes like any
+ * other identifiers. Consequently the ISBN and ISSN codes are included in the collection
+ * returned by {@link #getIdentifiers()}, except at XML marshalling time (for ISO 19139 compliance).
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @author  Cédric Briançon (Geomatys)
+ * @author  Cédric Briançon (Geomatys)
  * @since   0.3 (derived from geotk-2.1)
  * @version 0.3
  * @module
  */
-public class DefaultCitation extends ModifiableMetadata implements Citation {
+@XmlType(name = "CI_Citation_Type", propOrder = {
+    "title",
+    "alternateTitles",
+    "dates",
+    "edition",
+    "editionDate",
+    "identifiers",
+    "citedResponsibleParties",
+    "presentationForms",
+    "series",
+    "otherCitationDetails",
+    "collectiveTitle",
+    "ISBN",
+    "ISSN"
+})
+@XmlRootElement(name = "CI_Citation")
+public class DefaultCitation extends ISOMetadata implements Citation {
     /**
      * Serial number for inter-operability with different versions.
      */
-    private static final long serialVersionUID = 2595269795652984755L;
+    private static final long serialVersionUID = 3490090845236158848L;
+
+    /**
+     * The authority for International Standard Book Number.
+     *
+     * <p><b>Implementation note:</b> This field is read by reflection in
+     * {@link org.apache.sis.internal.jaxb.NonMarshalledAuthority#getCitation(String)}.
+     * If this field is renamed or moved, then {@code NonMarshalledAuthority} needs
+     * to be updated.</p>
+     */
+    static final IdentifierSpace<String> ISBN = new NonMarshalledAuthority<String>("ISBN", NonMarshalledAuthority.ISBN);
+
+    /**
+     * The authority for International Standard Serial Number.
+     *
+     * <p><b>Implementation note:</b> This field is read by reflection in
+     * {@link org.apache.sis.internal.jaxb.NonMarshalledAuthority#getCitation(String)}.
+     * If this field is renamed or moved, then {@code NonMarshalledAuthority} needs
+     * to be updated.</p>
+     */
+    static final IdentifierSpace<String> ISSN = new NonMarshalledAuthority<String>("ISSN", NonMarshalledAuthority.ISSN);
 
     /**
      * Name by which the cited resource is known.
@@ -74,12 +125,12 @@ public class DefaultCitation extends Mod
 
     /**
      * Name and position information for an individual or organization that is responsible
-     * for the resource. Returns an empty string if there is none.
+     * for the resource. Returns an empty collection if there is none.
      */
     private Collection<ResponsibleParty> citedResponsibleParties;
 
     /**
-     * Mode in which the resource is represented, or an empty string if none.
+     * Mode in which the resource is represented, or an empty collection if none.
      */
     private Collection<PresentationForm> presentationForms;
 
@@ -110,175 +161,307 @@ public class DefaultCitation extends Mod
     }
 
     /**
+     * Constructs a citation with the specified title.
+     *
+     * @param title The title as a {@link String} or an {@link InternationalString} object,
+     *        or {@code null} if none.
+     */
+    public DefaultCitation(final CharSequence title) {
+        this(); // Initialize the date field.
+        this.title = Types.toInternationalString(title);
+    }
+
+    /**
+     * Constructs a citation with the specified responsible party.
+     * This convenience constructor initializes the citation title
+     * to the first non-null of the following properties:
+     * {@linkplain DefaultResponsibleParty#getOrganisationName() organisation name},
+     * {@linkplain DefaultResponsibleParty#getPositionName() position name} or
+     * {@linkplain DefaultResponsibleParty#getIndividualName() individual name}.
+     *
+     * @param party The name and position information for an individual or organization that is
+     *              responsible for the resource, or {@code null} if none.
+     */
+    public DefaultCitation(final ResponsibleParty party) {
+        this(); // Initialize the date field.
+        if (party != null) {
+            citedResponsibleParties = singleton(party, ResponsibleParty.class);
+            title = party.getOrganisationName();
+            if (title == null) {
+                title = party.getPositionName();
+                if (title == null) {
+                    String name = party.getIndividualName();
+                    if (name != null) {
+                        title = new SimpleInternationalString(name);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Constructs a new instance initialized with the values from the specified metadata object.
+     * This is a <cite>shallow</cite> copy constructor, since the other metadata contained in the
+     * given object are not recursively copied.
+     *
+     * @param object The metadata to copy values from.
+     *
+     * @see #castOrCopy(Citation)
+     */
+    public DefaultCitation(final Citation object) {
+        super(object);
+        title                   = object.getTitle();
+        alternateTitles         = copyCollection(object.getAlternateTitles(), InternationalString.class);
+        dates                   = copyCollection(object.getDates(), CitationDate.class);
+        edition                 = object.getEdition();
+        editionDate             = toMilliseconds(object.getEditionDate());
+        identifiers             = copyCollection(object.getIdentifiers(), Identifier.class);
+        citedResponsibleParties = copyCollection(object.getCitedResponsibleParties(), ResponsibleParty.class);
+        presentationForms       = copyCollection(object.getPresentationForms(), PresentationForm.class);
+        series                  = object.getSeries();
+        otherCitationDetails    = object.getOtherCitationDetails();
+        collectiveTitle         = object.getCollectiveTitle();
+// TODO ISBN                    = object.getISBN();
+// TODO ISSN                    = object.getISSN();
+    }
+
+    /**
+     * Returns a SIS metadata implementation with the values of the given arbitrary implementation.
+     * This method performs the first applicable actions in the following choices:
+     *
+     * <ul>
+     *   <li>If the given object is {@code null}, then this method returns {@code null}.</li>
+     *   <li>Otherwise if the given object is already an instance of
+     *       {@code DefaultCitation}, then it is returned unchanged.</li>
+     *   <li>Otherwise a new {@code DefaultCitation} instance is created using the
+     *       {@linkplain #DefaultCitation(Citation) copy constructor}
+     *       and returned. Note that this is a <cite>shallow</cite> copy operation, since the other
+     *       metadata contained in the given object are not recursively copied.</li>
+     * </ul>
+     *
+     * @param  object The object to get as a SIS implementation, or {@code null} if none.
+     * @return A SIS implementation containing the values of the given object (may be the
+     *         given object itself), or {@code null} if the argument was null.
+     */
+    public static DefaultCitation castOrCopy(final Citation object) {
+        if (object == null || object instanceof DefaultCitation) {
+            return (DefaultCitation) object;
+        }
+        return new DefaultCitation(object);
+    }
+
+    /**
      * Returns the name by which the cited resource is known.
      */
     @Override
-    public synchronized InternationalString getTitle() {
+    @XmlElement(name = "title", required = true)
+    public InternationalString getTitle() {
         return title;
     }
 
     /**
      * Sets the name by which the cited resource is known.
      *
-     * @param newValue The new title.
+     * @param newValue The new title, or {@code null} if none.
      */
-    public synchronized void setTitle(final InternationalString newValue) {
+    public void setTitle(final InternationalString newValue) {
         checkWritePermission();
         title = newValue;
     }
 
     /**
      * Returns the short name or other language name by which the cited information is known.
-     * Example: "DCW" as an alternative title for "Digital Chart of the World".
+     * Example: "DCW" as an alternative title for "<cite>Digital Chart of the World</cite>".
      */
     @Override
-    public synchronized Collection<InternationalString> getAlternateTitles() {
+    @XmlElement(name = "alternateTitle")
+    public Collection<InternationalString> getAlternateTitles() {
         return alternateTitles = nonNullCollection(alternateTitles, InternationalString.class);
     }
 
     /**
      * Sets the short name or other language name by which the cited information is known.
      *
-     * @param newValues The new alternate titles.
+     * @param newValues The new alternate titles, or {@code null} if none.
      */
-    public synchronized void setAlternateTitles(final Collection<? extends InternationalString> newValues) {
-        alternateTitles = copyCollection(newValues, alternateTitles, InternationalString.class);
+    public void setAlternateTitles(final Collection<? extends InternationalString> newValues) {
+        alternateTitles = writeCollection(newValues, alternateTitles, InternationalString.class);
     }
 
     /**
      * Returns the reference date for the cited resource.
      */
     @Override
-    public synchronized Collection<CitationDate> getDates() {
+    @XmlElement(name = "date", required = true)
+    public Collection<CitationDate> getDates() {
         return dates = nonNullCollection(dates, CitationDate.class);
     }
 
     /**
      * Sets the reference date for the cited resource.
      *
-     * @param newValues The new dates.
+     * @param newValues The new dates, or {@code null} if none.
      */
-    public synchronized void setDates(final Collection<? extends CitationDate> newValues) {
-        dates = copyCollection(newValues, dates, CitationDate.class);
+    public void setDates(final Collection<? extends CitationDate> newValues) {
+        dates = writeCollection(newValues, dates, CitationDate.class);
     }
 
     /**
      * Returns the version of the cited resource.
      */
     @Override
-    public synchronized InternationalString getEdition() {
+    @XmlElement(name = "edition")
+    public InternationalString getEdition() {
         return edition;
     }
 
     /**
      * Sets the version of the cited resource.
      *
-     * @param newValue The new edition.
+     * @param newValue The new edition, or {@code null} if none.
      */
-    public synchronized void setEdition(final InternationalString newValue) {
+    public void setEdition(final InternationalString newValue) {
         checkWritePermission();
         edition = newValue;
     }
 
     /**
-     * Returns the date of the edition, or {@code null} if none.
+     * Returns the date of the edition.
      */
     @Override
-    public synchronized Date getEditionDate() {
-        return (editionDate != Long.MIN_VALUE) ? new Date(editionDate) : null;
+    @XmlElement(name = "editionDate")
+    public Date getEditionDate() {
+        return toDate(editionDate);
     }
 
     /**
-     * Sets the date of the edition, or {@code null} if none.
+     * Sets the date of the edition.
      *
-     * @param newValue The new edition date.
+     * @param newValue The new edition date, or {@code null} if none.
      */
-    public synchronized void setEditionDate(final Date newValue) {
+    public void setEditionDate(final Date newValue) {
         checkWritePermission();
-        editionDate = (newValue != null) ? newValue.getTime() : Long.MIN_VALUE;
+        editionDate = toMilliseconds(newValue);
     }
 
     /**
-     * Returns the unique identifier for the resource. Example: Universal Product Code (UPC),
-     * National Stock Number (NSN).
+     * Returns the unique identifier for the resource.
+     * Example: Universal Product Code (UPC), National Stock Number (NSN).
+     *
+     * {@section Unified identifiers view}
+     * In this SIS implementation, the collection returned by this method includes the XML identifiers
+     * ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>),
+     * as well as the {@linkplain #getISBN() ISBN} and {@linkplain #getISSN() ISSN} codes, thus
+     * providing a unified view of every kind of identifiers associated to this citation.
+     *
+     * {@note The <code>&lt:gmd:identifier&gt;</code> element marshalled to XML will exclude
+     *        all the above cited identifiers, for ISO 19139 compliance. Those identifiers
+     *        will appear in other XML elements or attributes.}
+     *
+     * @see #getISBN()
+     * @see #getISSN()
+     * @see #getIdentifierMap()
      */
     @Override
+    @XmlElement(name = "identifier")
     public Collection<Identifier> getIdentifiers() {
-        return null; // Not yet implemented on intend.
+        return NonMarshalledAuthority.excludeOnMarshalling(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>
+     *
+     * @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);
+        identifiers = writeCollection(newValues, identifiers, Identifier.class);
+        NonMarshalledAuthority.replace(identifiers, oldIds);
     }
 
     /**
      * Returns the name and position information for an individual or organization that is
-     * responsible for the resource. Returns an empty string if there is none.
+     * responsible for the resource.
      */
     @Override
-    public synchronized Collection<ResponsibleParty> getCitedResponsibleParties() {
+    @XmlElement(name = "citedResponsibleParty")
+    public Collection<ResponsibleParty> getCitedResponsibleParties() {
         return citedResponsibleParties = nonNullCollection(citedResponsibleParties, ResponsibleParty.class);
     }
 
     /**
      * Sets the name and position information for an individual or organization that is responsible
-     * for the resource. Returns an empty string if there is none.
+     * for the resource.
      *
-     * @param newValues The new cited responsible parties.
+     * @param newValues The new cited responsible parties, or {@code null} if none.
      */
-    public synchronized void setCitedResponsibleParties(final Collection<? extends ResponsibleParty> newValues) {
-        citedResponsibleParties = copyCollection(newValues, citedResponsibleParties, ResponsibleParty.class);
+    public void setCitedResponsibleParties(final Collection<? extends ResponsibleParty> newValues) {
+        citedResponsibleParties = writeCollection(newValues, citedResponsibleParties, ResponsibleParty.class);
     }
 
     /**
-     * Returns the mode in which the resource is represented, or an empty string if none.
+     * Returns the mode in which the resource is represented.
      */
     @Override
-    public synchronized Collection<PresentationForm> getPresentationForms() {
+    @XmlElement(name = "presentationForm")
+    public Collection<PresentationForm> getPresentationForms() {
         return presentationForms = nonNullCollection(presentationForms, PresentationForm.class);
     }
 
     /**
-     * Sets the mode in which the resource is represented, or an empty string if none.
+     * Sets the mode in which the resource is represented.
      *
-     * @param newValues The new presentation form.
+     * @param newValues The new presentation form, or {@code null} if none.
      */
-    public synchronized void setPresentationForms(final Collection<? extends PresentationForm> newValues) {
-        presentationForms = copyCollection(newValues, presentationForms, PresentationForm.class);
+    public void setPresentationForms(final Collection<? extends PresentationForm> newValues) {
+        presentationForms = writeCollection(newValues, presentationForms, PresentationForm.class);
     }
 
     /**
-     * Returns the information about the series, or aggregate dataset, of which the dataset is
-     * a part. Returns {@code null} if none.
+     * Returns the information about the series, or aggregate dataset, of which the dataset is a part.
      */
     @Override
-    public synchronized Series getSeries() {
+    @XmlElement(name = "series")
+    public Series getSeries() {
         return series;
     }
 
     /**
-     * Sets the information about the series, or aggregate dataset, of which the dataset is
-     * a part. Set to {@code null} if none.
+     * Sets the information about the series, or aggregate dataset, of which the dataset is a part.
      *
      * @param newValue The new series.
      */
-    public synchronized void setSeries(final Series newValue) {
+    public void setSeries(final Series newValue) {
         checkWritePermission();
         series = newValue;
     }
 
     /**
      * Returns other information required to complete the citation that is not recorded elsewhere.
-     * Returns {@code null} if none.
      */
     @Override
-    public synchronized InternationalString getOtherCitationDetails() {
+    @XmlElement(name = "otherCitationDetails")
+    public InternationalString getOtherCitationDetails() {
         return otherCitationDetails;
     }
 
     /**
      * Sets other information required to complete the citation that is not recorded elsewhere.
-     * Sets to {@code null} if none.
      *
-     * @param newValue Other citations details.
+     * @param newValue Other citations details, or {@code null} if none.
      */
-    public synchronized void setOtherCitationDetails(final InternationalString newValue) {
+    public void setOtherCitationDetails(final InternationalString newValue) {
         checkWritePermission();
         otherCitationDetails = newValue;
     }
@@ -286,38 +469,96 @@ public class DefaultCitation extends Mod
     /**
      * Returns the common title with holdings note. Note: title identifies elements of a series
      * collectively, combined with information about what volumes are available at the
-     * source cited. Returns {@code null} if there is no title.
+     * source cited.
      */
     @Override
-    public synchronized InternationalString getCollectiveTitle() {
+    @XmlElement(name = "collectiveTitle")
+    public InternationalString getCollectiveTitle() {
         return collectiveTitle;
     }
 
     /**
-     * Sets the common title with holdings note. Note: title identifies elements of a series
-     * collectively, combined with information about what volumes are available at the
-     * source cited. Set to {@code null} if there is no title.
+     * Sets the common title with holdings note. This title identifies elements of a series
+     * collectively, combined with information about what volumes are available at the source cited.
      *
-     * @param newValue The new collective title.
+     * @param newValue The new collective title, or {@code null} if none.
      */
-    public synchronized void setCollectiveTitle(final InternationalString newValue) {
+    public void setCollectiveTitle(final InternationalString newValue) {
         checkWritePermission();
         collectiveTitle = newValue;
     }
 
     /**
-     * Returns the International Standard Book Number, or {@code null} if none.
+     * Returns the International Standard Book Number.
+     * In this SIS implementation, invoking this method is equivalent to:
+     *
+     * {@preformat java
+     *   return getIdentifierMap().getSpecialized(Citations.ISBN);
+     * }
+     *
+     * @see #getIdentifiers()
+     * @see Citations#ISBN
      */
     @Override
-    public synchronized String getISBN() {
-        return null; // Not yet implemented on intend.
+    @XmlElement(name = "ISBN")
+    public String getISBN() {
+        return isNullOrEmpty(identifiers) ? null : getIdentifierMap().get(ISBN);
     }
 
     /**
-     * Returns the International Standard Serial Number, or {@code null} if none.
+     * Sets the International Standard Book Number.
+     * In this SIS implementation, invoking this method is equivalent to:
+     *
+     * {@preformat java
+     *   getIdentifierMap().putSpecialized(Citations.ISBN, newValue);
+     * }
+     *
+     * @param newValue The new ISBN, or {@code null} if none.
+     *
+     * @see #setIdentifiers(Collection)
+     * @see Citations#ISBN
+     */
+    public void setISBN(final String newValue) {
+        checkWritePermission();
+        if (newValue != null || !isNullOrEmpty(identifiers)) {
+            getIdentifierMap().putSpecialized(ISBN, newValue);
+        }
+    }
+
+    /**
+     * Returns the International Standard Serial Number.
+     * In this SIS implementation, invoking this method is equivalent to:
+     *
+     * {@preformat java
+     *   return getIdentifierMap().getSpecialized(Citations.ISSN);
+     * }
+     *
+     * @see #getIdentifiers()
+     * @see Citations#ISSN
      */
     @Override
-    public synchronized String getISSN() {
-        return null; // Not yet implemented on intend.
+    @XmlElement(name = "ISSN")
+    public String getISSN() {
+        return isNullOrEmpty(identifiers) ? null : getIdentifierMap().get(ISSN);
+    }
+
+    /**
+     * Sets the International Standard Serial Number.
+     * In this SIS implementation, invoking this method is equivalent to:
+     *
+     * {@preformat java
+     *   getIdentifierMap().putSpecialized(Citations.ISSN, newValue);
+     * }
+     *
+     * @param newValue The new ISSN.
+     *
+     * @see #setIdentifiers(Collection)
+     * @see Citations#ISSN
+     */
+    public void setISSN(final String newValue) {
+        checkWritePermission();
+        if (newValue != null || !isNullOrEmpty(identifiers)) {
+            getIdentifierMap().putSpecialized(ISSN, newValue);
+        }
     }
 }

Propchange: sis/branches/Android/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Modified: sis/branches/Android/sis-metadata/src/site/site.xml
URL: http://svn.apache.org/viewvc/sis/branches/Android/sis-metadata/src/site/site.xml?rev=1480602&r1=1480601&r2=1480602&view=diff
==============================================================================
--- sis/branches/Android/sis-metadata/src/site/site.xml (original)
+++ sis/branches/Android/sis-metadata/src/site/site.xml Thu May  9 12:24:13 2013
@@ -26,8 +26,8 @@
   <body>
     <menu name="Quick links">
       <item name="Module Javadoc"  href="apidocs/index.html"/>
+      <item name="FAQ"             href="faq.html"/>
       <item name="Apache SIS home" href="../index.html"/>
     </menu>
-
   </body>
 </project>

Propchange: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/core/LatLon.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Propchange: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/core/LatLonPointRadius.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Propchange: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/core/LatLonRect.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Propchange: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/distance/DistanceUtils.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Modified: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
URL: http://svn.apache.org/viewvc/sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java?rev=1480602&r1=1480601&r2=1480602&view=diff
==============================================================================
--- sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java [iso-8859-1] (original)
+++ sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java [UTF-8] Thu May  9 12:24:13 2013
@@ -28,17 +28,17 @@ import org.opengis.referencing.crs.Coord
 import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.RangeMeaning;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
 
 import static java.lang.Double.doubleToLongBits;
-import static org.apache.sis.util.Arrays.resize;
 import static org.apache.sis.util.StringBuilders.trimFractionalPart;
 import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
 
 // Related to JDK7
-import org.apache.sis.internal.util.Objects;
+import org.apache.sis.internal.jdk7.Objects;
 
 
 /**
@@ -137,7 +137,7 @@ public abstract class AbstractDirectPosi
      *       are replaced by the axis minimum.</li>
      *
      *   <li>If {@link RangeMeaning#WRAPAROUND} (typically <em>longitudes</em> ordinates), then
-     *       a multiple of the axis range (e.g. 360° for longitudes) is added or subtracted.</li>
+     *       a multiple of the axis range (e.g. 360° for longitudes) is added or subtracted.</li>
      * </ul>
      *
      * @return {@code true} if this position has been modified as a result of this method call,
@@ -195,11 +195,11 @@ public abstract class AbstractDirectPosi
 
     /**
      * Formats this position in the <cite>Well Known Text</cite> (WKT) format.
-     * The returned string is like below, where {@code x₀}, {@code x₁}, {@code x₂}, <i>etc.</i>
+     * The returned string is like below, where {@code x₀}, {@code x₁}, {@code x₂}, <i>etc.</i>
      * are the ordinate values at index 0, 1, 2, <i>etc.</i>:
      *
      * {@preformat wkt
-     *   POINT(x₀ x₁ x₂ …)
+     *   POINT(x₀ x₁ x₂ …)
      * }
      *
      * The string returned by this method can be {@linkplain GeneralDirectPosition#GeneralDirectPosition(CharSequence) parsed}
@@ -213,7 +213,7 @@ public abstract class AbstractDirectPosi
     }
 
     /**
-     * Implementation of the public {@link #toString()} and {@link Envelope2D#toString()} methods
+     * Implementation of the public {@link #toString()} and {@link DirectPosition2D#toString()} methods
      * for formatting a {@code POINT} element from a direct position in <cite>Well Known Text</cite>
      * (WKT) format.
      *
@@ -341,7 +341,7 @@ parse:  while (i < length) {
                 c = Character.codePointAt(wkt, i);
             }
         }
-        return resize(ordinates, dimension);
+        return ArraysExt.resize(ordinates, dimension);
     }
 
     /**

Propchange: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8

Modified: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
URL: http://svn.apache.org/viewvc/sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1480602&r1=1480601&r2=1480602&view=diff
==============================================================================
--- sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [iso-8859-1] (original)
+++ sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] Thu May  9 12:24:13 2013
@@ -45,7 +45,7 @@ import static org.apache.sis.math.MathFu
 import static org.apache.sis.math.MathFunctions.isPositive;
 
 // Related to JDK7
-import org.apache.sis.internal.util.Objects;
+import org.apache.sis.internal.jdk7.Objects;
 
 
 /**
@@ -87,7 +87,7 @@ import org.apache.sis.internal.util.Obje
  * </td></tr></table>
  *
  * {@section Choosing the range of longitude values}
- * Geographic CRS typically have longitude values in the [-180 … +180]° range, but the [0 … 360]°
+ * Geographic CRS typically have longitude values in the [-180 … +180]° range, but the [0 … 360]°
  * range is also occasionally used. Users of this class need to ensure that this envelope CRS is
  * associated to axes having the desired {@linkplain CoordinateSystemAxis#getMinimumValue() minimum}
  * and {@linkplain CoordinateSystemAxis#getMaximumValue() maximum value}.
@@ -98,8 +98,8 @@ import org.apache.sis.internal.util.Obje
  * different meanings:
  *
  * <ul>
- *   <li>The [-0…0]° range is an empty envelope.</li>
- *   <li>The [0…-0]° range makes a full turn around the globe, like the [-180…180]°
+ *   <li>The [-0…0]° range is an empty envelope.</li>
+ *   <li>The [0…-0]° range makes a full turn around the globe, like the [-180…180]°
  *       range except that the former range spans across the anti-meridian.</li>
  * </ul>
  *
@@ -380,7 +380,7 @@ public abstract class AbstractEnvelope i
      * {@linkplain CoordinateSystemAxis#getRangeMeaning() range meaning} for the requested
      * dimension is {@linkplain RangeMeaning#WRAPAROUND wraparound}, then the median calculated
      * above is actually in the middle of the space <em>outside</em> the envelope. In such cases,
-     * this method shifts the <var>median</var> value by half of the periodicity (180° in the
+     * this method shifts the <var>median</var> value by half of the periodicity (180° in the
      * longitude case) in order to switch from <cite>outer</cite> space to <cite>inner</cite>
      * space. If the axis range meaning is not {@code WRAPAROUND}, then this method returns
      * {@link Double#NaN NaN}.
@@ -430,7 +430,7 @@ public abstract class AbstractEnvelope i
      * If <var>upper</var> &lt; <var>lower</var> and the
      * {@linkplain CoordinateSystemAxis#getRangeMeaning() range meaning} for the requested
      * dimension is {@linkplain RangeMeaning#WRAPAROUND wraparound}, then the span calculated
-     * above is negative. In such cases, this method adds the periodicity (typically 360° of
+     * above is negative. In such cases, this method adds the periodicity (typically 360° of
      * longitude) to the span. If the result is a positive number, it is returned. Otherwise
      * this method returns {@link Double#NaN NaN}.
      *
@@ -496,8 +496,8 @@ public abstract class AbstractEnvelope i
     /**
      * Determines whether or not this envelope is empty. An envelope is non-empty only if it has
      * at least one {@linkplain #getDimension() dimension}, and the {@linkplain #getSpan(int) span}
-     * is greater than 0 along all dimensions. Note that a non-empty envelope is always
-     * non-{@linkplain #isNull() null}, but the converse is not always true.
+     * is greater than 0 along all dimensions. Note that {@link #isAllNaN()} always returns
+     * {@code false} for a non-empty envelope, but the converse is not always true.
      *
      * @return {@code true} if this envelope is empty.
      *
@@ -514,28 +514,32 @@ public abstract class AbstractEnvelope i
                 return true;
             }
         }
-        assert !isNull() : this;
+        assert !isAllNaN() : this;
         return false;
     }
 
     /**
      * Returns {@code false} if at least one ordinate value is not {@linkplain Double#NaN NaN}.
-     * This {@code isNull()} check is a little bit different than the {@link #isEmpty()} check
+     * This {@code isAllNaN()} check is a little bit different than the {@link #isEmpty()} check
      * since it returns {@code false} for a partially initialized envelope, while {@code isEmpty()}
      * returns {@code false} only after all dimensions have been initialized. More specifically,
      * the following rules apply:
      *
      * <ul>
-     *   <li>If {@code isNull() == true}, then {@code isEmpty() == true}</li>
-     *   <li>If {@code isEmpty() == false}, then {@code isNull() == false}</li>
+     *   <li>If {@code isAllNaN() == true}, then {@code isEmpty() == true}</li>
+     *   <li>If {@code isEmpty() == false}, then {@code isAllNaN() == false}</li>
      *   <li>The converse of the above-cited rules are not always true.</li>
      * </ul>
      *
+     * Note that a all-NaN envelope can still have a non-null
+     * {@linkplain #getCoordinateReferenceSystem() coordinate reference system}.
+     *
      * @return {@code true} if this envelope has NaN values.
      *
-     * @see GeneralEnvelope#setToNull()
+     * @see GeneralEnvelope#setToNaN()
+     * @see org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#isEmpty()
      */
-    public boolean isNull() {
+    public boolean isAllNaN() {
         final int dimension = getDimension();
         for (int i=0; i<dimension; i++) {
             if (!Double.isNaN(getLower(i)) || !Double.isNaN(getUpper(i))) {
@@ -644,10 +648,10 @@ public abstract class AbstractEnvelope i
             }
             if (lowerCondition & upperCondition) {
                 /*         upperCnd          upperCnd
-                 *  ┌─────────────┐          ────┐  ┌────                      ┌─┐
-                 *  │  ┌───────┐  │    or    ──┐ │  │ ┌──    excluding    ───┐ │ │ ┌───
-                 *  │  └───────┘  │          ──┘ │  │ └──                 ───┘ │ │ └───
-                 *  └─────────────┘          ────┘  └────                      └─┘
+                 *  ┌─────────────┐          ────┐  ┌────                      ┌─┐
+                 *  │  ┌───────┐  │    or    ──┐ │  │ ┌──    excluding    ───┐ │ │ ┌───
+                 *  │  └───────┘  │          ──┘ │  │ └──                 ───┘ │ │ └───
+                 *  └─────────────┘          ────┘  └────                      └─┘
                  *  lowerCnd                        lowerCnd
                  */
                 // (upper1-lower1) is negative if the small rectangle in above pictures spans the anti-meridian.
@@ -666,16 +670,16 @@ public abstract class AbstractEnvelope i
                 }
             } else if (lowerCondition != upperCondition) {
                 /*     upperCnd                     !upperCnd
-                 *  ──────────┐  ┌─────              ─────┐  ┌─────────
-                 *    ┌────┐  │  │           or           │  │  ┌────┐
-                 *    └────┘  │  │                        │  │  └────┘
-                 *  ──────────┘  └─────              ─────┘  └─────────
+                 *  ──────────┐  ┌─────              ─────┐  ┌─────────
+                 *    ┌────┐  │  │           or           │  │  ┌────┐
+                 *    └────┘  │  │                        │  │  └────┘
+                 *  ──────────┘  └─────              ─────┘  └─────────
                  *               !lowerCnd                   lowerCnd */
                 if (isNegative(upper0 - lower0)) {
                     if (isPositive(upper1 - lower1)) {
                         continue;
                     }
-                    // Special case for the [0…-0] range, if inclusive.
+                    // Special case for the [0…-0] range, if inclusive.
                     if (edgesInclusive && Double.doubleToRawLongBits(lower0) == 0L &&
                             Double.doubleToRawLongBits(upper0) == SIGN_BIT_MASK)
                     {
@@ -733,10 +737,10 @@ public abstract class AbstractEnvelope i
                 upperCondition = (upper1 > lower0);
             }
             if (upperCondition & lowerCondition) {
-                /*     ┌──────────┐
-                 *     │  ┌───────┼──┐
-                 *     │  └───────┼──┘
-                 *     └──────────┘ (this is the most standard case) */
+                /*     ┌──────────┐
+                 *     │  ┌───────┼──┐
+                 *     │  └───────┼──┘
+                 *     └──────────┘ (this is the most standard case) */
                 continue;
             }
             final boolean sp0 = isNegative(upper0 - lower0);
@@ -747,10 +751,10 @@ public abstract class AbstractEnvelope i
                  * intersection (since both envelopes extend to infinities). Otherwise we have one
                  * of the cases illustrated below. Note that the rectangle could also intersect on
                  * only once side.
-                 *         ┌──────────┐                   ─────┐      ┌─────
-                 *     ────┼───┐  ┌───┼────      or          ┌─┼──────┼─┐
-                 *     ────┼───┘  └───┼────                  └─┼──────┼─┘
-                 *         └──────────┘                   ─────┘      └───── */
+                 *         ┌──────────┐                   ─────┐      ┌─────
+                 *     ────┼───┐  ┌───┼────      or          ┌─┼──────┼─┐
+                 *     ────┼───┘  └───┼────                  └─┼──────┼─┘
+                 *         └──────────┘                   ─────┘      └───── */
                 if ((sp0 & sp1) | (upperCondition | lowerCondition)) {
                     continue;
                 }
@@ -793,7 +797,7 @@ public abstract class AbstractEnvelope i
      *
      * <ul>
      *   <li>If {@code epsIsRelative} is set to {@code true}, the actual tolerance value for a
-     *       given dimension <var>i</var> is {@code eps} × {@code span} where {@code span}
+     *       given dimension <var>i</var> is {@code eps} × {@code span} where {@code span}
      *       is the maximum of {@linkplain #getSpan(int) this envelope span} and the specified
      *       envelope span along dimension <var>i</var>.</li>
      *   <li>If {@code epsIsRelative} is set to {@code false}, the actual tolerance value for a
@@ -803,7 +807,7 @@ public abstract class AbstractEnvelope i
      * {@note Relative tolerance value (as opposed to absolute tolerance value) help to workaround
      * the fact that tolerance value are CRS dependent. For example the tolerance value need to be
      * smaller for geographic CRS than for UTM projections, because the former typically has a
-     * [-180…180]° range while the later can have a range of thousands of meters.}
+     * [-180…180]° range while the later can have a range of thousands of meters.}
      *
      * {@section Coordinate Reference System}
      * To be considered equal, the two envelopes must have the same {@linkplain #getDimension() dimension}
@@ -831,15 +835,15 @@ public abstract class AbstractEnvelope i
         final DirectPosition lowerCorner = other.getLowerCorner();
         final DirectPosition upperCorner = other.getUpperCorner();
         for (int i=0; i<dimension; i++) {
-            double ε = eps;
+            double ε = eps;
             if (epsIsRelative) {
                 final double span = Math.max(getSpan(i), other.getSpan(i));
                 if (span > 0 && span < Double.POSITIVE_INFINITY) {
-                    ε *= span;
+                    ε *= span;
                 }
             }
-            if (!epsilonEqual(getLower(i), lowerCorner.getOrdinate(i), ε) ||
-                !epsilonEqual(getUpper(i), upperCorner.getOrdinate(i), ε))
+            if (!epsilonEqual(getLower(i), lowerCorner.getOrdinate(i), ε) ||
+                !epsilonEqual(getUpper(i), upperCorner.getOrdinate(i), ε))
             {
                 return false;
             }
@@ -909,20 +913,26 @@ public abstract class AbstractEnvelope i
     }
 
     /**
-     * Formats this envelope in the <cite>Well Known Text</cite> (WKT) format.
+     * Formats this envelope as a "{@code BOX}" element.
      * The output is of the form "{@code BOX}<var>n</var>{@code D(}{@linkplain #getLowerCorner()
      * lower corner}{@code ,}{@linkplain #getUpperCorner() upper corner}{@code )}"
      * where <var>n</var> is the {@linkplain #getDimension() number of dimensions}.
-     * Example:
+     * The number of dimension is written only if different than 2.
+     *
+     * <p>Example:</p>
      *
      * {@preformat wkt
+     *   BOX(-90 -180, 90 180)
      *   BOX3D(-90 -180 0, 90 180 1)
      * }
      *
+     * {@note The <code>BOX</code> element is not part of the standard <cite>Well Known Text</cite>
+     *        (WKT) format. However it is understood by many softwares, for example GDAL and PostGIS.}
+     *
      * The string returned by this method can be {@linkplain GeneralEnvelope#GeneralEnvelope(CharSequence) parsed}
      * by the {@code GeneralEnvelope} constructor.
      *
-     * @return This envelope as a {@code BOX2D} or {@code BOX3D} (most typical dimensions) in WKT format.
+     * @return This envelope as a {@code BOX} or {@code BOX3D} (most typical dimensions) element.
      */
     @Override
     public String toString() {
@@ -930,20 +940,23 @@ public abstract class AbstractEnvelope i
     }
 
     /**
-     * Implementation of the public {@link #toString()} and {@link Envelopes#toWKT(Envelope)} methods
-     * for formatting a {@code BOX} element from an envelope in <cite>Well Known Text</cite> (WKT) format.
+     * Implementation of the public {@link #toString()} and {@link Envelopes#toString(Envelope)}
+     * methods for formatting a {@code BOX} element from an envelope.
      *
      * @param  envelope The envelope to format.
      * @param  isSimplePrecision {@code true} if every lower and upper corner values can be casted to {@code float}.
-     * @return The envelope as a {@code BOX2D} or {@code BOX3D} (most typical dimensions) in WKT format.
+     * @return This envelope as a {@code BOX} or {@code BOX3D} (most typical dimensions) element.
      *
-     * @see GeneralEnvelope#GeneralEnvelope(String)
+     * @see GeneralEnvelope#GeneralEnvelope(CharSequence)
      * @see org.apache.sis.measure.CoordinateFormat
      * @see org.apache.sis.io.wkt
      */
     static String toString(final Envelope envelope, final boolean isSimplePrecision) {
         final int dimension = envelope.getDimension();
-        final StringBuilder buffer = new StringBuilder(64).append("BOX").append(dimension).append('D');
+        final StringBuilder buffer = new StringBuilder(64).append("BOX");
+        if (dimension != 2) {
+            buffer.append(dimension).append('D');
+        }
         if (dimension == 0) {
             buffer.append("()");
         } else {
@@ -980,7 +993,7 @@ public abstract class AbstractEnvelope i
      * @module
      */
     private abstract class Point extends AbstractDirectPosition implements Serializable {
-        private static final long serialVersionUID = 9051824576982927750L;
+        private static final long serialVersionUID = -4868610696294317932L;
 
         /** The coordinate reference system in which the coordinate is given. */
         @Override public final CoordinateReferenceSystem getCoordinateReferenceSystem() {
@@ -997,7 +1010,7 @@ public abstract class AbstractEnvelope i
      * The corner returned by {@link AbstractEnvelope#getLowerCorner()}.
      */
     private final class LowerCorner extends Point {
-        private static final long serialVersionUID = 1342844299471364436L;
+        private static final long serialVersionUID = 1310741484466506178L;
 
         @Override public double getOrdinate(final int dimension) throws IndexOutOfBoundsException {
             return getLower(dimension);
@@ -1013,7 +1026,7 @@ public abstract class AbstractEnvelope i
      * The corner returned by {@link AbstractEnvelope#getUpperCorner()}.
      */
     private final class UpperCorner extends Point {
-        private static final long serialVersionUID = 8999737674570427517L;
+        private static final long serialVersionUID = -6458663549974061472L;
 
         @Override public double getOrdinate(final int dimension) throws IndexOutOfBoundsException {
             return getUpper(dimension);
@@ -1029,7 +1042,7 @@ public abstract class AbstractEnvelope i
      * The point returned by {@link AbstractEnvelope#getMedian()}.
      */
     private final class Median extends Point {
-        private static final long serialVersionUID = 4204675972453668922L;
+        private static final long serialVersionUID = -5826011018957321729L;
 
         @Override public double getOrdinate(final int dimension) throws IndexOutOfBoundsException {
             return getMedian(dimension);

Propchange: sis/branches/Android/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
------------------------------------------------------------------------------
--- svn:mime-type (original)
+++ svn:mime-type Thu May  9 12:24:13 2013
@@ -1 +1 @@
-text/plain
+text/plain;charset=UTF-8



Mime
View raw message