sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1627171 - in /sis/branches/JDK7: ./ core/sis-metadata/src/main/java/org/apache/sis/io/wkt/ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/ core/sis-metadata/src/t...
Date Tue, 23 Sep 2014 22:37:08 GMT
Author: desruisseaux
Date: Tue Sep 23 22:37:08 2014
New Revision: 1627171

URL: http://svn.apache.org/r1627171
Log:
Merge from the JDK8 branch.

Added:
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
      - copied unchanged from r1627164, sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/WKTFormatTest.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java   (with props)
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleExtent.java
      - copied unchanged from r1627164, sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleExtent.java
Modified:
    sis/branches/JDK7/   (props changed)
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultExtent.java
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/FormatterTest.java
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataStandardTest.java
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/mock/IdentifiedObjectMock.java

Propchange: sis/branches/JDK7/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK8:r1626813-1627164

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -40,6 +40,8 @@ import javax.measure.quantity.Quantity;
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.extent.VerticalExtent;
+import org.opengis.metadata.extent.TemporalExtent;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.parameter.GeneralParameterDescriptor;
 import org.opengis.referencing.IdentifiedObject;
@@ -63,6 +65,7 @@ import org.apache.sis.util.CharSequences
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Citations;
+import org.apache.sis.internal.simple.SimpleExtent;
 import org.apache.sis.internal.metadata.ReferencingServices;
 import org.apache.sis.measure.Range;
 import org.apache.sis.measure.MeasurementRange;
