sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1451422 - in /sis/branches/JDK7: sis-metadata/src/main/java/org/apache/sis/metadata/ sis-metadata/src/test/java/org/apache/sis/metadata/ sis-utility/src/main/java/org/apache/sis/internal/simple/ sis-utility/src/main/java/org/apache/sis/int...
Date Thu, 28 Feb 2013 23:09:22 GMT
Author: desruisseaux
Date: Thu Feb 28 23:09:22 2013
New Revision: 1451422

URL: http://svn.apache.org/r1451422
Log:
Ported a little bit more of metadata internal mechanics.

Added:
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
  (with props)
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java   (with
props)
Modified:
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1451422&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
(added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata;
+
+import java.util.logging.Logger;
+import java.lang.reflect.Modifier;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.LenientComparable;
+import org.apache.sis.util.logging.Logging;
+
+
+/**
+ * Base class for metadata implementations, providing basic operations using Java reflection.
+ * Available operations include the {@linkplain #AbstractMetadata(Object) copy constructor},
+ * together with {@link #equals(Object)} and {@link #hashCode()} implementations.
+ *
+ * {@section Requirements for subclasses}
+ * Subclasses need to implement the interfaces of some {@linkplain MetadataStandard metadata
standard}
+ * and return that standard in the {@link #getStandard()} method.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+@ThreadSafe
+public abstract class AbstractMetadata implements LenientComparable {
+    /**
+     * The logger for messages related to metadata implementations.
+     */
+    protected static final Logger LOGGER = Logging.getLogger(AbstractMetadata.class);
+
+    /**
+     * Creates an initially empty metadata.
+     */
+    protected AbstractMetadata() {
+    }
+
+    /**
+     * Returns the class of the given metadata, ignoring SIS private classes
+     * like {@link org.apache.sis.metadata.iso.citation.CitationConstant}.
+     *
+     * @see <a href="http://jira.geotoolkit.org/browse/GEOTK-48">GEOTK-48</a>
+     */
+    private static Class<?> getClass(final Object metadata) {
+        Class<?> type = metadata.getClass();
+        while (!Modifier.isPublic(type.getModifiers()) && type.getName().startsWith("org.geotoolkit.metadata.iso."))
{ // TODO
+            type = type.getSuperclass();
+        }
+        return type;
+    }
+
+    /**
+     * Compares this metadata with the specified object for equality. The default
+     * implementation uses Java reflection. Subclasses may override this method
+     * for better performances, or for comparing "hidden" attributes not specified
+     * by the GeoAPI (or other standard) interface.
+     *
+     * <p>This method performs a <cite>deep</cite> comparison: if this
metadata contains
+     * other metadata, then the comparison will invoke the {@link Object#equals(Object)}
+     * method on those children as well.</p>
+     *
+     * @param  object The object to compare with this metadata.
+     * @param  mode The strictness level of the comparison.
+     * @return {@code true} if the given object is equal to this metadata.
+     */
+    @Override
+    public boolean equals(final Object object, final ComparisonMode mode) {
+        if (object == this) {
+            return true;
+        }
+        if (mode == ComparisonMode.STRICT) {
+            if (object == null || getClass(object) != getClass(this)) {
+                return false;
+            }
+        }
+        // TODO: There is some code to port here.
+        /*
+         * DEADLOCK WARNING: A deadlock may occur if the same pair of objects is being compared
+         * in an other thread (see http://jira.codehaus.org/browse/GEOT-1777). Ideally we
would
+         * synchronize on 'this' and 'object' atomically (RFE #4210659). Since we can't in
Java
+         * a workaround is to always get the locks in the same order. Unfortunately we have
no
+         * guarantee that the caller didn't looked the object himself. For now the safest
approach
+         * is to not synchronize at all.
+         */
+        // TODO: There is some code to port here.
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+
+    /**
+     * Performs a {@linkplain ComparisonMode#STRICT strict} comparison of this metadata with
+     * the given object.
+     *
+     * @param object The object to compare with this metadata for equality.
+     */
+    @Override
+    public final boolean equals(final Object object) {
+        return equals(object, ComparisonMode.STRICT);
+    }
+
+    /**
+     * Computes a hash code value for this metadata using Java reflection. The hash code
+     * is defined as the sum of hash code values of all non-null properties. This is the
+     * same contract than {@link java.util.Set#hashCode()} and ensure that the hash code
+     * value is insensitive to the ordering of properties.
+     */
+    @Override
+    public synchronized int hashCode() {
+        // TODO: There is some code to port here.
+        throw new UnsupportedOperationException("Not yet implemented.");
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java?rev=1451422&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java [UTF-8]
Thu Feb 28 23:09:22 2013
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import org.apache.sis.util.collection.CollectionsExt;
+import org.apache.sis.util.collection.UnmodifiableArrayList;
+
+
+/**
+ * Returns unmodifiable view of metadata elements of arbitrary type.
+ * Despite the {@code Cloner} class name, this class actually tries
+ * to avoid creating new clones as much as possible.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-2.1)
+ * @version 0.3
+ * @module
+ */
+final class Cloner extends org.apache.sis.internal.util.Cloner {
+    /**
+     * Creates a new {@code Cloner} instance.
+     */
+    Cloner() {
+    }
+
+    /**
+     * Tells {@link #clone(Object)} to return the original object
+     * if no public {@code clone()} method is found.
+     */
+    @Override
+    protected boolean isCloneRequired(final Object object) {
+        return false;
+    }
+
+    /**
+     * Returns an unmodifiable copy of the specified object.
+     * This method performs the following heuristic tests:
+     *
+     * <ul>
+     *   <li>If the specified object is an instance of {@code ModifiableMetadata},
+     *       then {@link ModifiableMetadata#unmodifiable()} is invoked on that object.</li>
+     *   <li>Otherwise, if the object is a {@linkplain Collection collection}, then
the
+     *       content is copied into a new collection of similar type, with values replaced
+     *       by their unmodifiable variant.</li>
+     *   <li>Otherwise, if the object implements the {@link Cloneable} interface,
+     *       then a clone is returned.</li>
+     *   <li>Otherwise, the object is assumed immutable and returned unchanged.</li>
+     * </ul>
+     *
+     * @param  object The object to convert in an immutable one.
+     * @return A presumed immutable view of the specified object.
+     */
+    @Override
+    public Object clone(final Object object) {
+        /*
+         * CASE 1 - The object is an implementation of ModifiableMetadata. It may have
+         *          its own algorithm for creating an unmodifiable view of metadata.
+         */
+        if (object instanceof ModifiableMetadata) {
+            return ((ModifiableMetadata) object).unmodifiable();
+        }
+        /*
+         * CASE 2 - The object is a collection. All elements are replaced by their
+         *          unmodifiable variant and stored in a new collection of similar
+         *          type.
+         */
+        if (object instanceof Collection<?>) {
+            Collection<?> collection = (Collection<?>) object;
+            final boolean isSet = (collection instanceof Set<?>);
+            if (collection.isEmpty()) {
+                if (isSet) {
+                    collection = Collections.EMPTY_SET;
+                } else {
+                    collection = Collections.EMPTY_LIST;
+                }
+            } else {
+                final Object[] array = collection.toArray();
+                for (int i=0; i<array.length; i++) {
+                    array[i] = clone(array[i]);
+                }
+                // Do not use the SIS Checked* classes since we don't
+                // need synchronization or type checking anymore.
+                if (isSet) {
+                    collection = CollectionsExt.immutableSet(array);
+                } else {
+                    // Conservatively assumes a List if we are not sure to have a Set,
+                    // since the list is less destructive (no removal of duplicated).
+                    collection = UnmodifiableArrayList.wrap(array);
+                }
+            }
+            return collection;
+        }
+        /*
+         * CASE 3 - The object is a map. Copies all entries in a new map and replaces all
values
+         *          by their unmodifiable variant. The keys are assumed already immutable.
+         */
+        if (object instanceof Map<?,?>) {
+            final Map<Object,Object> map = new LinkedHashMap<>((Map<?,?>)
object);
+            for (final Iterator<Map.Entry<Object,Object>> it=map.entrySet().iterator();
it.hasNext();) {
+                final Map.Entry<Object,Object> entry = it.next();
+                entry.setValue(clone(entry.getValue()));
+            }
+            return CollectionsExt.unmodifiableOrCopy(map);
+        }
+        /*
+         * CASE 4 - The object is presumed cloneable.
+         */
+        if (object instanceof Cloneable) try {
+            return super.clone(object);
+        } catch (CloneNotSupportedException e) {
+            throw new UnsupportedOperationException(e);
+        }
+        /*
+         * CASE 5 - Any other case. The object is assumed immutable and returned unchanged.
+         */
+        return object;
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -23,6 +23,7 @@ import java.util.Collections;
 import java.util.NoSuchElementException;
 import net.jcip.annotations.ThreadSafe;
 import org.opengis.util.CodeList;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.collection.CheckedHashSet;
 import org.apache.sis.util.collection.CheckedArrayList;
 
@@ -44,29 +45,33 @@ import static org.apache.sis.util.collec
  * <p>For singleton value:</p>
  *
  * {@preformat java
- *     private Foo property;
+ *     public class MyMetadata {
+ *         private Foo property;
  *
- *     public synchronized Foo getProperty() {
- *         return property;
- *     }
+ *         public synchronized Foo getProperty() {
+ *             return property;
+ *         }
  *
- *     public synchronized void setProperty(Foo newValue) {
- *         checkWritePermission();
- *         property = newValue;
+ *         public synchronized 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 synchronized Collection<Foo> getProperties() {
+ *             return properties = nonNullCollection(properties, Foo.class);
+ *         }
  *
- *     public synchronized void setProperties(Collection<Foo> newValues) {
- *         properties = copyCollection(newValues, properties, Foo.class);
+ *         public synchronized void setProperties(Collection<Foo> newValues) {
+ *             properties = copyCollection(newValues, properties, Foo.class);
+ *         }
  *     }
  * }
  *
@@ -76,7 +81,16 @@ import static org.apache.sis.util.collec
  * @module
  */
 @ThreadSafe
-public abstract class ModifiableMetadata {
+public abstract class ModifiableMetadata extends AbstractMetadata implements Cloneable {
+    /**
+     * 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.
      */
@@ -93,8 +107,83 @@ public abstract class ModifiableMetadata
      * @see #freeze()
      * @see #checkWritePermission()
      */
-    public final boolean isModifiable() {
-        return true; // To be implemented later.
+    public final synchronized boolean isModifiable() {
+        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 synchronized 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 attributes
+                 * 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 attributes 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 synchronized void freeze() {
+        if (isModifiable()) {
+            throw new UnsupportedOperationException("Not yet implemented."); // TODO
+        }
     }
 
     /**
@@ -112,6 +201,7 @@ public abstract class ModifiableMetadata
         if (!isModifiable()) {
             throw new UnmodifiableMetadataException("Unmodifiable metadata"); // TODO: localize
         }
+        unmodifiable = null;
     }
 
     /**
@@ -137,7 +227,6 @@ public abstract class ModifiableMetadata
      *
      * @see #nonNullList(List, Class)
      */
-    @SuppressWarnings("unchecked")
     protected final <E> List<E> copyList(final Collection<? extends E>
source,
             List<E> target, final Class<E> elementType)
             throws UnmodifiableMetadataException
@@ -182,7 +271,6 @@ public abstract class ModifiableMetadata
      *
      * @see #nonNullSet(Set, Class)
      */
-    @SuppressWarnings("unchecked")
     protected final <E> Set<E> copySet(final Collection<? extends E> source,
             Set<E> target, final Class<E> elementType)
             throws UnmodifiableMetadataException
@@ -233,7 +321,6 @@ public abstract class ModifiableMetadata
      *         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,
             Collection<E> target, final Class<E> elementType)
             throws UnmodifiableMetadataException
@@ -451,4 +538,22 @@ public abstract class ModifiableMetadata
         return (Class) (CodeList.class.isAssignableFrom(elementType) ||
                             Enum.class.isAssignableFrom(elementType) ? Set.class : List.class);
     }
+
+    /**
+     * Returns a shallow copy of this metadata.
+     *
+     * {@section Usage}
+     * While {@linkplain Cloneable cloneable}, this class do 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 <strong>shallow</strong>
+     * copies of metadata entities. The default {@link Object#clone()} implementation is
+     * sufficient in most cases.
+     *
+     * @return A <strong>shallow</strong> copy of this metadata.
+     * @throws CloneNotSupportedException if the clone is not supported.
+     */
+    @Override
+    protected ModifiableMetadata clone() throws CloneNotSupportedException {
+        return (ModifiableMetadata) super.clone();
+    }
 }

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -117,6 +117,11 @@ final class PropertyAccessor {
     }
 
     /**
+     * The standard which define the {@link #type} interface.
+     */
+    private final Citation standard;
+
+    /**
      * The implemented metadata interface.
      */
     final Class<?> type;
@@ -213,14 +218,16 @@ final class PropertyAccessor {
     /**
      * Creates a new property accessor for the specified metadata implementation.
      *
-     * @param  implementation The type of metadata implementations to wrap.
+     * @param  standard The standard which define the {@code type} interface.
      * @param  type The interface implemented by the metadata, which must be
      *         the value returned by {@link #getStandardType(Class, String)}.
+     * @param  implementation The class of metadata implementations.
      */
-    PropertyAccessor(final Class<?> implementation, final Class<?> type) {
+    PropertyAccessor(final Citation standard, final Class<?> type, final Class<?>
implementation) {
         assert type.isAssignableFrom(implementation) : implementation;
-        this.implementation = implementation;
+        this.standard       = standard;
         this.type           = type;
+        this.implementation = implementation;
         this.getters        = getGetters(type);
         int allCount = getters.length;
         int standardCount = allCount;
@@ -703,7 +710,7 @@ final class PropertyAccessor {
         ParameterDescriptor<?> descriptor = descriptors[index];
         if (descriptor == null) {
             final Class<?> elementType = elementTypes[index];
-            final Citation standard    = null; // TODO
+            final Citation standard    = this.standard;
             final String   name        = name(index, KeyNamePolicy.UML_IDENTIFIER);
             final Method   getter      = getters[index];
             ValueRange range = null;
@@ -958,8 +965,8 @@ final class PropertyAccessor {
                      * runtime. However other implementations could use unchecked collection.
                      * There is not much we can do...
                      */
-                    final Collection<Object> unsafe = (Collection<Object>) addTo;
-                    unsafe.add(elements[0]);
+                    // No @SuppressWarnings because this is a real hole.
+                    ((Collection<Object>) addTo).add(elements[0]);
                 }
             }
             /*
@@ -1104,12 +1111,13 @@ final class PropertyAccessor {
         assert implementation.isInstance(metadata) : metadata;
         if (setters != null) {
             final Object[] arguments = new Object[1];
+            final Cloner cloner = new Cloner();
             for (int i=0; i<allCount; i++) {
                 final Method setter = setters[i];
                 if (setter != null) {
                     final Method getter = getters[i];
                     final Object source = get(getter, metadata);
-                    final Object target = null; // TODO ModifiableMetadata.unmodifiable(source);
+                    final Object target = cloner.clone(source);
                     if (source != target) {
                         arguments[0] = target;
                         set(setter, metadata, arguments);

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -54,6 +54,11 @@ import java.util.Objects;
  * @module
  *
  * @see MetadataStandard#asDescriptorMap(Object, KeyNamePolicy, NullValuePolicy)
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-80">SIS-80</a>
+ *
+ * @todo Implementing {@code ParameterDescriptor} is not really appropriate since metadata
properties
+ *       are not parameters. Implementing {@link org.opengis.feature.type.PropertyDescriptor}
would
+ *       be better, but the later is not yet part of GeoAPI standard and needs cleaning.
See SIS-80.
  */
 @Immutable
 class PropertyDescriptor<T> extends SimpleReferenceIdentifier implements ParameterDescriptor<T>
{

Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -24,7 +24,7 @@ import org.apache.sis.internal.simple.Si
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 
 
 /**
@@ -92,4 +92,14 @@ public final strictfp class PropertyDesc
         assertEquals(0, descriptor.getMinimumOccurs());
         assertEquals(Integer.MAX_VALUE, descriptor.getMaximumOccurs());
     }
+
+    /**
+     * Tests serialization.
+     *
+     * @throws NoSuchMethodException Should never happen.
+     */
+    @Test
+    public void testSerialization() throws NoSuchMethodException {
+        assertSerializedEquals(create(InternationalString.class, "getTitle", "title"));
+    }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -28,9 +28,11 @@ 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.util.iso.SimpleInternationalString;
 
+// Related to JDK7
+import java.util.Objects;
+
 
 /**
  * A trivial implementation of {@link Citation}.
@@ -87,6 +89,28 @@ public class SimpleCitation implements C
     @Override public String                           getISSN()                    {return
null;}
 
     /**
+     * Compares the given object with this citation for equality.
+     *
+     * @param  obj The object to compare with this citation.
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj != null && obj.getClass() == getClass()) {
+            return Objects.equals(title, ((SimpleCitation) obj).title);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a hash code value for this citation.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(title) ^ (int) serialVersionUID;
+    }
+
+    /**
      * Returns a string representation of this citation.
      */
     @Override

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java [UTF-8]
(original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java [UTF-8]
Thu Feb 28 23:09:22 2013
@@ -18,6 +18,7 @@ package org.apache.sis.internal.util;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
+import net.jcip.annotations.NotThreadSafe;
 import org.apache.sis.util.Workaround;
 import org.apache.sis.util.resources.Errors;
 
@@ -31,15 +32,18 @@ import org.apache.sis.util.resources.Err
  * @version 0.3
  * @module
  */
+@NotThreadSafe
 @Workaround(library="JDK", version="1.7")
-public final class Cloner {
+public class Cloner {
     /**
      * The type of the object to clone, or {@code null} if not yet specified.
+     * Used for checking if the cached {@linkplain #method} is still valid.
      */
     private Class<?> type;
 
     /**
      * The {@code clone()} method, or {@code null} if not yet determined.
+     * This is cached for better performance if many instances of the same type are cloned.
      */
     private Method method;
 
@@ -50,7 +54,30 @@ public final class Cloner {
     }
 
     /**
-     * Clones the given object.
+     * Invoked when the given object can not be cloned because no public {@code clone()}
method
+     * has been found. If this method returns {@code true}, then the {@link #clone(Object)}
+     * method in this class will throw a {@link CloneNotSupportedException}. Otherwise the
+     * {@code clone(Object)} method will return the original object.
+     *
+     * <p>The default implementation returns {@code true} in every cases.
+     * Subclasses can override this method if they need a different behavior.</p>
+     *
+     * @param  object The object that can not be cloned.
+     * @return {@code true} if the problem shall be considered a clone failure.
+     */
+    protected boolean isCloneRequired(final Object object) {
+        return true;
+    }
+
+    /**
+     * Clones the given object. If the given object does not provide a public {@code clone()}
+     * method, then there is a choice:
+     *
+     * <ul>
+     *   <li>If {@code isCloneRequired(object)} returns {@code true} (the default),
+     *       then a {@link CloneNotSupportedException} is thrown.</li>
+     *   <li>Otherwise the given object is returned.</li>
+     * </ul>
      *
      * @param  object The object to clone, or {@code null}.
      * @return A clone of the given object, or {@code null} if {@code object} was null.
@@ -66,7 +93,9 @@ public final class Cloner {
                 method = valueType.getMethod("clone", (Class<?>[]) null);
                 type = valueType; // Set only if the above line succeed.
             }
-            return method.invoke(object, (Object[]) null);
+            if (method != null) { // May be null if previous call threw NoSuchMethodException.
+                return method.invoke(object, (Object[]) null);
+            }
         } catch (InvocationTargetException e) {
             final Throwable cause = e.getCause();
             if (cause instanceof CloneNotSupportedException) {
@@ -79,9 +108,16 @@ public final class Cloner {
                 throw (Error) cause;
             }
             throw fail(e);
+        } catch (NoSuchMethodException e) {
+            if (isCloneRequired(object)) {
+                throw fail(e);
+            }
+            method = null;
+            type = valueType;
         } catch (ReflectiveOperationException e) {
             throw fail(e);
         }
+        return object;
     }
 
     /**
@@ -91,7 +127,7 @@ public final class Cloner {
      * @param  cause The cause for the failure to clone an object.
      * @return An exception with an error message and the given cause.
      */
-    public CloneNotSupportedException fail(final Throwable cause) {
+    private CloneNotSupportedException fail(final Throwable cause) {
         CloneNotSupportedException e = new CloneNotSupportedException(
                 Errors.format(Errors.Keys.CloneNotSupported_1, type));
         e.initCause(cause);



Mime
View raw message