Author: desruisseaux
Date: Thu Jan  9 18:55:40 2014
New Revision: 1556920

URL: http://svn.apache.org/r1556920
Log:
Removed the AbstractIdentifiedObject.setNames(Collection<ReferenceIdentifier>) 
because whether that method
was invoked or not was JAXB-implementation dependent. Instead design 
AbstractIdentifiedObject in a way that
should work the same on all supported JDK versions.

Modified:
    
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
    
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java

Modified: 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1556920&r1=1556919&r2=1556920&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java
 [UTF-8] Thu Jan  9 18:55:40 2014
@@ -18,7 +18,6 @@ package org.apache.sis.referencing;
 
 import java.util.Map;
 import java.util.Set;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.AbstractCollection;
@@ -42,6 +41,7 @@ import org.opengis.referencing.Reference
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.jaxb.referencing.Code;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.io.wkt.FormattableObject;
 import org.apache.sis.xml.Namespaces;
 import org.apache.sis.util.Deprecable;
@@ -490,7 +490,7 @@ public class AbstractIdentifiedObject ex
          * Invoked by JAXB at unmarshalling time for each identifier. The 
first identifier will be taken
          * as the name and all other identifiers (if any) as aliases.
          *
-         * <p>Some JAXB implementations never invoke {@link 
AbstractIdentifiedObject#setNames(Collection)}.
+         * <p>Some (but not all) JAXB implementations never invoke setter 
method for collections.
          * Instead they invoke {@link AbstractIdentifiedObject#getNames()} and 
add directly the identifiers
          * in the returned collection. Consequently this method must writes 
directly in the enclosing object.
          * See <a href="https://java.net/jira/browse/JAXB-488";>JAXB-488</a> 
for more information.</p>
@@ -499,57 +499,59 @@ public class AbstractIdentifiedObject ex
         public boolean add(final ReferenceIdentifier id) {
             if (name == null) {
                 name = id;
-                return true;
-            }
-            if (alias == null) {
-                alias = new ArrayList<>(4);
+            } else {
+                /*
+                 * Our Code and RS_Identifier implementations should always 
create NamedIdentifier instance,
+                 * so the 'instanceof' check should not be necessary. But we 
do a paranoiac check anyway.
+                 */
+                final GenericName n = id instanceof GenericName ? 
(GenericName) id : new NamedIdentifier(id);
+                if (alias == null) {
+                    alias = Collections.singleton(n);
+                } else {
+                    /*
+                     * This implementation is inefficient since each addition 
copies the array, but we rarely
+                     * have more than two aliases.  This implementation is 
okay for a small number of aliases
+                     * and ensures that the enclosing AbstractIdentifiedObject 
is unmodifiable except by this
+                     * add(…) method.
+                     *
+                     * Note about alternative approaches
+                     * ---------------------------------
+                     * An alternative approach could be to use an ArrayList 
and replace it by an unmodifiable
+                     * list only after unmarshalling (using an 
afterUnmarshal(Unmarshaller, Object) method),
+                     * but we want to avoid Unmarshaller dependency (for 
reducing classes loading for users
+                     * who are not interrested in XML) and it may actually be 
less efficient for the vast
+                     * majority of cases where there is less than 3 aliases.
+                     */
+                    final int size = alias.size();
+                    final GenericName[] names = alias.toArray(new 
GenericName[size + 1]);
+                    names[size] = n;
+                    alias = UnmodifiableArrayList.wrap(names);
+                }
             }
-            /*
-             * Our Code and RS_Identifier implementations should always create 
NamedIdentifier instance,
-             * so the 'instanceof' check should not be necessary. But do a 
paranoiac check anyway.
-             */
-            return alias.add(id instanceof GenericName ? (GenericName) id : 
new NamedIdentifier(id));
+            return true;
         }
     }
 
     /**
      * Returns the {@link #name} and all aliases which are also instance of 
{@lik ReferenceIdentifier}.
      * The later happen often in SIS implementation since many aliases are 
instance of {@link NamedIdentifier}.
-     */
-    @XmlElement(name = "name", required = true)
-    final Collection<ReferenceIdentifier> getNames() {
-        return new Names();
-    }
-
-    /**
-     * Sets the first element as the {@link #name} and all remaining elements 
as {@link #alias}.
-     * This method is invoked by some implementations of JAXB (not all of 
them) at unmarshalling time.
-     * It should not be invoked anymore after the object has been made 
available to the user.
      *
-     * <p>Some JAXB implementations never invoke this setter method. Instead 
they invoke {@link #getNames()}
-     * and add directly the identifiers in the returned collection. Whether 
JAXB will perform a final call to
+     * <p>The returned collection is <cite>live</cite>: adding elements in 
that collection will modify this
+     * {@code AbstractIdentifiedObject} instance. This is needed for 
unmarshalling with JAXB and should not
+     * be used in other context.</p>
+     *
+     * {@section Why there is no <code>setNames(…)</code> method}
+     * Some JAXB implementations never invoke setter method for collections. 
Instead they invoke the getter and
+     * add directly the identifiers in the returned collection. Whether JAXB 
will perform or not a final call to
      * {@code setNames(…)} is JAXB-implementation dependent (JDK7 does but 
JDK6 and JDK8 early access do not).
-     * Consequently we can not rely on this method to be invoked. It is better 
if this method is invoked, but
-     * we will not lost data if it is not.</p>
+     * It seems a more portable approach (at least for JAXB reference 
implementations) to design our class
+     * without setter method, in order to have the same behavior on all 
supported JDK versions.
      *
      * @see <a href="https://java.net/jira/browse/JAXB-488";>JAXB-488</a>
      */
-    private void setNames(final Collection<ReferenceIdentifier> names) {
-        /*
-         * If the collection is an instance of Names, then assume that the 
collection is the instance obtained
-         * by getNames(), in which case this IdentifiedObject already contains 
the content of that collection.
-         * This behavior is necessary for working around the JAXB-488 issue.
-         */
-        if (!(names instanceof Names)) {
-            getNames().addAll(names);
-        }
-        /*
-         * Froze aliases in an unmodifiable set. In JAXB implementations that 
do not invoke the setter method,
-         * the aliases list is left modifiable. This is a hole in our object 
immutability.
-         */
-        if (alias != null) {
-            alias = immutableSet(true, alias.toArray(new 
GenericName[alias.size()]));
-        }
+    @XmlElement(name = "name", required = true)
+    final Collection<ReferenceIdentifier> getNames() {
+        return new Names();
     }
 
     /**

Modified: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java?rev=1556920&r1=1556919&r2=1556920&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java
 [UTF-8] Thu Jan  9 18:55:40 2014
@@ -18,6 +18,8 @@ package org.apache.sis.internal.util;
 
 import java.io.Serializable;
 import java.util.AbstractList;
+import java.util.Arrays;
+import java.lang.reflect.Array;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.collection.CheckedContainer;
 
@@ -69,7 +71,7 @@ public class UnmodifiableArrayList<E> ex
     /**
      * The wrapped array.
      */
-    private final E[] array;
+    final E[] array;
 
     /**
      * Creates a new instance wrapping the given array. A direct reference to 
the given array is
@@ -167,6 +169,8 @@ public class UnmodifiableArrayList<E> ex
 
     /**
      * Returns the list size.
+     *
+     * @return The size of this list.
      */
     @Override
     public int size() {
@@ -192,6 +196,9 @@ public class UnmodifiableArrayList<E> ex
 
     /**
      * Returns the element at the specified index.
+     *
+     * @param  index The index of the element to get.
+     * @return The element at the given index.
      */
     @Override
     public E get(final int index) {
@@ -355,11 +362,21 @@ public class UnmodifiableArrayList<E> ex
             ArgumentChecks.ensureValidIndex(size, index);
             return super.get(index + lower);
         }
+
+        /**
+         * Returns a copy of the backing array section viewed by this sublist.
+         */
+        @Override
+        public E[] toArray() {
+            return Arrays.copyOfRange(array, lower, lower + size);
+        }
     }
 
     /**
      * Returns a copy of the backing array. Note that the array type is {@code 
E[]} rather than {@code Object[]}.
      * This is not what {@code ArrayList} does, but is not forbidden by {@link 
List#toArray()} javadoc neither.
+     *
+     * @return A copy of the wrapped array.
      */
     @Override
     public E[] toArray() {
@@ -367,6 +384,35 @@ public class UnmodifiableArrayList<E> ex
     }
 
     /**
+     * Copies the backing array in the given one if the list fits in the given 
array.
+     * If the list does not fit in the given array, returns the collection in 
a new array.
+     *
+     * @param  <T>   The type of array element.
+     * @param  dest  The array where to copy the elements if the list can fits 
in the array.
+     * @return The given array, or a newly created array if this list is 
larger than the given array.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T[] toArray(T[] dest) {
+        final int size = size();
+        if (dest.length != size) {
+            if (dest.length < size) {
+                /*
+                 * We are cheating here since the array component may not be 
assignable to T.
+                 * But if this assumption is wrong, then the call to 
System.arraycopy(…) later
+                 * will thrown an ArrayStoreException, which is the exception 
type required by
+                 * the Collection.toArray(T[]) javadoc.
+                 */
+                dest = (T[]) 
Array.newInstance(dest.getClass().getComponentType(), size);
+            } else {
+                dest[size] = null; // Required by Collection.toArray(T[]) 
javadoc.
+            }
+        }
+        System.arraycopy(array, lower(), dest, 0, size);
+        return dest;
+    }
+
+    /**
      * Compares this list with the given object for equality.
      *
      * @param  object The object to compare with this list.


Reply via email to