@@ -85,8 +88,8 @@ import org.apache.sis.metadata.iso.exten
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @since   0.5 (derived from geotk-2.0)
- * @version 0.4
+ * @since   0.4 (derived from geotk-2.0)
+ * @version 0.5
  * @module
  */
 public class Formatter implements Localized {
@@ -100,7 +103,13 @@ public class Formatter implements Locali
      * Maximal accuracy of vertical extents, in number of fraction digits.
      * The value used here is arbitrary and may change in any future SIS version.
      */
-    private static final int VERTICAL_ACCURACY = 3;
+    private static final int VERTICAL_ACCURACY = 9;
+
+    /**
+     * The time span threshold for switching between the {@code "yyyy-MM-dd'T'HH:mm:ss.SX"}
+     * and {@code "yyyy-MM-dd"} date pattern when formatting a temporal extent.
+     */
+    private static final long TEMPORAL_THRESHOLD = 24 * 60 * 60 * 1000L;
 
     /**
      * The value of {@code X364.FOREGROUND_DEFAULT.sequence()}, hard-coded for avoiding
@@ -725,32 +734,8 @@ public class Formatter implements Locali
         if (area != null) {
             appendOnNewLine("Area", area.getDescription(), ElementKind.EXTENT);
             append(Extents.getGeographicBoundingBox(area), BBOX_ACCURACY);
-            final MeasurementRange<Double> range = Extents.getVerticalRange(area);
-            if (range != null) {
-                openElement(true, "VerticalExtent");
-                setColor(ElementKind.EXTENT);
-                numberFormat.setMinimumFractionDigits(0);
-                numberFormat.setMaximumFractionDigits(VERTICAL_ACCURACY);
-                numberFormat.setRoundingMode(RoundingMode.FLOOR);
-                appendPreset(range.getMinDouble());
-                numberFormat.setRoundingMode(RoundingMode.CEILING);
-                appendPreset(range.getMaxDouble());
-                final Unit<?> unit = range.unit();
-                if (!convention.isSimplified() || !SI.METRE.equals(unit)) {
-                    append(unit); // Unit are optional if they are metres.
-                }
-                resetColor();
-                closeElement(true);
-            }
-            final Range<Date> timeRange = Extents.getTimeRange(area);
-            if (timeRange != null) {
-                openElement(true, "TimeExtent");
-                setColor(ElementKind.EXTENT);
-                append(timeRange.getMinValue());
-                append(timeRange.getMaxValue());
-                resetColor();
-                closeElement(true);
-            }
+            appendVerticalExtent(Extents.getVerticalRange(area));
+            appendTemporalExtent(Extents.getTimeRange(area));
         }
     }
 
@@ -786,6 +771,82 @@ public class Formatter implements Locali
     }
 
     /**
+     * Appends the given vertical extent, if non-null.
+     * This method chooses an accuracy from the vertical span.
+     * Examples:
+     *
+     * <ul>
+     *   <li>“{@code VerticalExtent[102, 108, LengthUnit["m", 1]]}”       (Δz =   6)</li>
+     *   <li>“{@code VerticalExtent[100.2, 100.8, LengthUnit["m", 1]]}”   (Δz = 0.6)</li>
+     * </ul>
+     */
+    private void appendVerticalExtent(final MeasurementRange<Double> range) {
+        if (range != null) {
+            final double min = range.getMinDouble();
+            final double max = range.getMaxDouble();
+            int minimumFractionDigits = Math.max(0, DecimalFunctions.fractionDigitsForDelta(max - min, false));
+            int maximumFractionDigits = minimumFractionDigits + 2; // Arbitrarily allow 2 more digits.
+            if (maximumFractionDigits > VERTICAL_ACCURACY) {
+                maximumFractionDigits = VERTICAL_ACCURACY;
+                minimumFractionDigits = 0;
+            }
+            openElement(true, "VerticalExtent");
+            setColor(ElementKind.EXTENT);
+            numberFormat.setMinimumFractionDigits(minimumFractionDigits);
+            numberFormat.setMaximumFractionDigits(maximumFractionDigits);
+            numberFormat.setRoundingMode(RoundingMode.FLOOR);   appendPreset(min);
+            numberFormat.setRoundingMode(RoundingMode.CEILING); appendPreset(max);
+            final Unit<?> unit = range.unit();
+            if (!convention.isSimplified() || !SI.METRE.equals(unit)) {
+                append(unit); // Unit are optional if they are metres.
+            }
+            resetColor();
+            closeElement(true);
+        }
+    }
+
+    /**
+     * Appends the given temporal extents, if non-null.
+     * This method uses a simplified format if the time span is large enough.
+     * Examples:
+     *
+     * <ul>
+     *   <li>“{@code TemporalExtent[1980-04-12, 1980-04-18]}” (Δt = 6 days)</li>
+     *   <li>“{@code TemporalExtent[1980-04-12T18:00:00.0Z, 1980-04-12T21:00:00.0Z]}” (Δt = 3 hours)</li>
+     * </ul>
+     */
+    private void appendTemporalExtent(final Range<Date> range) {
+        if (range != null) {
+            final Date min = range.getMinValue();
+            final Date max = range.getMaxValue();
+            if (min != null && max != null) {
+                String pattern = null;
+                if (dateFormat instanceof SimpleDateFormat && (max.getTime() - min.getTime()) >= TEMPORAL_THRESHOLD) {
+                    final String p = ((SimpleDateFormat) dateFormat).toPattern();
+                    if (p.length() > WKTFormat.SHORT_DATE_PATTERN.length() &&
+                        p.startsWith(WKTFormat.SHORT_DATE_PATTERN))
+                    {
+                        pattern = p;
+                        ((SimpleDateFormat) dateFormat).applyPattern(WKTFormat.SHORT_DATE_PATTERN);
+                    }
+                }
+                openElement(true, "TimeExtent");
+                setColor(ElementKind.EXTENT);
+                try {
+                    append(min);
+                    append(max);
+                } finally {
+                    if (pattern != null) {
+                        ((SimpleDateFormat) dateFormat).applyPattern(pattern);
+                    }
+                }
+                resetColor();
+                closeElement(true);
+            }
+        }
+    }
+
+    /**
      * Appends the given math transform, typically (but not necessarily) in a {@code PARAM_MT[…]} element.
      *
      * @param transform The transform object to append to the WKT, or {@code null} if none.
@@ -1167,11 +1228,17 @@ public class Formatter implements Locali
         } else if (value instanceof IdentifiedObject) {
             append(ReferencingServices.getInstance().toFormattableObject((IdentifiedObject) value));
         }
-        else if (value instanceof GeographicBoundingBox) append((GeographicBoundingBox) value, BBOX_ACCURACY);
         else if (value instanceof MathTransform)         append((MathTransform)         value);
         else if (value instanceof Matrix)                append((Matrix)                value);
         else if (value instanceof Unit<?>)               append((Unit<?>)               value);
-        else return false;
+        else if (value instanceof GeographicBoundingBox) append((GeographicBoundingBox) value, BBOX_ACCURACY);
+        else if (value instanceof VerticalExtent) {
+            appendVerticalExtent(Extents.getVerticalRange(new SimpleExtent(null, (VerticalExtent) value, null)));
+        } else if (value instanceof TemporalExtent) {
+            appendTemporalExtent(Extents.getTimeRange(new SimpleExtent(null, null, (TemporalExtent) value)));
+        } else {
+            return false;
+        }
         return true;
     }
 

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -106,6 +106,15 @@ public class WKTFormat extends CompoundF
     static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SX";
 
     /**
+     * Short version of {@link #DATE_PATTERN}, to be used when formatting temporal extents
+     * if the duration is at least {@link Formatter#TEMPORAL_THRESHOLD}. This pattern must
+     * be a prefix of {@link #DATE_PATTERN}, since we will use that condition for deciding
+     * if this pattern is really shorter (the user could have created his own date format
+     * with a different pattern).
+     */
+    static final String SHORT_DATE_PATTERN = "yyyy-MM-dd";
+
+    /**
      * The symbols to use for this formatter.
      * The same object is also referenced in the {@linkplain #parser} and {@linkplain #formatter}.
      * It appears here for serialization purpose.
@@ -389,9 +398,12 @@ public class WKTFormat extends CompoundF
     /**
      * Formats the specified object as a Well Know Text. The formatter accepts at least the following types:
      * {@link FormattableObject}, {@link IdentifiedObject},
-     * {@link org.opengis.metadata.extent.GeographicBoundingBox},
      * {@link org.opengis.referencing.operation.MathTransform},
-     * {@link org.opengis.referencing.operation.Matrix} and {@link Unit}.
+     * {@link org.opengis.referencing.operation.Matrix}
+     * {@link org.opengis.metadata.extent.GeographicBoundingBox},
+     * {@link org.opengis.metadata.extent.VerticalExtent},
+     * {@link org.opengis.metadata.extent.TemporalExtent}
+     * and {@link Unit}.
      *
      * @param  object     The object to format.
      * @param  toAppendTo Where the text is to be appended.

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -279,24 +279,7 @@ public abstract class AbstractMetadata i
      */
     @Override
     public boolean equals(final Object object, final ComparisonMode mode) {
-        if (object == this) {
-            return true;
-        }
-        if (object == null) {
-            return false;
-        }
-        if (mode == ComparisonMode.STRICT) {
-            if (object.getClass() != getClass()) {
-                return false;
-            }
-        }
-        final MetadataStandard standard = getStandard();
-        if (mode != ComparisonMode.STRICT) {
-            if (!getInterface().isInstance(object)) {
-                return false;
-            }
-        }
-        return standard.equals(this, object, mode);
+        return getStandard().equals(this, object, mode);
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -18,10 +18,11 @@ package org.apache.sis.metadata;
 
 import java.util.Set;
 import java.util.Map;
-import java.util.IdentityHashMap;
 import java.util.LinkedHashSet;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.io.IOException;
 import java.io.Serializable;
 import java.io.ObjectInputStream;
@@ -40,6 +41,11 @@ import org.apache.sis.internal.system.Sy
 import org.apache.sis.internal.simple.SimpleCitation;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+import static org.apache.sis.util.ArgumentChecks.ensureNonNullElement;
+
+// Branch-specific imports
+import org.apache.sis.internal.jdk8.JDK8;
+import org.apache.sis.internal.jdk8.BiFunction;
 
 
 /**
@@ -136,10 +142,10 @@ public class MetadataStandard implements
         final String[] acronyms = {"CoordinateSystem", "CS", "CoordinateReferenceSystem", "CRS"};
 
         // If new StandardImplementation instances are added below, please update StandardImplementation.readResolve().
-        ISO_19111 = new StandardImplementation("ISO 19111", "org.opengis.referencing.", "org.apache.sis.referencing.", acronyms);
-        ISO_19115 = new StandardImplementation("ISO 19115", "org.opengis.metadata.", "org.apache.sis.metadata.iso.", null);
-        ISO_19119 = new MetadataStandard      ("ISO 19119", "org.opengis.service.");
-        ISO_19123 = new MetadataStandard      ("ISO 19123", "org.opengis.coverage.");
+        ISO_19115 = new StandardImplementation("ISO 19115", "org.opengis.metadata.", "org.apache.sis.metadata.iso.", null, null);
+        ISO_19111 = new StandardImplementation("ISO 19111", "org.opengis.referencing.", "org.apache.sis.referencing.", acronyms, new MetadataStandard[] {ISO_19115});
+        ISO_19119 = new MetadataStandard      ("ISO 19119", "org.opengis.service.", ISO_19111.dependencies);
+        ISO_19123 = new MetadataStandard      ("ISO 19123", "org.opengis.coverage.", new MetadataStandard[] {ISO_19111});
         INSTANCES = new MetadataStandard[] {
             ISO_19111,
             ISO_19115,
@@ -161,37 +167,54 @@ public class MetadataStandard implements
     final Citation citation;
 
     /**
-     * The root packages for metadata interfaces. Must have a trailing {@code '.'}.
+     * The root package for metadata interfaces. Must have a trailing {@code '.'}.
      */
     final String interfacePackage;
 
     /**
+     * The dependencies, or {@code null} if none.
+     *
+     * Note: the {@code null} value is for serialization compatibility.
+     */
+    private final MetadataStandard[] dependencies;
+
+    /**
      * Accessors for the specified implementations.
      * The only legal value types are:
      *
      * <ul>
-     *   <li>{@link Class} if we have determined the standard interface for a given type
-     *       but did not yet created the {@link PropertyAccessor} for it.</li>
+     *   <li>{@link MetadataStandard} if type is handled by {@linkplain #dependencies} rather than this standard.</li>
+     *   <li>{@link Class} if we found the interface for the type but did not yet created the {@link PropertyAccessor}.</li>
      *   <li>{@link PropertyAccessor} otherwise.</li>
      * </ul>
      */
-    private final transient Map<Class<?>, Object> accessors; // written by reflection on deserialization.
+    private final transient ConcurrentMap<Class<?>, Object> accessors; // written by reflection on deserialization.
 
     /**
      * Creates a new instance working on implementation of interfaces defined in the specified package.
      *
-     * <p><b>Example:</b>: For the ISO 19115 standard reflected by GeoAPI interfaces,
-     * {@code interfacePackage} shall be the {@link org.opengis.metadata} package.</p>
+     * <div class="note"><b>Example:</b>: For the ISO 19115 standard reflected by GeoAPI interfaces,
+     * {@code interfacePackage} shall be the {@link org.opengis.metadata} package.</div>
      *
      * @param citation         Bibliographical reference to the international standard.
      * @param interfacePackage The root package for metadata interfaces.
+     * @param dependencies     The dependencies to other metadata standards.
      */
-    public MetadataStandard(final Citation citation, final Package interfacePackage) {
-        ensureNonNull("citation", citation);
+    public MetadataStandard(final Citation citation, final Package interfacePackage, MetadataStandard... dependencies) {
+        ensureNonNull("citation",         citation);
         ensureNonNull("interfacePackage", interfacePackage);
+        ensureNonNull("dependencies",     dependencies);
         this.citation         = citation;
         this.interfacePackage = interfacePackage.getName() + '.';
-        this.accessors        = new IdentityHashMap<>(); // Also defined in readObject(…)
+        this.accessors        = new ConcurrentHashMap<>(); // Also defined in readObject(…)
+        if (dependencies.length == 0) {
+            this.dependencies = null;
+        } else {
+            this.dependencies = dependencies = dependencies.clone();
+            for (int i=0; i<dependencies.length; i++) {
+                ensureNonNullElement("dependencies", i, dependencies[i]);
+            }
+        }
     }
 
     /**
@@ -200,11 +223,25 @@ public class MetadataStandard implements
      *
      * @param citation         Bibliographical reference to the international standard.
      * @param interfacePackage The root package for metadata interfaces.
+     * @param dependencies     The dependencies to other metadata standards, or {@code null} if none.
      */
-    MetadataStandard(final String citation, final String interfacePackage) {
+    MetadataStandard(final String citation, final String interfacePackage, final MetadataStandard[] dependencies) {
         this.citation         = new SimpleCitation(citation);
         this.interfacePackage = interfacePackage;
-        this.accessors        = new IdentityHashMap<>();
+        this.accessors        = new ConcurrentHashMap<>();
+        this.dependencies     = dependencies; // No clone, since this constructor is for internal use only.
+    }
+
+    /**
+     * Returns {@code true} if class or interface of the given name is supported by this standard.
+     * This method verifies if the class is a member of the package given at construction time or
+     * a sub-package. This method does not verify if the type is supported by a dependency.
+     *
+     * @param  classname The name of the type to verify.
+     * @return {@code true} if the given type is supported by this standard.
+     */
+    final boolean isSupported(final String classname) {
+        return classname.startsWith(interfacePackage);
     }
 
     /**
@@ -220,16 +257,16 @@ public class MetadataStandard implements
      * @return The metadata standard for the given type, or {@code null} if not found.
      */
     public static MetadataStandard forClass(final Class<?> type) {
-        String name = type.getName();
+        String classname = type.getName();
         for (final MetadataStandard candidate : INSTANCES) {
-            if (name.startsWith(candidate.interfacePackage)) {
+            if (candidate.isSupported(classname)) {
                 return candidate;
             }
         }
         for (final Class<?> interf : Classes.getAllInterfaces(type)) {
-            name = interf.getName();
+            classname = interf.getName();
             for (final MetadataStandard candidate : INSTANCES) {
-                if (name.startsWith(candidate.interfacePackage)) {
+                if (candidate.isSupported(classname)) {
                     return candidate;
                 }
             }
@@ -243,9 +280,7 @@ public class MetadataStandard implements
      */
     static void clearCache() {
         for (final MetadataStandard standard : INSTANCES) {
-            synchronized (standard.accessors) {
-                standard.accessors.clear();
-            }
+            standard.accessors.clear();
         }
     }
 
@@ -279,48 +314,77 @@ public class MetadataStandard implements
      *         of the expected package and {@code mandatory} is {@code true}.
      */
     final PropertyAccessor getAccessor(final Class<?> implementation, final boolean mandatory) {
-        synchronized (accessors) {
-            // Check for previously created accessors.
-            final Object value = accessors.get(implementation);
-            if (value instanceof PropertyAccessor) {
-                return (PropertyAccessor) value;
-            }
-            // Check if we started some computation that we can finish.
-            final Class<?> type;
-            if (value != null) {
-                type = (Class<?>) value;
-            } else {
-                // Nothing were computed. Try to compute now.
-                type = findInterface(implementation);
-                if (type == null) {
-                    if (mandatory) {
-                        throw new ClassCastException(Errors.format(Errors.Keys.UnknownType_1, implementation));
+        /*
+         * Check for accessors created by previous call to this method.
+         * Values are added to this cache but never cleared.
+         */
+        final Object value = accessors.get(implementation);
+        if (value instanceof PropertyAccessor) {
+            return (PropertyAccessor) value;
+        }
+        /*
+         * Check if we started some computation that we can finish. A partial computation exists
+         * when we already found the Class<?> for the interface, but didn't created the accessor.
+         */
+        final Class<?> type;
+        if (value instanceof Class<?>) {
+            type = (Class<?>) value; // Stored result of previous call to findInterface(…).
+            assert type == findInterface(implementation) : implementation;
+        } else {
+            /*
+             * Nothing was computed, we need to start from scratch. The first step is to find
+             * the interface implemented by the given class. If we can not find an interface,
+             * we will delegate to the dependencies and store the result for avoiding redoing
+             * this search next time.
+             */
+            type = findInterface(implementation);
+            if (type == null) {
+                if (dependencies != null) {
+                    for (final MetadataStandard dependency : dependencies) {
+                        final PropertyAccessor accessor = dependency.getAccessor(implementation, false);
+                        if (accessor != null) {
+                            accessors.put(implementation, accessor); // Ok to overwrite existing instance here.
+                            return accessor;
+                        }
                     }
-                    return null;
                 }
+                if (mandatory) {
+                    throw new ClassCastException(Errors.format(Errors.Keys.UnknownType_1, implementation));
+                }
+                return null;
             }
-            final PropertyAccessor accessor;
-            if (SpecialCases.isSpecialCase(type)) {
-                accessor = new SpecialCases(citation, type, implementation);
-            } else {
-                /*
-                 * If "multi-value returns" was allowed in the Java language, the 'onlyUML' boolean would
-                 * be returned by 'findInterface(Class)' method when it falls in the special case for the
-                 * UML annotation on implementation class. But since we do not have multi-values, we have
-                 * to infer it from our knownledge of how 'findInterface(Class)' is implemented.
-                 */
-                final boolean onlyUML = (type == implementation && !type.isInterface());
-                accessor = new PropertyAccessor(citation, type, implementation, onlyUML);
-            }
-            accessors.put(implementation, accessor);
-            return accessor;
         }
+        /*
+         * Found the interface for which to create an accessor. Creates the accessor now, unless an accessor
+         * has been created concurrently in another thread in which case the later will be returned.
+         */
+        return (PropertyAccessor) JDK8.compute(accessors, implementation, new BiFunction<Class<?>, Object, Object>() {
+            @Override public Object apply(final Class<?> k, final Object v) {
+                if (v instanceof PropertyAccessor) {
+                    return v;
+                }
+                final PropertyAccessor accessor;
+                if (SpecialCases.isSpecialCase(type)) {
+                    accessor = new SpecialCases(citation, type, implementation);
+                } else {
+                    /*
+                     * If "multi-value returns" was allowed in the Java language, the 'onlyUML' boolean would
+                     * be returned by 'findInterface(Class)' method when it falls in the special case for the
+                     * UML annotation on implementation class. But since we do not have multi-values, we have
+                     * to infer it from our knownledge of how 'findInterface(Class)' is implemented.
+                     */
+                    final boolean onlyUML = (type == implementation && !type.isInterface());
+                    accessor = new PropertyAccessor(citation, type, implementation, onlyUML);
+                }
+                return accessor;
+            }
+        });
     }
 
     /**
-     * Returns {@code true} if the given type is assignable to a type from this standard.
-     * If this method returns {@code true}, then invoking {@link #getInterface(Class)} is
-     * guaranteed to succeed without throwing an exception.
+     * Returns {@code true} if the given type is assignable to a type from this standard or one of its dependencies.
+     * If this method returns {@code true}, then invoking {@link #getInterface(Class)} is guaranteed to succeed
+     * without throwing an exception.
      *
      * @param  type The implementation class (can be {@code null}).
      * @return {@code true} if the given class is an interface of this standard,
@@ -328,16 +392,27 @@ public class MetadataStandard implements
      */
     public boolean isMetadata(final Class<?> type) {
         if (type != null) {
-            synchronized (accessors) {
-                if (accessors.containsKey(type)) {
-                    return true;
-                }
-                final Class<?> standardType = findInterface(type);
-                if (standardType != null) {
-                    accessors.put(type, standardType);
-                    return true;
+            if (accessors.containsKey(type)) {
+                return true;
+            }
+            if (dependencies != null) {
+                for (final MetadataStandard dependency : dependencies) {
+                    if (dependency.isMetadata(type)) {
+                        accessors.putIfAbsent(type, dependency);
+                        return true;
+                    }
                 }
             }
+            /*
+             * At this point, all cached values (including those in dependencies) have been checked.
+             * Performs the 'findInterface' computation only in last resort. Current implementation
+             * does not store negative results in order to avoid filling the cache with unrelated classes.
+             */
+            final Class<?> standardType = findInterface(type);
+            if (standardType != null) {
+                accessors.putIfAbsent(type, standardType);
+                return true;
+            }
         }
         return false;
     }
@@ -359,13 +434,15 @@ public class MetadataStandard implements
      * Only one metadata interface can be implemented. If the given type is already
      * an interface from the standard, then it is returned directly.
      *
+     * <p>This method ignores dependencies. Fallback on metadata standard dependencies shall be done by the caller.</p>
+     *
      * @param  type The standard interface or the implementation class.
      * @return The single interface, or {@code null} if none where found.
      */
     private Class<?> findInterface(final Class<?> type) {
         if (type != null) {
             if (type.isInterface()) {
-                if (type.getName().startsWith(interfacePackage)) {
+                if (isSupported(type.getName())) {
                     return type;
                 }
             } else {
@@ -423,7 +500,7 @@ public class MetadataStandard implements
      */
     private void getInterfaces(final Class<?> type, final Collection<Class<?>> interfaces) {
         for (final Class<?> candidate : type.getInterfaces()) {
-            if (candidate.getName().startsWith(interfacePackage)) {
+            if (isSupported(candidate.getName())) {
                 interfaces.add(candidate);
             }
             getInterfaces(candidate, interfaces);
@@ -452,25 +529,32 @@ public class MetadataStandard implements
     @SuppressWarnings("unchecked")
     public <T> Class<? super T> getInterface(final Class<T> type) throws ClassCastException {
         ensureNonNull("type", type);
-        final Class<?> standard;
-        synchronized (accessors) {
-            final Object value = accessors.get(type);
-            if (value != null) {
-                if (value instanceof PropertyAccessor) {
-                    standard = ((PropertyAccessor) value).type;
-                } else {
-                    standard = (Class<?>) value;
-                }
+        final Class<?> interf;
+        final Object value = accessors.get(type);
+        if (value instanceof PropertyAccessor) {
+            interf = ((PropertyAccessor) value).type;
+        } else if (value instanceof Class<?>) {
+            interf = (Class<?>) value;
+        } else if (value instanceof MetadataStandard) {
+            interf = ((MetadataStandard) value).getInterface(type);
+        } else {
+            interf = findInterface(type);
+            if (interf != null) {
+                accessors.putIfAbsent(type, interf);
             } else {
-                standard = findInterface(type);
-                if (standard == null) {
-                    throw new ClassCastException(Errors.format(Errors.Keys.UnknownType_1, type));
+                if (dependencies != null) {
+                    for (final MetadataStandard dependency : dependencies) {
+                        if (dependency.isMetadata(type)) {
+                            accessors.putIfAbsent(type, dependency);
+                            return dependency.getInterface(type);
+                        }
+                    }
                 }
-                accessors.put(type, standard);
+                throw new ClassCastException(Errors.format(Errors.Keys.UnknownType_1, type));
             }
         }
-        assert standard.isAssignableFrom(type) : type;
-        return (Class<? super T>) standard;
+        assert interf.isAssignableFrom(type) : type;
+        return (Class<? super T>) interf;
     }
 
     /**
@@ -793,8 +877,17 @@ public class MetadataStandard implements
         if (metadata1 == null || metadata2 == null) {
             return false;
         }
-        final PropertyAccessor accessor = getAccessor(metadata1.getClass(), true);
-        if (accessor.type != findInterface(metadata2.getClass())) {
+        final Class<?> type1 = metadata1.getClass();
+        final Class<?> type2 = metadata2.getClass();
+        if (type1 != type2 && mode == ComparisonMode.STRICT) {
+            return false;
+        }
+        final PropertyAccessor accessor = getAccessor(type1, true);
+        if (type1 != type2 && (!accessor.type.isAssignableFrom(type2) || accessor.type != getAccessor(type2, false).type)) {
+            /*
+             * Note: the check for (accessor.type != getAccessor(…).type) would have been enough, but checking
+             * for isAssignableFrom(…) first can avoid the (relatively costly) creation of new PropertyAccessor.
+             */
             return false;
         }
         /*
@@ -867,7 +960,7 @@ public class MetadataStandard implements
         try {
             final Field field = classe.getDeclaredField(name);
             field.setAccessible(true);
-            field.set(this, new IdentityHashMap<>());
+            field.set(this, new ConcurrentHashMap<>());
         } catch (ReflectiveOperationException e) {
             throw new AssertionError(e); // Should never happen (tested by MetadataStandardTest).
         }

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/StandardImplementation.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -74,11 +74,12 @@ final class StandardImplementation exten
      * @param interfacePackage      The root package for metadata interfaces, with a trailing {@code '.'}.
      * @param implementationPackage The root package for metadata implementations. with a trailing {@code '.'}.
      * @param acronyms              An array of (full text, acronyms) pairs. This array is not cloned.
+     * @param dependencies          The dependencies to other metadata standards, or {@code null} if none.
      */
-    StandardImplementation(final String citation, final String interfacePackage,
-            final String implementationPackage, final String[] acronyms)
+    StandardImplementation(final String citation, final String interfacePackage, final String implementationPackage,
+            final String[] acronyms, final MetadataStandard[] dependencies)
     {
-        super(citation, interfacePackage);
+        super(citation, interfacePackage, dependencies);
         this.implementationPackage = implementationPackage;
         this.acronyms              = acronyms;
         this.implementations       = new IdentityHashMap<>();
@@ -120,8 +121,8 @@ final class StandardImplementation exten
          * CodeLists, Enums and Exceptions.
          */
         if (type != null && type.isInterface()) {
-            String name = type.getName();
-            if (name.startsWith(interfacePackage)) {
+            String classname = type.getName();
+            if (isSupported(classname)) {
                 synchronized (implementations) {
                     Class<?> candidate = implementations.get(type);
                     if (candidate != null) {
@@ -133,7 +134,7 @@ final class StandardImplementation exten
                      * have been replaced by their acronym (if any).
                      */
                     final StringBuilder buffer = new StringBuilder(implementationPackage)
-                            .append(name, interfacePackage.length(), name.length());
+                            .append(classname, interfacePackage.length(), classname.length());
                     if (acronyms != null) {
                         for (int i=0; i<acronyms.length; i+=2) {
                             final String acronym = acronyms[i];
@@ -149,9 +150,9 @@ final class StandardImplementation exten
                      */
                     final int prefixPosition = buffer.lastIndexOf(".") + 1;
                     buffer.insert(prefixPosition, isAbstract(type) ? "Abstract" : "Default");
-                    name = buffer.toString();
+                    classname = buffer.toString();
                     try {
-                        candidate = Class.forName(name);
+                        candidate = Class.forName(classname);
                         implementations.put(type, candidate);
                         return candidate.asSubclass(type);
                     } catch (ClassNotFoundException e) {

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultExtent.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultExtent.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultExtent.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultExtent.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -87,19 +87,19 @@ public class DefaultExtent extends ISOMe
     private InternationalString description;
 
     /**
-     * Provides geographic component of the extent of the referring object
+     * Provides geographic component of the extent of the referring object.
      */
     private Collection<GeographicExtent> geographicElements;
 
     /**
-     * Provides temporal component of the extent of the referring object
+     * Provides vertical component of the extent of the referring object.
      */
-    private Collection<TemporalExtent> temporalElements;
+    private Collection<VerticalExtent> verticalElements;
 
     /**
-     * Provides vertical component of the extent of the referring object
+     * Provides temporal component of the extent of the referring object.
      */
-    private Collection<VerticalExtent> verticalElements;
+    private Collection<TemporalExtent> temporalElements;
 
     /**
      * Constructs an initially empty extent.
@@ -215,43 +215,43 @@ public class DefaultExtent extends ISOMe
     }
 
     /**
-     * Provides temporal component of the extent of the referring object.
+     * Provides vertical component of the extent of the referring object.
      *
-     * @return The temporal extent, or an empty set if none.
+     * @return The vertical extent, or an empty set if none.
      */
     @Override
-    @XmlElement(name = "temporalElement")
-    public Collection<TemporalExtent> getTemporalElements() {
-        return temporalElements = nonNullCollection(temporalElements, TemporalExtent.class);
+    @XmlElement(name = "verticalElement")
+    public Collection<VerticalExtent> getVerticalElements() {
+        return verticalElements = nonNullCollection(verticalElements, VerticalExtent.class);
     }
 
     /**
-     * Sets temporal component of the extent of the referring object.
+     * Sets vertical component of the extent of the referring object.
      *
-     * @param newValues The new temporal elements.
+     * @param newValues The new vertical elements.
      */
-    public void setTemporalElements(final Collection<? extends TemporalExtent> newValues) {
-        temporalElements = writeCollection(newValues, temporalElements, TemporalExtent.class);
+    public void setVerticalElements(final Collection<? extends VerticalExtent> newValues) {
+        verticalElements = writeCollection(newValues, verticalElements, VerticalExtent.class);
     }
 
     /**
-     * Provides vertical component of the extent of the referring object.
+     * Provides temporal component of the extent of the referring object.
      *
-     * @return The vertical extent, or an empty set if none.
+     * @return The temporal extent, or an empty set if none.
      */
     @Override
-    @XmlElement(name = "verticalElement")
-    public Collection<VerticalExtent> getVerticalElements() {
-        return verticalElements = nonNullCollection(verticalElements, VerticalExtent.class);
+    @XmlElement(name = "temporalElement")
+    public Collection<TemporalExtent> getTemporalElements() {
+        return temporalElements = nonNullCollection(temporalElements, TemporalExtent.class);
     }
 
     /**
-     * Sets vertical component of the extent of the referring object.
+     * Sets temporal component of the extent of the referring object.
      *
-     * @param newValues The new vertical elements.
+     * @param newValues The new temporal elements.
      */
-    public void setVerticalElements(final Collection<? extends VerticalExtent> newValues) {
-        verticalElements = writeCollection(newValues, verticalElements, VerticalExtent.class);
+    public void setTemporalElements(final Collection<? extends TemporalExtent> newValues) {
+        temporalElements = writeCollection(newValues, temporalElements, TemporalExtent.class);
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/FormatterTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/FormatterTest.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/FormatterTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/FormatterTest.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -23,9 +23,11 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
 import org.apache.sis.measure.Units;
 import org.apache.sis.internal.util.X364;
 import org.apache.sis.test.mock.MatrixMock;
+import org.apache.sis.test.mock.VerticalCRSMock;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -38,7 +40,7 @@ import static org.apache.sis.test.Metada
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.4
+ * @version 0.5
  * @module
  */
 @DependsOn({ConventionTest.class, SymbolsTest.class, ColorsTest.class})
@@ -64,6 +66,19 @@ public final strictfp class FormatterTes
     }
 
     /**
+     * Tests (indirectly) formatting of a vertical extent.
+     */
+    @Test
+    public void testAppendVerticalExtent() {
+        final DefaultVerticalExtent extent = new DefaultVerticalExtent(102, 108, VerticalCRSMock.HEIGHT_ft);
+        assertWktEquals(Convention.WKT2, "VerticalExtent[102, 108, LengthUnit[“ft”, 0.3048]]", extent);
+
+        extent.setMinimumValue(100.2);
+        extent.setMaximumValue(100.8);
+        assertWktEquals(Convention.WKT2, "VerticalExtent[100.2, 100.8, LengthUnit[“ft”, 0.3048]]", extent);
+    }
+
+    /**
      * Tests (indirectly) {@link Formatter#append(Matrix)}.
      */
     @Test

Modified: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataStandardTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataStandardTest.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataStandardTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/MetadataStandardTest.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -23,12 +23,15 @@ import java.util.HashSet;
 import java.util.Collection;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.quality.Completeness;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.coverage.grid.RectifiedGrid;
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.apache.sis.metadata.iso.citation.HardCodedCitations;
 import org.apache.sis.metadata.iso.acquisition.DefaultPlatform;
 import org.apache.sis.metadata.iso.acquisition.DefaultInstrument;
 import org.apache.sis.metadata.iso.quality.AbstractCompleteness;
+import org.apache.sis.internal.simple.SimpleIdentifiedObject;
 import org.apache.sis.util.iso.SimpleInternationalString;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.test.DependsOnMethod;
@@ -67,13 +70,79 @@ import static org.apache.sis.test.TestUt
     ValueMapTest.class})
 public final strictfp class MetadataStandardTest extends TestCase {
     /**
+     * Tests {@link MetadataStandard#isMetadata(Class)}.
+     */
+    @Test
+    public void testIsMetadata() {
+        MetadataStandard std = MetadataStandard.ISO_19115;
+        assertFalse("isMetadata(String)",                 std.isMetadata(String.class));
+        assertTrue ("isMetadata(Citation)",               std.isMetadata(Citation.class));
+        assertTrue ("isMetadata(DefaultCitation)",        std.isMetadata(DefaultCitation.class));
+        assertFalse("isMetadata(IdentifiedObject)",       std.isMetadata(IdentifiedObject.class));
+        assertFalse("isMetadata(SimpleIdentifiedObject)", std.isMetadata(SimpleIdentifiedObject.class));
+        assertFalse("isMetadata(GeographicCRS)",          std.isMetadata(GeographicCRS.class));
+        assertFalse("isMetadata(RectifiedGrid)",          std.isMetadata(RectifiedGrid.class));
+
+        std = MetadataStandard.ISO_19111;
+        assertFalse("isMetadata(String)",                 std.isMetadata(String.class));
+        assertTrue ("isMetadata(Citation)",               std.isMetadata(Citation.class));          // Dependency
+        assertTrue ("isMetadata(DefaultCitation)",        std.isMetadata(DefaultCitation.class));   // Dependency
+        assertTrue ("isMetadata(IdentifiedObject)",       std.isMetadata(IdentifiedObject.class));
+        assertTrue ("isMetadata(SimpleIdentifiedObject)", std.isMetadata(SimpleIdentifiedObject.class));
+        assertTrue ("isMetadata(GeographicCRS)",          std.isMetadata(GeographicCRS.class));
+        assertFalse("isMetadata(RectifiedGrid)",          std.isMetadata(RectifiedGrid.class));
+
+        std = MetadataStandard.ISO_19123;
+        assertFalse("isMetadata(String)",                 std.isMetadata(String.class));
+        assertTrue ("isMetadata(Citation)",               std.isMetadata(Citation.class));               // Transitive dependency
+        assertTrue ("isMetadata(DefaultCitation)",        std.isMetadata(DefaultCitation.class));        // Transivive dependency
+        assertTrue ("isMetadata(IdentifiedObject)",       std.isMetadata(IdentifiedObject.class));       // Dependency
+        assertTrue ("isMetadata(SimpleIdentifiedObject)", std.isMetadata(SimpleIdentifiedObject.class)); // Dependency
+        assertTrue ("isMetadata(GeographicCRS)",          std.isMetadata(GeographicCRS.class));          // Dependency
+        assertTrue ("isMetadata(RectifiedGrid)",          std.isMetadata(RectifiedGrid.class));
+    }
+
+    /**
      * Tests {@link MetadataStandard#getInterface(Class)}.
      */
     @Test
+    @DependsOnMethod("testIsMetadata")
     public void testGetInterface() {
-        final MetadataStandard std = MetadataStandard.ISO_19115;
-        assertEquals(Citation.class,     std.getInterface(DefaultCitation.class));
-        assertEquals(Completeness.class, std.getInterface(AbstractCompleteness.class));
+        MetadataStandard std = MetadataStandard.ISO_19115;
+        assertEquals("getInterface(Citation)",             Citation.class,     std.getInterface(Citation.class));
+        assertEquals("getInterface(DefaultCitation)",      Citation.class,     std.getInterface(DefaultCitation.class));
+        assertEquals("getInterface(AbstractCompleteness)", Completeness.class, std.getInterface(AbstractCompleteness.class));
+
+        std = MetadataStandard.ISO_19111;
+        assertEquals("getInterface(Citation)",               Citation.class,         std.getInterface(Citation.class));
+        assertEquals("getInterface(DefaultCitation)",        Citation.class,         std.getInterface(DefaultCitation.class));
+        assertEquals("getInterface(AbstractCompleteness)",   Completeness.class,     std.getInterface(AbstractCompleteness.class));
+        assertEquals("getInterface(IdentifiedObject)",       IdentifiedObject.class, std.getInterface(IdentifiedObject.class));
+        assertEquals("getInterface(SimpleIdentifiedObject)", IdentifiedObject.class, std.getInterface(SimpleIdentifiedObject.class));
+        assertEquals("getInterface(GeographicCRS)",          GeographicCRS.class,    std.getInterface(GeographicCRS.class));
+
+        // Verify that the cache has not been updated in inconsistent way.
+        testIsMetadata();
+    }
+
+    /**
+     * Tests {@link MetadataStandard#getAccessor(Class, boolean)}.
+     */
+    @Test
+    @DependsOnMethod("testGetInterface")
+    public void testGetAccessor() {
+        MetadataStandard std = MetadataStandard.ISO_19115;
+        assertEquals("getAccessor(DefaultCitation)",      Citation.class,     std.getAccessor(DefaultCitation.class, true).type);
+        assertEquals("getAccessor(AbstractCompleteness)", Completeness.class, std.getAccessor(AbstractCompleteness.class, true).type);
+        assertNull  ("getAccessor(SimpleIdentifiedObject)",                   std.getAccessor(SimpleIdentifiedObject.class, false));
+
+        std = MetadataStandard.ISO_19111;
+        assertEquals("getAccessor(DefaultCitation)",        Citation.class,         std.getAccessor(DefaultCitation.class, true).type);
+        assertEquals("getAccessor(AbstractCompleteness)",   Completeness.class,     std.getAccessor(AbstractCompleteness.class, true).type);
+        assertEquals("getAccessor(SimpleIdentifiedObject)", IdentifiedObject.class, std.getAccessor(SimpleIdentifiedObject.class, true).type);
+
+        // Verify that the cache has not been updated in inconsistent way.
+        testGetInterface();
     }
 
     /**
@@ -81,8 +150,9 @@ public final strictfp class MetadataStan
      * A {@link ClassCastException} is expected.
      */
     @Test
+    @DependsOnMethod("testGetInterface")
     public void testGetWrongInterface() {
-        final MetadataStandard std = new MetadataStandard("SIS", "org.apache.sis.dummy.");
+        final MetadataStandard std = new MetadataStandard("SIS", "org.apache.sis.dummy.", null);
         try {
             std.getInterface(DefaultCitation.class);
             fail("No dummy interface expected.");
@@ -96,6 +166,7 @@ public final strictfp class MetadataStan
      * Tests the {@link MetadataStandard#equals(Object, Object, ComparisonMode)} method.
      */
     @Test
+    @DependsOnMethod("testGetAccessor")
     public void testEquals() {
         final MetadataStandard std = MetadataStandard.ISO_19115;
 
@@ -150,6 +221,7 @@ public final strictfp class MetadataStan
      * {@code MetadataStandard} methods depend on it ({@code equals}, {@code hashCode}, {@code prune}, <i>etc.</i>).
      */
     @Test
+    @DependsOnMethod("testGetAccessor")
     public void testValueMap() {
         final DefaultCitation instance = new DefaultCitation(HardCodedCitations.EPSG);
         final Map<String,Object> map = MetadataStandard.ISO_19115.asValueMap(instance,
@@ -224,10 +296,12 @@ public final strictfp class MetadataStan
      * be accessible even if there is no implementation on the classpath.
      */
     @Test
+    @DependsOnMethod("testGetAccessor")
     public void testWithoutImplementation() {
         final MetadataStandard std = MetadataStandard.ISO_19123;
-        assertFalse("isMetadata(Citation)",        std.isMetadata(Citation.class));
-        assertFalse("isMetadata(DefaultCitation)", std.isMetadata(DefaultCitation.class));
+        assertFalse("isMetadata(String)",          std.isMetadata(String.class));
+        assertTrue ("isMetadata(Citation)",        std.isMetadata(Citation.class));         // Transitive dependency
+        assertTrue ("isMetadata(DefaultCitation)", std.isMetadata(DefaultCitation.class));  // Transitive dependency
         assertTrue ("isMetadata(RectifiedGrid)",   std.isMetadata(RectifiedGrid.class));
         /*
          * Ensure that the getters have been found.

Modified: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -89,7 +89,8 @@ import org.junit.BeforeClass;
     org.apache.sis.io.wkt.ConventionTest.class,
     org.apache.sis.io.wkt.SymbolsTest.class,
     org.apache.sis.io.wkt.ColorsTest.class,
-    org.apache.sis.io.wkt.FormatterTest.class
+    org.apache.sis.io.wkt.FormatterTest.class,
+    org.apache.sis.io.wkt.WKTFormatTest.class
 })
 public final strictfp class MetadataTestSuite extends TestSuite {
     /**

Added: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java?rev=1627171&view=auto
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java (added)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -0,0 +1,36 @@
+/*
+ * 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.internal.jdk8;
+
+
+/**
+ * Placeholder for the {@link java.util.function.BiFunction} interface.
+ *
+ * @param <T> The type of first parameter  (source type 1).
+ * @param <U> The type of second parameter (source type 2).
+ * @param <R> The type of return values    (target type).
+ */
+public interface BiFunction<T,U,R> {
+    /**
+     * Computes a value for the given arguments.
+     *
+     * @param v1 The first source value.
+     * @param v2 The second source value.
+     * @return The target value.
+     */
+    R apply(T v1, U v2);
+}

Propchange: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/BiFunction.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -21,6 +21,7 @@ import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.xml.bind.DatatypeConverter;
 
@@ -61,6 +62,45 @@ public final class JDK8 {
     }
 
     /**
+     * Atomically computes and stores the value for the given key. This is a substitute for
+     * {@link ConcurrentMap#compute(java.lang.Object, java.util.function.BiFunction)}
+     * on pre-JDK8 branches.
+     *
+     * @param  <K> The type of keys.
+     * @param  <V> The type of values.
+     * @param  map The map where to store the value.
+     * @param  key The key for the value to compute and store.
+     * @param  remappingFunction The function for computing the value.
+     * @return The new value computed by the given function.
+     *
+     * @since 0.5
+     */
+    public static <K,V> V compute(final ConcurrentMap<K,V> map, final K key,
+            BiFunction<? super K, ? super V, ? extends V> remappingFunction)
+    {
+        V newValue;
+        boolean success;
+        do {
+            final V oldValue = map.get(key);
+            newValue = remappingFunction.apply(key, oldValue);
+            if (newValue != null) {
+                if (oldValue != null) {
+                    success = map.replace(key, oldValue, newValue);
+                } else {
+                    success = (map.putIfAbsent(key, newValue) == null);
+                }
+            } else {
+                if (oldValue != null) {
+                    success = map.remove(key, oldValue);
+                } else {
+                    return null;
+                }
+            }
+        } while (!success);
+        return newValue;
+    }
+
+    /**
      * Parses a date from a string in ISO 8601 format. More specifically, this method expects the
      * format defined by <cite>XML Schema Part 2: Datatypes for {@code xsd:dateTime}</cite>.
      *

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -94,6 +94,17 @@ public final class CollectionsExt extend
     }
 
     /**
+     * Returns the given value as a singleton if non-null, or returns an empty set otherwise.
+     *
+     * @param  <E> The element type.
+     * @param  element The element to returns in a collection if non-null.
+     * @return A collection containing the given element if non-null, or an empty collection otherwise.
+     */
+    public static <E> Set<E> singletonOrEmpty(final E element) {
+        return (element != null) ? Collections.singleton(element) : Collections.<E>emptySet();
+    }
+
+    /**
      * Returns the given array if non-empty, or {@code null} if the given array is null or empty.
      * This method is generally not recommended, since public API should prefer empty array instead of null.
      * However this method is occasionally useful for managing private fields.

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/mock/IdentifiedObjectMock.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/mock/IdentifiedObjectMock.java?rev=1627171&r1=1627170&r2=1627171&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/mock/IdentifiedObjectMock.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/mock/IdentifiedObjectMock.java [UTF-8] Tue Sep 23 22:37:08 2014
@@ -19,7 +19,6 @@ package org.apache.sis.test.mock;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.Collection;
-import java.util.Collections;
 import java.io.Serializable;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -29,6 +28,7 @@ import org.opengis.util.InternationalStr
 import org.opengis.metadata.citation.Citation;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.ReferenceIdentifier;
+import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.jaxb.gco.GO_GenericName;
 
 
@@ -151,7 +151,7 @@ public strictfp class IdentifiedObjectMo
      */
     @Override
     public final Collection<GenericName> getAlias() {
-        return (alias != null) ? Collections.singleton(alias) : Collections.<GenericName>emptySet();
+        return CollectionsExt.singletonOrEmpty(alias);
     }
 
     /**



Mime
View raw message