Author: desruisseaux
Date: Sat Mar  2 23:11:13 2013
New Revision: 1451957

URL: http://svn.apache.org/r1451957
Log:
- Try to explain more in the javadoc what we are doing.
- Replace HashMap<Class,...> by IdentityHashMap and explain why in javadoc.
- Removed MetadataStandard.isModifiable(...) method (part of effort to simplify 
the code).

Modified:
    
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
    
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.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/StandardImplementation.java
    
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Numbers.java

Modified: 
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=1451957&r1=1451956&r2=1451957&view=diff
==============================================================================
--- 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
 [UTF-8] Sat Mar  2 23:11:13 2013
@@ -18,7 +18,6 @@ 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;
@@ -26,19 +25,32 @@ import org.apache.sis.util.logging.Loggi
 
 /**
  * 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.
+ * All {@code AbstractMetadata} instances shall be associated to a {@link 
MetadataStandard},
+ * which is provided by subclasses in the {@link #getStandard()} method. There 
is typically
+ * a large number of {@code AbstractMetadata} subclasses (not necessarily as 
direct children)
+ * for the same standard.
  *
- * {@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.
+ * <p>This base class reduces the effort required to implement metadata 
interface by providing
+ * {@link #equals(Object)}, {@link #hashCode()} and {@link #toString()} 
implementations.
+ * Those methods are implemented using Java reflection for invoking the getter 
methods
+ * defined by the {@code MetadataStandard}.</p>
+ *
+ * <p>{@code AbstractMetadata} may be read-only or read/write, at 
implementation choice.
+ * The {@link ModifiableMetadata} subclass provides the basis of most SIS 
metadata classes
+ * having writing capabilities.</p>
+ *
+ * {@section Synchronization}
+ * The methods in this class are not synchronized. Synchronizations may be 
done by getter and
+ * setter methods in subclasses, at implementation choice. We never 
synchronize the methods that
+ * perform deep traversal of the metadata tree (like {@code equals(Object)}, 
{@code hashCode()}
+ * or {@code toString()}) because such synchronizations are deadlock prone. 
For example if
+ * subclasses synchronize their getter methods, then many locks may be 
acquired in various orders.
  *
  * @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.
@@ -52,8 +64,36 @@ public abstract class AbstractMetadata i
     }
 
     /**
+     * Returns the metadata standard implemented by subclasses.
+     *
+     * @return The metadata standard implemented.
+     *
+     * @todo This method returns {@link MetadataStandard#ISO_19115} for now,
+     *       but will become an abstract method soon.
+     */
+    public MetadataStandard getStandard() {
+        return MetadataStandard.ISO_19115;
+    }
+
+    /**
+     * Returns the metadata interface implemented by this class. It should be 
one of the
+     * interfaces defined in the {@linkplain #getStandard() metadata standard} 
implemented
+     * by this class.
+     *
+     * @return The standard interface implemented by this implementation class.
+     *
+     * @see MetadataStandard#getInterface(Class)
+     */
+    public Class<?> getInterface() {
+        // No need to sychronize, since this method does not depend on 
property values.
+        return getStandard().getInterface(getClass());
+    }
+
+    /**
      * Returns the class of the given metadata, ignoring SIS private classes
      * like {@link org.apache.sis.metadata.iso.citation.CitationConstant}.
+     * This method does <strong>not</strong> ignores user's private classes,
+     * only the SIS ones.
      *
      * @see <a href="http://jira.geotoolkit.org/browse/GEOTK-48";>GEOTK-48</a>
      */
@@ -86,12 +126,20 @@ public abstract class AbstractMetadata i
         if (object == this) {
             return true;
         }
+        if (object == null) {
+            return false;
+        }
         if (mode == ComparisonMode.STRICT) {
-            if (object == null || getPublicClass(object) != 
getPublicClass(this)) {
+            if (getPublicClass(object) != getPublicClass(this)) {
+                return false;
+            }
+        }
+        final MetadataStandard standard = getStandard();
+        if (mode != ComparisonMode.STRICT) {
+            if (!getInterface().isInstance(object)) {
                 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
@@ -99,16 +147,28 @@ public abstract class AbstractMetadata i
          * 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.
+         *
+         * Edit: actually, even if we could synchronize the two objects 
atomically, a deadlock
+         *       risk would still exists for the reason documented in this 
class's javadoc.
          */
-        // TODO: There is some code to port here.
-        throw new UnsupportedOperationException("Not yet implemented.");
+        return standard.shallowEquals(this, object, mode, false);
     }
 
     /**
      * Performs a {@linkplain ComparisonMode#STRICT strict} comparison of this 
metadata with
-     * the given object.
+     * the given object. This method is implemented as below:
+     *
+     * {@preformat java
+     *     public final boolean equals(final Object object) {
+     *         return equals(object, ComparisonMode.STRICT);
+     *     }
+     * }
+     *
+     * If a subclass needs to override the behavior of this method, then
+     * override {@link #equals(Object, ComparisonMode)} instead.
      *
-     * @param object The object to compare with this metadata for equality.
+     * @param  object The object to compare with this metadata for equality.
+     * @return {@code true} if the given object is strictly equals to this 
metadata.
      */
     @Override
     public final boolean equals(final Object object) {
@@ -120,10 +180,16 @@ public abstract class AbstractMetadata i
      * 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.
+     *
+     * {@section Performance note}
+     * This method does not cache the value because current implementation has 
no notification
+     * mechanism for tracking changes in children properties. If this metadata 
is known to be
+     * immutable, then subclasses may consider caching the hash code value at 
their choice.
+     *
+     * @see MetadataStandard#hashCode(Object)
      */
     @Override
-    public synchronized int hashCode() {
-        // TODO: There is some code to port here.
-        throw new UnsupportedOperationException("Not yet implemented.");
+    public int hashCode() {
+        return getStandard().hashCode(this);
     }
 }

Modified: 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1451957&r1=1451956&r2=1451957&view=diff
==============================================================================
--- 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
 [UTF-8] Sat Mar  2 23:11:13 2013
@@ -41,21 +41,34 @@ import static org.apache.sis.util.Argume
  * {@linkplain java.lang.reflect Java reflection}. The following rules are 
assumed:</p>
  *
  * <ul>
- *   <li>Properties (or metadata attributes) are defined by the collection of 
{@code get*()}
- *       methods with arbitrary return type, or {@code is*()} methods with 
boolean return type,
- *       found in the <strong>interface</strong>. Getters declared only in the 
implementation
- *       are ignored.</li>
- *   <li>Every properties are <cite>readable</cite>.</li>
- *   <li>A property is <cite>writable</cite> if a {@code set*(...)} method is 
defined
- *       in the implementation class for the corresponding {@code get*()} 
method. The
- *       setter doesn't need to be defined in the interface.</li>
+ *   <li>Properties (or metadata attributes) are defined by the collection of
+ *       following getter methods found <strong>in the interface</strong>
+ *       (methods declared only in the implementation are ignored):
+ *       <ul>
+ *         <li>{@code get*()} methods with arbitrary return type;</li>
+ *         <li>or {@code is*()} methods with boolean return type.</li>
+ *       </ul></li>
+ *   <li>Every properties are <cite>readable</cite>.
+ *       But a property is also <cite>writable</cite> if a {@code set*(…)} 
method is defined
+ *       <strong>in the implementation class</strong> for the corresponding 
getter method.
+ *       The setter method doesn't need to be defined in the interface.</li>
  * </ul>
  *
  * An instance of {@code MetadataStandard} is associated to every {@link 
AbstractMetadata} objects.
  * The {@code AbstractMetadata} base class usually form the basis of ISO 19115 
implementations but
- * can also be used for other standards. An instance of {@code 
MetadataStandard} is also associated
- * with Image I/O {@link 
org.apache.sis.image.io.metadata.SpatialMetadataFormat} in order to define
- * the tree of XML nodes to be associated with raster data.
+ * can also be used for other standards.
+ *
+ * {@section Defining new <code>MetadataStandard</code> instances}
+ * Users should use the pre-defined constants when applicable.
+ * However if new instances need to be defined, then there is a choice:
+ *
+ * <ul>
+ *   <li>For <em>read-only</em> metadata, {@code MetadataStandard} can be 
instantiated directly.
+ *       only getter methods will be used and all operations that modify the 
metadata properties
+ *       will throw an {@link UnmodifiableMetadataException}.</li>
+ *   <li>For <em>read/write</em> metadata, the {@link 
#getImplementation(Class)}
+ *       method must be overridden in a {@code MetadataStandard} subclass.</li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.4)
@@ -67,6 +80,8 @@ public class MetadataStandard {
     /**
      * The package of SIS implementation of ISO 19115.
      * This package name has a trailing dot.
+     *
+     * @see #interfacePackage
      */
     static final String SIS_PACKAGE = "org.apache.sis.metadata.iso.";
 
@@ -363,7 +378,7 @@ public class MetadataStandard {
      * @throws ClassCastException if the specified implementation class does
      *         not implement an interface of this standard.
      *
-     * @see AbstractMetadata#getInterface
+     * @see AbstractMetadata#getInterface()
      */
     public Class<?> getInterface(final Class<?> type) throws 
ClassCastException {
         ensureNonNull("type", type);
@@ -386,19 +401,6 @@ public class MetadataStandard {
     }
 
     /**
-     * Returns {@code true} if this metadata is modifiable. This method is not 
public because it
-     * uses heuristic rules. In case of doubt, this method conservatively 
returns {@code true}.
-     *
-     * @throws ClassCastException if the specified implementation class do
-     *         not implements a metadata interface of the expected package.
-     *
-     * @see ModifiableMetadata#isModifiable()
-     */
-    final boolean isModifiable(final Class<?> implementation) throws 
ClassCastException {
-        return getAccessor(implementation, true).isModifiable();
-    }
-
-    /**
      * Replaces every properties in the specified metadata by their
      * {@linkplain ModifiableMetadata#unmodifiable() unmodifiable variant}.
      *
@@ -442,18 +444,21 @@ public class MetadataStandard {
 
     /**
      * Compares the two specified metadata objects.
-     * The comparison is <cite>shallow</cite>, i.e. all metadata attributes 
are compared using the
-     * {@link LenientComparable#equals(Object, ComparisonMode)} method if 
possible, or the
-     * {@link Object#equals(Object)} method otherwise, without explicit 
recursive call to
-     * this {@code shallowEquals(...)} method for child metadata.
+     * The two metadata arguments shall be implementations of a metadata 
interface defined by
+     * this {@code MetadataStandard}, otherwise an exception will be thrown. 
However the two
+     * arguments do not need to be the same implementation class.
      *
      * <p>This method can optionally excludes null values from the comparison. 
In metadata,
-     * null value often means "don't know", so in some occasion we want to 
consider two
+     * null value often means "don't know", so in some occasions we want to 
consider two
      * metadata as different only if a property value is know for sure to be 
different.</p>
      *
-     * <p>The first arguments must be an implementation of a metadata 
interface, otherwise an
-     * exception will be thrown. The two arguments do not need to be the same 
implementation
-     * however.</p>
+     * {@section Shallow or deep comparisons}
+     * This method implements a <cite>shallow</cite> comparison in that 
properties are compared by
+     * invoking their {@code properties.equals(…)} method without 
<em>explicit</em> recursive call
+     * to this {@code shallowEquals(…)} method for children metadata. However 
the comparison will
+     * do <em>implicit</em> recursive calls if the {@code 
properties.equals(…)} implementations
+     * invoke this {@code shallowEquals(…)} method. In the later case, the 
final result is a deep
+     * comparison.
      *
      * @param metadata1 The first metadata object to compare.
      * @param metadata2 The second metadata object to compare.

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=1451957&r1=1451956&r2=1451957&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] Sat Mar  2 23:11:13 2013
@@ -96,6 +96,14 @@ final class PropertyAccessor {
     /**
      * Getters shared between many instances of this class. Two different 
implementations
      * may share the same getters but different setters.
+     *
+     * {@note In the particular case of <code>Class</code> keys, 
<code>IdentityHashMap</code> and
+     *        <code>HashMap</code> have identical behavior since 
<code>Class</code> is final and
+     *        does not override the <code>equals(Object)</code> and 
<code>hashCode()</code> methods.
+     *        The <code>IdentityHashMap</code> Javadoc claims that it is 
faster than the regular
+     *        <code>HashMap</code>. But maybe the most interesting property is 
that it allocates
+     *        less objects since <code>IdentityHashMap</code> implementation 
doesn't need the chain
+     *        of objects created by <code>HashMap</code>.}
      */
     private static final Map<Class<?>, Method[]> SHARED_GETTERS = new 
IdentityHashMap<>();
 
@@ -951,8 +959,8 @@ final class PropertyAccessor {
 
     /**
      * Compares the two specified metadata objects. The comparison is 
<cite>shallow</cite>,
-     * i.e. all metadata attributes are compared using the {@link 
Object#equals(Object)}
-     * method without recursive call to this {@code shallowEquals} method for 
other metadata.
+     * i.e. all metadata properties are compared using their {@code 
properties.equals(…)}
+     * method without explicit calls to this {@code shallowEquals} method for 
children.
      *
      * <p>This method can optionally excludes null values from the comparison. 
In metadata,
      * null value often means "don't know", so in some occasion we want to 
consider two
@@ -963,6 +971,8 @@ final class PropertyAccessor {
      * @param  mode      The strictness level of the comparison.
      * @param  skipNulls If {@code true}, only non-null values will be 
compared.
      * @throws BackingStoreException If the implementation threw a checked 
exception.
+     *
+     * @see MetadataStandard#shallowEquals(Object, Object, ComparisonMode, 
boolean)
      */
     public boolean shallowEquals(final Object metadata1, final Object 
metadata2,
             final ComparisonMode mode, final boolean skipNulls) throws 
BackingStoreException
@@ -1069,24 +1079,6 @@ final class PropertyAccessor {
     }
 
     /**
-     * Returns {@code true} if the metadata is modifiable. This method is not 
public because it
-     * uses heuristic rules. In case of doubt, this method conservatively 
returns {@code true}.
-     */
-    final boolean isModifiable() {
-        if (setters != null) {
-            return true;
-        }
-        for (int i=0; i<allCount; i++) {
-            // Immutable objects usually don't need to be cloned. So if
-            // an object is cloneable, it is probably not immutable.
-            if (Cloneable.class.isAssignableFrom(getters[i].getReturnType())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Returns a hash code for the specified metadata. 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

Modified: 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java?rev=1451957&r1=1451956&r2=1451957&view=diff
==============================================================================
--- 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java
 [UTF-8] Sat Mar  2 23:11:13 2013
@@ -55,6 +55,14 @@ final class StandardImplementation exten
 
     /**
      * Implementations for a given interface, computed when first needed then 
cached.
+     *
+     * {@note In the particular case of <code>Class</code> keys, 
<code>IdentityHashMap</code> and
+     *        <code>HashMap</code> have identical behavior since 
<code>Class</code> is final and
+     *        does not override the <code>equals(Object)</code> and 
<code>hashCode()</code> methods.
+     *        The <code>IdentityHashMap</code> Javadoc claims that it is 
faster than the regular
+     *        <code>HashMap</code>. But maybe the most interesting property is 
that it allocates
+     *        less objects since <code>IdentityHashMap</code> implementation 
doesn't need the chain
+     *        of objects created by <code>HashMap</code>.}
      */
     private final Map<Class<?>,Class<?>> implementations;
 

Modified: 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java?rev=1451957&r1=1451956&r2=1451957&view=diff
==============================================================================
--- 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
 [UTF-8] Sat Mar  2 23:11:13 2013
@@ -17,7 +17,7 @@
 package org.apache.sis.io;
 
 import java.util.Map;
-import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.util.Date;
@@ -95,7 +95,7 @@ public abstract class CompoundFormat<T> 
      * The formats for smaller unit of information.
      * Will be created only when first needed.
      */
-    private transient Map<Class<?>,Format> formats;
+    private transient Map<Class<?>, Format> formats;
 
     /**
      * Creates a new format for the given locale. The given locale can be 
{@code null} or
@@ -328,7 +328,7 @@ public abstract class CompoundFormat<T> 
             format = createFormat(type);
             if (format != null) {
                 if (formats == null) {
-                    this.formats = formats = new HashMap<>(4);
+                    this.formats = formats = new IdentityHashMap<>(4);
                 }
                 formats.put(type, format);
                 break;
@@ -399,7 +399,7 @@ public abstract class CompoundFormat<T> 
         @SuppressWarnings("unchecked")
         final CompoundFormat<T> clone = (CompoundFormat<T>) super.clone();
         if (clone.formats != null) {
-            clone.formats = new HashMap<>(clone.formats);
+            clone.formats = new IdentityHashMap<>(clone.formats);
             for (final Map.Entry<Class<?>,Format> entry : 
clone.formats.entrySet()) {
                 entry.setValue((Format) entry.getValue().clone());
             }

Modified: 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Numbers.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Numbers.java?rev=1451957&r1=1451956&r2=1451957&view=diff
==============================================================================
--- 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Numbers.java 
[UTF-8] (original)
+++ 
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Numbers.java 
[UTF-8] Sat Mar  2 23:11:13 2013
@@ -17,7 +17,7 @@
 package org.apache.sis.util;
 
 import java.util.Map;
-import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Queue;
 import java.util.Set;
@@ -51,8 +51,16 @@ public final class Numbers extends Stati
 
     /**
      * Mapping between a primitive type and its wrapper, if any.
+     *
+     * {@note In the particular case of <code>Class</code> keys, 
<code>IdentityHashMap</code> and
+     *        <code>HashMap</code> have identical behavior since 
<code>Class</code> is final and
+     *        does not override the <code>equals(Object)</code> and 
<code>hashCode()</code> methods.
+     *        The <code>IdentityHashMap</code> Javadoc claims that it is 
faster than the regular
+     *        <code>HashMap</code>. But maybe the most interesting property is 
that it allocates
+     *        less objects since <code>IdentityHashMap</code> implementation 
doesn't need the chain
+     *        of objects created by <code>HashMap</code>.}
      */
-    private static final Map<Class<?>,Numbers> MAPPING = new HashMap<>(16);
+    private static final Map<Class<?>,Numbers> MAPPING = new 
IdentityHashMap<>(16);
     static {
         new Numbers(BigDecimal.class, true, false, (byte) (DOUBLE+2)); // 
Undocumented enum.
         new Numbers(BigInteger.class, false, true, (byte) (DOUBLE+1)); // 
Undocumented enum.


Reply via email to