sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1795924 - in /sis/trunk: ./ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/test/java/org/apache/sis/feature/ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-metadata/src/main/java/org/apache/s...
Date Tue, 23 May 2017 15:43:26 GMT
Author: desruisseaux
Date: Tue May 23 15:43:26 2017
New Revision: 1795924

URL: http://svn.apache.org/viewvc?rev=1795924&view=rev
Log:
Merge from JDK7 branch.

Added:
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java
      - copied unchanged from r1795923, sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/IndexMap.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java
      - copied unchanged from r1795923, sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/LookupInfo.java
Modified:
    sis/trunk/   (props changed)
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
    sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue May 23 15:43:26 2017
@@ -1,5 +1,5 @@
 /sis/branches/Android:1430670-1480699
 /sis/branches/JDK6:1394364-1758914
-/sis/branches/JDK7:1394913-1795380
-/sis/branches/JDK8:1584960-1795379
+/sis/branches/JDK7:1394913-1795923
+/sis/branches/JDK8:1584960-1795919
 /sis/branches/JDK9:1773327-1789983

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java [UTF-8] Tue May 23 15:43:26 2017
@@ -21,6 +21,7 @@ import java.util.Iterator;
 import java.util.Collection;
 import java.util.Collections;
 import java.io.Serializable;
+import org.opengis.util.ScopedName;
 import org.opengis.util.GenericName;
 import org.opengis.metadata.quality.DataQuality;
 import org.opengis.metadata.maintenance.ScopeCode;
@@ -234,7 +235,7 @@ public abstract class AbstractFeature im
         } else if (pt instanceof DefaultAssociationRole) {
             return ((DefaultAssociationRole) pt).newInstance();
         } else {
-            throw unsupportedPropertyType(pt.getName());
+            throw new IllegalArgumentException(unsupportedPropertyType(pt.getName()));
         }
     }
 
@@ -269,7 +270,7 @@ public abstract class AbstractFeature im
             final int maximumOccurs = ((DefaultAssociationRole) pt).getMaximumOccurs();
             return maximumOccurs > 1 ? Collections.EMPTY_LIST : null;       // No default value for associations.
         } else {
-            throw unsupportedPropertyType(pt.getName());
+            throw new IllegalArgumentException(unsupportedPropertyType(pt.getName()));
         }
     }
 
@@ -435,7 +436,7 @@ public abstract class AbstractFeature im
         } else if (property instanceof AbstractAssociation) {
             setAssociationValue((AbstractAssociation) property, value);
         } else {
-            throw unsupportedPropertyType(property.getName());
+            throw new IllegalArgumentException(unsupportedPropertyType(property.getName()));
         }
     }
 
@@ -463,7 +464,7 @@ public abstract class AbstractFeature im
                     } while ((element = it.next()) == null || base.isInstance(element));
                     // Found an illegal value. Exeption is thrown below.
                 }
-                throw illegalValueClass(pt, base, element);         // 'element' can not be null here.
+                throw new ClassCastException(illegalValueClass(pt, base, element));         // 'element' can not be null here.
             }
         }
         ((AbstractAttribute) attribute).setValue(value);
@@ -481,14 +482,14 @@ public abstract class AbstractFeature im
             if (value instanceof AbstractFeature) {
                 final DefaultFeatureType actual = ((AbstractFeature) value).getType();
                 if (base != actual && !DefaultFeatureType.maybeAssignableFrom(base, actual)) {
-                    throw illegalFeatureType(role, base, actual);
+                    throw new IllegalArgumentException(illegalFeatureType(role, base, actual));
                 }
             } else if (value instanceof Collection<?>) {
                 verifyAssociationValues(role, (Collection<?>) value);
                 association.setValues((Collection<? extends AbstractFeature>) value);
                 return;                                 // Skip the setter at the end of this method.
             } else {
-                throw illegalValueClass(role, AbstractFeature.class, value);
+                throw new ClassCastException(illegalValueClass(role, AbstractFeature.class, value));
             }
         }
         association.setValue((AbstractFeature) value);
@@ -555,7 +556,7 @@ public abstract class AbstractFeature im
                 return verifyAssociationValue((DefaultAssociationRole) pt, value);
             }
         } else {
-            throw unsupportedPropertyType(pt.getName());
+            throw new IllegalArgumentException(unsupportedPropertyType(pt.getName()));
         }
         return value;
     }
@@ -578,7 +579,7 @@ public abstract class AbstractFeature im
         } else if (!isSingleton && value instanceof Collection<?>) {
             return CheckedArrayList.castOrCopy((Collection<?>) value, valueClass);
         } else {
-            throw illegalValueClass(type, valueClass, value);
+            throw new ClassCastException(illegalValueClass(type, valueClass, value));
         }
     }
 
@@ -604,13 +605,13 @@ public abstract class AbstractFeature im
             if (base == valueType || DefaultFeatureType.maybeAssignableFrom(base, valueType)) {
                 return isSingleton ? value : singletonList(AbstractFeature.class, role.getMinimumOccurs(), value);
             } else {
-                throw illegalFeatureType(role, base, valueType);
+                throw new IllegalArgumentException(illegalFeatureType(role, base, valueType));
             }
         } else if (!isSingleton && value instanceof Collection<?>) {
             verifyAssociationValues(role, (Collection<?>) value);
             return CheckedArrayList.castOrCopy((Collection<?>) value, AbstractFeature.class);
         } else {
-            throw illegalValueClass(role, AbstractFeature.class, value);
+            throw new ClassCastException(illegalValueClass(role, AbstractFeature.class, value));
         }
     }
 
@@ -623,11 +624,11 @@ public abstract class AbstractFeature im
         for (final Object value : values) {
             ArgumentChecks.ensureNonNullElement("values", index, value);
             if (!(value instanceof AbstractFeature)) {
-                throw illegalValueClass(role, AbstractFeature.class, value);
+                throw new ClassCastException(illegalValueClass(role, AbstractFeature.class, value));
             }
             final DefaultFeatureType type = ((AbstractFeature) value).getType();
             if (base != type && !DefaultFeatureType.maybeAssignableFrom(base, type)) {
-                throw illegalFeatureType(role, base, type);
+                throw new IllegalArgumentException(illegalFeatureType(role, base, type));
             }
             index++;
         }
@@ -645,34 +646,57 @@ public abstract class AbstractFeature im
     }
 
     /**
-     * Returns the exception for a property type which is neither an attribute or an association.
+     * Returns the exception message for a property not found. The message will differ depending
+     * on whether the property is not found because ambiguous or because it does not exist.
+     *
+     * @param  feature   the name of the feature where a property where searched ({@link String} or {@link GenericName}).
+     * @param  property  the name of the property which has not been found.
+     */
+    static String propertyNotFound(final FeatureType type, final Object feature, final String property) {
+        GenericName ambiguous = null;
+        for (final AbstractIdentifiedType p : type.getProperties(true)) {
+            final GenericName next = p.getName();
+            GenericName name = next;
+            do {
+                if (property.equalsIgnoreCase(name.toString())) {
+                    if (ambiguous == null) {
+                        ambiguous = next;
+                    } else {
+                        return Errors.format(Errors.Keys.AmbiguousName_3, ambiguous, next, property);
+                    }
+                }
+            } while (name instanceof ScopedName && (name = ((ScopedName) name).tail()) != null);
+        }
+        return Resources.format(Resources.Keys.PropertyNotFound_2, feature, property);
+    }
+
+    /**
+     * Returns the exception message for a property type which is neither an attribute or an association.
      * This method is invoked after a {@code PropertyType} has been found for the user-supplied name,
      * but that property can not be stored in or extracted from a {@link Property} instance.
      */
-    static IllegalArgumentException unsupportedPropertyType(final GenericName name) {
-        return new IllegalArgumentException(Resources.format(Resources.Keys.CanNotInstantiateProperty_1, name));
+    static String unsupportedPropertyType(final GenericName name) {
+        return Resources.format(Resources.Keys.CanNotInstantiateProperty_1, name);
     }
 
     /**
-     * Returns the exception for a property value of wrong Java class.
+     * Returns the exception message for a property value of wrong Java class.
      *
      * @param  value  the value, which shall be non-null.
      */
-    private static ClassCastException illegalValueClass(
-            final AbstractIdentifiedType property, final Class<?> expected, final Object value)
-    {
-        return new ClassCastException(Resources.format(Resources.Keys.IllegalPropertyValueClass_3,
-                property.getName(), expected, value.getClass()));
+    private static String illegalValueClass(final AbstractIdentifiedType property, final Class<?> expected, final Object value) {
+        return Resources.format(Resources.Keys.IllegalPropertyValueClass_3,
+                                property.getName(), expected, value.getClass());
     }
 
     /**
-     * Returns the exception for an association value of wrong type.
+     * Returns the exception message for an association value of wrong type.
      */
-    private static IllegalArgumentException illegalFeatureType(
+    private static String illegalFeatureType(
             final DefaultAssociationRole association, final FeatureType expected, final FeatureType actual)
     {
-        return new IllegalArgumentException(Resources.format(Resources.Keys.IllegalFeatureType_3,
-                association.getName(), expected.getName(), actual.getName()));
+        return Resources.format(Resources.Keys.IllegalFeatureType_3,
+                                association.getName(), expected.getName(), actual.getName());
     }
 
     /**

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Tue May 23 15:43:26 2017
@@ -826,7 +826,7 @@ public class DefaultFeatureType extends
         if (pt != null) {
             return pt;
         }
-        throw new IllegalArgumentException(Resources.format(Resources.Keys.PropertyNotFound_2, getName(), name));
+        throw new IllegalArgumentException(AbstractFeature.propertyNotFound(this, getName(), name));
     }
 
     /**

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java [UTF-8] Tue May 23 15:43:26 2017
@@ -20,7 +20,6 @@ import java.util.Map;
 import java.util.Arrays;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.quality.DataQuality;
-import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.internal.util.Cloner;
 import org.apache.sis.util.ArgumentChecks;
 
@@ -87,7 +86,7 @@ final class DenseFeature extends Abstrac
         if (index != null) {
             return index;
         }
-        throw new IllegalArgumentException(Resources.format(Resources.Keys.PropertyNotFound_2, getName(), name));
+        throw new IllegalArgumentException(propertyNotFound(type, getName(), name));
     }
 
     /**
@@ -190,7 +189,7 @@ final class DenseFeature extends Abstrac
                 } else if (element instanceof AbstractAssociation) {
                     return getAssociationValue((AbstractAssociation) element);
                 } else {
-                    throw unsupportedPropertyType(((Property) element).getName());
+                    throw new IllegalArgumentException(unsupportedPropertyType(((Property) element).getName()));
                 }
             }
         }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java [UTF-8] Tue May 23 15:43:26 2017
@@ -22,13 +22,10 @@ import java.util.Objects;
 import java.util.ConcurrentModificationException;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.quality.DataQuality;
-import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.internal.util.Cloner;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CorruptedObjectException;
 
-// Branch-dependent imports
-
 
 /**
  * A feature in which only a small fraction of properties are expected to be provided. This implementation uses
@@ -124,7 +121,7 @@ final class SparseFeature extends Abstra
         if (index != null) {
             return index;
         }
-        throw new IllegalArgumentException(Resources.format(Resources.Keys.PropertyNotFound_2, getName(), name));
+        throw new IllegalArgumentException(propertyNotFound(type, getName(), name));
     }
 
     /**
@@ -239,7 +236,7 @@ final class SparseFeature extends Abstra
             } else if (element instanceof AbstractAssociation) {
                 return getAssociationValue((AbstractAssociation) element);
             } else if (valuesKind == PROPERTIES) {
-                throw unsupportedPropertyType(((Property) element).getName());
+                throw new IllegalArgumentException(unsupportedPropertyType(((Property) element).getName()));
             } else {
                 throw new CorruptedObjectException(getName());
             }

Modified: sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/test/java/org/apache/sis/feature/DefaultFeatureTypeTest.java [UTF-8] Tue May 23 15:43:26 2017
@@ -349,6 +349,15 @@ public final strictfp class DefaultFeatu
         } catch (IllegalArgumentException e) {
             final String message = e.getMessage();
             assertTrue(message, message.contains("name"));      // Property name.
+            assertTrue(message, message.contains("ns1:name"));  // Ambiguity 1.
+            assertTrue(message, message.contains("ns2:name"));  // Ambiguity 2.
+        }
+        try {
+            feature.getProperty("other");
+            fail("Expected no property.");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("other"));     // Property name.
             assertTrue(message, message.contains("City"));      // Feature name.
         }
     }

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] Tue May 23 15:43:26 2017
@@ -598,8 +598,11 @@ public class MetadataStandard implements
 
     /**
      * Returns the implementation class for the given interface, or {@code null} if none.
-     * The default implementation returns {@code null} in every cases. Subclasses shall
-     * override this method in order to map GeoAPI interfaces to their implementation.
+     * If non-null, the returned class must have a public no-argument constructor and the
+     * metadata instance created by that constructor must be initially empty (no default value).
+     *
+     * <p>The default implementation returns {@code null} in every cases. Subclasses shall
+     * override this method in order to map GeoAPI interfaces to their implementation.</p>
      *
      * @param  <T>   the compile-time {@code type}.
      * @param  type  the interface, typically from the {@code org.opengis.metadata} package.
@@ -653,14 +656,14 @@ public class MetadataStandard implements
     }
 
     /**
-     * Returns the type of all properties, or their declaring type, defined in the given
-     * metadata type. The keys in the returned map are the same than the keys in the above
-     * {@linkplain #asNameMap name map}. The values are determined by the {@code valuePolicy}
-     * argument, which can be {@linkplain TypeValuePolicy#ELEMENT_TYPE element type} or the
+     * Returns the type of all properties, or their declaring type, defined in the given metadata type.
+     * The keys in the returned map are the same than the keys in the above {@linkplain #asNameMap name map}.
+     * The values are determined by the {@code valuePolicy} argument, which can be
+     * {@linkplain TypeValuePolicy#ELEMENT_TYPE element type} or the
      * {@linkplain TypeValuePolicy#DECLARING_INTERFACE declaring interface} among others.
      *
-     * <p><b>Example:</b> the following code prints the
-     * {@link org.opengis.util.InternationalString} class name:</p>
+     * <div class="note"><b>Example:</b>
+     * the following code prints the {@link org.opengis.util.InternationalString} class name:
      *
      * {@preformat java
      *   MetadataStandard  standard = MetadataStandard.ISO_19115;
@@ -668,6 +671,7 @@ public class MetadataStandard implements
      *   Class<?> value = types.get("alternateTitle");
      *   System.out.println(value);                       // class org.opengis.util.InternationalString
      * }
+     * </div>
      *
      * @param  type         the interface or implementation class of a metadata.
      * @param  keyPolicy    determines the string representation of map keys.
@@ -716,7 +720,7 @@ public class MetadataStandard implements
      * </ul>
      *
      * <div class="note"><b>Note:</b>
-     * The rational for implementing {@code CheckedContainer} is to consider each {@code ExtendedElementInformation}
+     * the rational for implementing {@code CheckedContainer} is to consider each {@code ExtendedElementInformation}
      * instance as the set of all possible values for the property. If the information had a {@code contains(E)} method,
      * it would return {@code true} if the given value is valid for that property.</div>
      *
@@ -734,8 +738,8 @@ public class MetadataStandard implements
      *
      * @see org.apache.sis.metadata.iso.DefaultExtendedElementInformation
      */
-    public Map<String,ExtendedElementInformation> asInformationMap(Class<?> type,
-            final KeyNamePolicy keyPolicy) throws ClassCastException
+    public Map<String,ExtendedElementInformation> asInformationMap(Class<?> type, final KeyNamePolicy keyPolicy)
+            throws ClassCastException
     {
         ensureNonNull("type",     type);
         ensureNonNull("keyNames", keyPolicy);
@@ -747,6 +751,35 @@ public class MetadataStandard implements
     }
 
     /**
+     * Returns indices for all properties defined in the given metadata type.
+     * The keys in the returned map are the same than the keys in the above {@linkplain #asNameMap name map}.
+     * The values are arbitrary indices numbered from 0 inclusive to <var>n</var> exclusive, where <var>n</var>
+     * is the number of properties declared in the given metadata type.
+     *
+     * <p>Property indices may be used as an alternative to property names by some applications doing their own storage.
+     * Such index usages are fine for temporary storage during the Java Virtual Machine lifetime, but indices should not
+     * be used in permanent storage. The indices are stable as long as the metadata implementation does not change,
+     * but may change when the implementation is upgraded to a newer version.</p>
+     *
+     * @param  type       the interface or implementation class of a metadata.
+     * @param  keyPolicy  determines the string representation of map keys.
+     * @return indices of all properties defined by the given metadata type.
+     * @throws ClassCastException if the specified interface or implementation class does
+     *         not extend or implement a metadata interface of the expected package.
+     */
+    public Map<String,Integer> asIndexMap(Class<?> type, final KeyNamePolicy keyPolicy)
+            throws ClassCastException
+    {
+        ensureNonNull("type",      type);
+        ensureNonNull("keyPolicy", keyPolicy);
+        final Class<?> implementation = getImplementation(type);
+        if (implementation != null) {
+            type = implementation;
+        }
+        return new IndexMap(getAccessor(new CacheKey(type), true), keyPolicy);
+    }
+
+    /**
      * Returns a view of the specified metadata object as a {@link Map}.
      * The map is backed by the metadata object using Java reflection, so changes in the
      * underlying metadata object are immediately reflected in the map and conversely.

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] Tue May 23 15:43:26 2017
@@ -702,9 +702,29 @@ class PropertyAccessor {
         try {
             return method.invoke(metadata, (Object[]) null);
         } catch (IllegalAccessException e) {
-            // Should never happen since 'getters' should contains only public methods.
-            throw new AssertionError(e);
+            /*
+             * Should never happen since 'getters' should contains only public methods.
+             */
+            throw new AssertionError(method.toString(), e);
+        } catch (IllegalArgumentException e) {
+            /*
+             * May happen if the getter method is defined only in the implementation class, not in the interface,
+             * but the given metadata object is an instance of another implementation class than the expected one.
+             *
+             * Example: CI_Citation.graphics didn't existed in ISO 19115:2003 and has been added in ISO 19115:2014.
+             * Consequently there is no Citation.getGraphics() method in GeoAPI 3.0 interfaces (only in GeoAPI 3.1),
+             * but there is a DefaultCitation.getGraphics() method in Apache SIS implementation since SIS is a little
+             * bit ahead of GeoAPI. But if the 'metadata' argument is another implementation of the Citation interface,
+             * attempt to invoke DefaultCitation.getGraphics() will fail with IllegalArgumentException.
+             */
+            if (!method.getDeclaringClass().isInstance(metadata)) {
+                return null;
+            }
+            throw e;                                // Exception thrown for another reason. This is probably a bug.
         } catch (InvocationTargetException e) {
+            /*
+             * Exception in user code (not a wrong usage of reflection).
+             */
             final Throwable cause = e.getTargetException();
             if (cause instanceof RuntimeException) {
                 throw (RuntimeException) cause;

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java [UTF-8] Tue May 23 15:43:26 2017
@@ -20,9 +20,17 @@ import java.lang.reflect.InvocationHandl
 import java.lang.reflect.Method;
 import java.sql.SQLException;
 import java.util.Collection;
+import java.util.Map;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.metadata.KeyNamePolicy;
+import org.apache.sis.metadata.ValueExistencePolicy;
+import org.apache.sis.internal.system.Semaphores;
+
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.JDK8;
 
 
 /**
@@ -35,6 +43,16 @@ import org.apache.sis.util.collection.Ba
  * name is translated into a table name, and the method name is translated into a column name.
  * Then the information is fetched in the underlying metadata database.
  *
+ * <p>There is usually a one-to-one correspondence between invoked methods and the columns to be read, but not always.
+ * Some method invocations may actually trig a computation using the values of other columns. This happen for example
+ * when invoking a deprecated method which computes its value from non-deprecated methods. Such situations happen in
+ * the transition from ISO 19115:2003 to ISO 19115:2014 and may happen again in the future as standards are revised.
+ * The algorithms are encoded in implementation classes like the ones in {@link org.apache.sis.metadata.iso} packages,
+ * and access to those implementation classes is enabled by the {@link #cache} field (which, consequently, is more than
+ * only a cache).</p>
+ *
+ * <p>Instance of this class shall be thread-safe.</p>
+ *
  * @author  Touraïvane (IRD)
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8
@@ -57,8 +75,35 @@ final class Dispatcher implements Invoca
     /**
      * Index in the {@code CachedStatement} cache array where to search first. This is only a hint for increasing
      * the chances to find quickly a {@code CachedStatement} instance for the right type and identifier.
+     *
+     * <div class="note"><b>Design note:</b>
+     * this field is declared in this {@code Dispatcher} class instead than {@link CachedStatement} because we need
+     * it before a {@code CachedStatement} instance can be found. Furthermore two {@code Dispatcher} instances may
+     * have different {@code preferredIndex} values even if their {@link CachedStatement#type} value is the same,
+     * since their {@link #identifier} values are different.</div>
+     */
+    byte preferredIndex;
+
+    /**
+     * The metadata instance where to store the property (column) values, or {@code null} if not yet created.
+     * For ISO 19115, this is an instance of one of the classes defined in {@link org.apache.sis.metadata.iso}
+     * package or sub-packages. The intend is not only to cache the property values, but also to leverage
+     * implementations that compute automatically some property values from other properties.
+     * The main usage is computing the value of a deprecated property from the values of non-deprecated ones,
+     * e.g. for transition from ISO 19115:2003 to ISO 19115:2014.
+     */
+    private transient volatile Object cache;
+
+    /**
+     * A bitmask of properties having null values. Cached for avoiding to query the database many times.
+     * Bit indices are given by {@link LookupInfo#asIndexMap(MetadataStandard)}. If a metadata contains
+     * more than 64 properties, no "null value" information will be stored for the extra properties.
+     * No damage will happen except more database accesses than needed.
+     *
+     * <p>We do not need to synchronize this field because it is only an optimization. It is okay if a bit
+     * is wrongly zero; the only consequence is that it will cause one more database access than needed.</p>
      */
-    int preferredIndex;
+    private transient long nullValues;
 
     /**
      * Creates a new metadata handler.
@@ -104,27 +149,84 @@ final class Dispatcher implements Invoca
                 return (args[0] == source) ? identifier : null;
             }
             default: {
-                if (n != 0) break;
-                if (!source.standard.isMetadata(type)) break;
-                /*
-                 * The invoked method is a method from the metadata interface.
-                 * Consequently, the information should exist in the database.
-                 */
-                try {
-                    return source.getValue(type, method, this);
-                } catch (SQLException | MetadataStoreException e) {
-                    Class<?> returnType = method.getReturnType();
-                    if (Collection.class.isAssignableFrom(returnType)) {
-                        final Class<?> elementType = Classes.boundOfParameterizedProperty(method);
-                        if (elementType != null) {
-                            returnType = elementType;
+                if (n != 0 || !source.standard.isMetadata(type)) {
+                    throw new BackingStoreException(Errors.format(Errors.Keys.UnsupportedOperation_1,
+                                Classes.getShortName(type) + '.' + name));
+                }
+                break;
+            }
+        }
+        /*
+         * The invoked method is a method from the metadata interface.
+         * Consequently, the information should exist in the database.
+         * First, we will check the cache. If the value is not present, we will query the database and
+         * fetch the cache again (because the class that implement the cache may perform some computation).
+         */
+        Object value = null;
+        final LookupInfo info = source.getLookupInfo(type);
+        final long nullBit = 1L << info.asIndexMap(source.standard).get(method.getName());     // Okay even if overflow.
+        /*
+         * The NULL_COLLECTION semaphore prevents creation of new empty collections by getter methods
+         * (a consequence of lazy instantiation). The intend is to avoid creation of unnecessary objects
+         * for all unused properties. Users should not see behavioral difference.
+         */
+        if ((nullValues & nullBit) == 0) try {
+            final boolean allowNull = Semaphores.queryAndSet(Semaphores.NULL_COLLECTION);
+            try {
+                Object cache = this.cache;
+                if (cache != null) {
+                    synchronized (cache) {
+                        value = method.invoke(cache);
+                    }
+                }
+                if (value == null) {
+                    info.setMetadataType(type);     // Precaution in case method.invoke(cache) fetched other metadata.
+                    value = source.readColumn(info, method, this);
+                    if (value != null) {
+                        if (cache == null) {
+                            final Class<?> impl = source.standard.getImplementation(type);
+                            if (impl == null) {
+                                return value;
+                            }
+                            this.cache = cache = impl.newInstance();
+                            /*
+                             * We do not use AtomicReference because it is okay if the cache is instantiated twice.
+                             * It would cause us to query the database twice, but we should get the same information.
+                             */
+                        }
+                        final Map<String, Object> map = source.standard.asValueMap(cache, type,
+                                    KeyNamePolicy.METHOD_NAME, ValueExistencePolicy.ALL);
+                        synchronized (cache) {
+                            value = JDK8.putIfAbsent(map, method.getName(), value);
+                            if (value == null) {
+                                value = method.invoke(cache);
+                            }
                         }
                     }
-                    throw new BackingStoreException(Errors.format(Errors.Keys.DatabaseError_2, returnType, identifier), e);
                 }
+            } finally {
+                if (!allowNull) {
+                    Semaphores.clear(Semaphores.NULL_COLLECTION);
+                }
+            }
+        } catch (ReflectiveOperationException | SQLException | MetadataStoreException e) {
+            Class<?> returnType = method.getReturnType();
+            if (Collection.class.isAssignableFrom(returnType)) {
+                final Class<?> elementType = Classes.boundOfParameterizedProperty(method);
+                if (elementType != null) {
+                    returnType = elementType;
+                }
+            }
+            throw new BackingStoreException(Errors.format(Errors.Keys.DatabaseError_2, returnType, identifier), e);
+        }
+        if (value == null) {
+            nullValues |= nullBit;
+            final Class<?> returnType = method.getReturnType();
+            if (Collection.class.isAssignableFrom(returnType)) {
+                value = CollectionsExt.empty(returnType);
             }
         }
-        throw new BackingStoreException(Errors.format(Errors.Keys.UnsupportedOperation_1, type + "." + name));
+        return value;
     }
 
     /**

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataProxy.java [UTF-8] Tue May 23 15:43:26 2017
@@ -19,6 +19,8 @@ package org.apache.sis.metadata.sql;
 
 /**
  * Interface for metadata that are implemented by a proxy class.
+ * Instances of this interface are created by calls to {@code Proxy.newProxyInstance(…)}
+ * in {@link MetadataSource#lookup(Class, String)}.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java [UTF-8] Tue May 23 15:43:26 2017
@@ -65,13 +65,14 @@ import org.apache.sis.util.logging.Warni
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.ObjectConverter;
-import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.UnconvertibleObjectException;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.iso.Types;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.JDK8;
+
 
 /**
  * A connection to a metadata database in read-only mode. It can be either the database
@@ -95,6 +96,7 @@ import org.apache.sis.util.iso.Types;
  * <table class="sis">
  *   <caption>Optional properties at construction time</caption>
  *   <tr><th>Key</th>                     <th>Value type</th>          <th>Description</th></tr>
+ *   <tr><td>{@code "catalog"}</td>       <td>{@link String}</td>      <td>The database catalog where the metadata schema is stored.</td></tr>
  *   <tr><td>{@code "classloader"}</td>   <td>{@link ClassLoader}</td> <td>The class loader to use for creating {@link Proxy} instances.</td></tr>
  *   <tr><td>{@code "maxStatements"}</td> <td>{@link Integer}</td>     <td>Maximal number of {@link PreparedStatement}s that can be kept simultaneously open.</td></tr>
  * </table>
@@ -112,10 +114,10 @@ import org.apache.sis.util.iso.Types;
  */
 public class MetadataSource implements AutoCloseable {
     /**
-     * The catalog, set to {@code null} for now. This is defined as a constant in order to make easier
-     * to spot the places where catalog would be used, if we want to use it in a future version.
+     * The policy for column names. We use UML identifiers (e.g. {@code "title"}) as defined by the metadata standard
+     * (typically ISO 19115).
      */
-    static final String CATALOG = null;
+    static final KeyNamePolicy NAME_POLICY = KeyNamePolicy.UML_IDENTIFIER;
 
     /**
      * The column name used for the identifiers. We do not quote this identifier;
@@ -206,6 +208,11 @@ public class MetadataSource implements A
     private final CachedStatement[] statements;
 
     /**
+     * The catalog, or {@code null} if none.
+     */
+    final String catalog;
+
+    /**
      * The database schema where metadata are stored, or {@code null} if none. In the metadata source
      * {@linkplain #getProvided() provided by SIS}, this is {@code "metadata"} or {@code "METADATA"},
      * depending on the database convention regarding lower case / upper case identifiers.
@@ -258,28 +265,15 @@ public class MetadataSource implements A
     private final WeakValueHashMap<CacheKey,Object> pool;
 
     /**
-     * The last converter used. This field exists only for performance purposes, on
-     * the assumption that the last used converter has good chances to be used again.
-     *
-     * @see #getValue(Class, Method, Dispatcher)
-     */
-    private transient volatile ObjectConverter<?,?> lastConverter;
-
-    /**
-     * The last "method name to column name" map returned by {@link #asNameMap(Class)}.
-     * Cached on assumption that the same map will be used more than once before to move to another metadata object.
-     */
-    private transient Map<String,String> lastNameMap;
-
-    /**
-     * The {@code type} argument in the last call to {@link #asNameMap(Class)}.
+     * Some information about last used objects. Cached on assumption that the same information
+     * will be used more than once before to move to another metadata object.
      */
-    private transient Class<?> lastNameMapType;
+    private final ThreadLocal<LookupInfo> lastUsed;
 
     /**
      * Where to report the warnings. This is not necessarily a logger, since users can register listeners.
      *
-     * @see #getValue(Class, Method, Dispatcher)
+     * @see #readColumn(LookupInfo, Method, Dispatcher)
      */
     private final WarningListeners<MetadataSource> listeners;
 
@@ -353,15 +347,19 @@ public class MetadataSource implements A
     {
         ArgumentChecks.ensureNonNull("standard",   standard);
         ArgumentChecks.ensureNonNull("dataSource", dataSource);
-        ClassLoader classloader   = Containers.property(properties, "classloader",   ClassLoader.class);
-        Integer     maxStatements = Containers.property(properties, "maxStatements", Integer.class);
+        ClassLoader classloader;
+        Integer maxStatements;
+
+        catalog       = Containers.property(properties, "catalog",       String.class);
+        classloader   = Containers.property(properties, "classloader",   ClassLoader.class);
+        maxStatements = Containers.property(properties, "maxStatements", Integer.class);
         if (classloader == null) {
             classloader = getClass().getClassLoader();
         }
         if (maxStatements == null) {
             maxStatements = 10;               // Default value, may change in any future Apache SIS version.
         } else {
-            ArgumentChecks.ensureBetween("maxStatements", 2, 100, maxStatements);       // Arbitrary limits.
+            ArgumentChecks.ensureBetween("maxStatements", 2, 0xFF, maxStatements);   // Unsigned byte range.
         }
         this.standard     = standard;
         this.dataSource   = dataSource;
@@ -372,6 +370,11 @@ public class MetadataSource implements A
         this.tableColumns = new HashMap<>();
         this.pool         = new WeakValueHashMap<>(CacheKey.class);
         this.listeners    = new WarningListeners<>(this);
+        this.lastUsed     = new ThreadLocal<LookupInfo>() {
+            @Override protected LookupInfo initialValue() {
+                return new LookupInfo();
+            }
+        };
     }
 
     /**
@@ -390,12 +393,14 @@ public class MetadataSource implements A
         ArgumentChecks.ensureNonNull("source", source);
         standard     = source.standard;
         dataSource   = source.dataSource;
+        catalog      = source.catalog;
         schema       = source.schema;
         quoteSchema  = source.quoteSchema;
         statements   = new CachedStatement[source.statements.length];
         tableColumns = new HashMap<>();
         classloader  = source.classloader;
         pool         = source.pool;
+        lastUsed     = source.lastUsed;
         listeners    = new WarningListeners<>(this, source.listeners);
     }
 
@@ -420,7 +425,7 @@ public class MetadataSource implements A
                 schema = schema.toLowerCase(Locale.US);
             }
             quoteSchema = false;
-            try (ResultSet result = md.getTables(CATALOG, schema, "CI_Citation", null)) {
+            try (ResultSet result = md.getTables(catalog, schema, "CI_Citation", null)) {
                 if (result.next()) {
                     return;
                 }
@@ -566,25 +571,6 @@ public class MetadataSource implements A
     }
 
     /**
-     * Maps method names to the name of columns in the table for the given metadata. The values in the
-     * returned map must be the same than the keys in the map returned by {@link #asValueMap(Object)}.
-     *
-     * @param  type  the type of metadata object for which to get column names.
-     * @return a map from method names to column names.
-     * @throws ClassCastException if the metadata object type does not extend a metadata interface
-     *         of the expected package.
-     */
-    @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    private Map<String,String> asNameMap(final Class<?> type) throws ClassCastException {
-        assert Thread.holdsLock(this);
-        if (type != lastNameMapType) {
-            lastNameMapType = type;
-            lastNameMap = standard.asNameMap(type, KeyNamePolicy.METHOD_NAME, KeyNamePolicy.UML_IDENTIFIER);
-        }
-        return lastNameMap;
-    }
-
-    /**
      * Returns a view of the given metadata as a map. This method returns always a map using UML identifier
      * and containing all entries including the null ones because the {@code MetadataSource} implementation
      * assumes so.
@@ -595,7 +581,7 @@ public class MetadataSource implements A
      *         of the expected package.
      */
     final Map<String,Object> asValueMap(final Object metadata) throws ClassCastException {
-        return standard.asValueMap(metadata, null, KeyNamePolicy.UML_IDENTIFIER, ValueExistencePolicy.ALL);
+        return standard.asValueMap(metadata, null, NAME_POLICY, ValueExistencePolicy.ALL);
     }
 
     /**
@@ -775,7 +761,7 @@ public class MetadataSource implements A
              * on the search path specified in the database environment variables.
              */
             final DatabaseMetaData md = connection().getMetaData();
-            try (ResultSet rs = md.getColumns(CATALOG, schema, table, null)) {
+            try (ResultSet rs = md.getColumns(catalog, schema, table, null)) {
                 while (rs.next()) {
                     if (!columns.add(rs.getString("COLUMN_NAME"))) {
                         // Paranoiac check, but should never happen.
@@ -825,22 +811,37 @@ public class MetadataSource implements A
     }
 
     /**
+     * Gets the {@link LookupInfo} instance for call to the {@link #readColumn(LookupInfo, Method, Dispatcher)} method.
+     * The call to those two methods must be in the same thread, and no other metadata object shall be queried between
+     * the two calls (unless {@link LookupInfo#setMetadataType(Class)} is invoked again).
+     *
+     * @param  type  the interface class. This is mapped to the table name in the database.
+     */
+    final LookupInfo getLookupInfo(final Class<?> type) {
+        final LookupInfo info = lastUsed.get();
+        info.setMetadataType(type);
+        return info;
+    }
+
+    /**
      * Invoked by {@link MetadataProxy} for fetching an attribute value from a table.
      *
-     * @param  type      the interface class. This is mapped to the table name in the database.
+     * @param  info      the interface type (together with cached information).
+     *                   This is mapped to the table name in the database.
      * @param  method    the method invoked. This is mapped to the column name in the database.
      * @param  toSearch  contains the identifier and preferred index of the record to search.
      * @return the value of the requested attribute.
      * @throws SQLException if the SQL query failed.
      * @throws MetadataStoreException if a value was not found or can not be converted to the expected type.
      */
-    final Object getValue(Class<?> type, final Method method, final Dispatcher toSearch)
+    final Object readColumn(final LookupInfo info, final Method method, final Dispatcher toSearch)
             throws SQLException, MetadataStoreException
     {
         /*
          * If the identifier is prefixed with a table name as in "{CI_Organisation}identifier",
          * the name between bracket is a subtype of the given 'type' argument.
          */
+        Class<?> type = info.getMetadataType();
         if (toSearch.identifier.charAt(0) == TYPE_OPEN) {
             final int i = toSearch.identifier.indexOf(TYPE_CLOSE);
             if (i >= 0) {
@@ -855,11 +856,10 @@ public class MetadataSource implements A
         final Class<?> elementType    = wantCollection ? Classes.boundOfParameterizedProperty(method) : returnType;
         final boolean  isMetadata     = standard.isMetadata(elementType);
         final String   tableName      = getTableName(type);
-        final String   columnName;
+        final String   columnName     = info.asNameMap(standard).get(method.getName());
         final boolean  isArray;
         Object value;
         synchronized (this) {
-            columnName = asNameMap(type).get(method.getName());
             if (!getExistingColumns(tableName).contains(columnName)) {
                 value   = null;
                 isArray = false;
@@ -869,7 +869,7 @@ public class MetadataSource implements A
                  * Note that the usage of 'result' must stay inside this synchronized block
                  * because we can not assume that JDBC connections are thread-safe.
                  */
-                CachedStatement result = take(type, toSearch.preferredIndex);
+                CachedStatement result = take(type, JDK8.toUnsignedInt(toSearch.preferredIndex));
                 if (result == null) {
                     final SQLBuilder helper = helper();
                     final String query = helper.clear().append("SELECT * FROM ")
@@ -884,7 +884,7 @@ public class MetadataSource implements A
                     value = array.getArray();
                     array.free();
                 }
-                toSearch.preferredIndex = recycle(result, toSearch.preferredIndex);
+                toSearch.preferredIndex = (byte) recycle(result, JDK8.toUnsignedInt(toSearch.preferredIndex));
             }
         }
         /*
@@ -899,7 +899,7 @@ public class MetadataSource implements A
                     if (isMetadata) {
                         element = lookup(elementType, element.toString());
                     } else try {
-                        element = convert(elementType, element);
+                        element = info.convert(elementType, element);
                     } catch (UnconvertibleObjectException e) {
                         throw new MetadataStoreException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
                                 columnName + '[' + i + ']', elementType, element.getClass()), e);
@@ -918,21 +918,13 @@ public class MetadataSource implements A
          */
         if (value == null) {
             if (wantCollection) {
-                if (Set.class.isAssignableFrom(returnType)) {
-                    if (SortedSet.class.isAssignableFrom(returnType)) {
-                        return CollectionsExt.emptySortedSet();
-                    } else {
-                        return Collections.EMPTY_SET;
-                    }
-                } else {
-                    return Collections.EMPTY_LIST;
-                }
+                return CollectionsExt.empty(returnType);
             }
         } else {
             if (isMetadata) {
                 value = lookup(elementType, value.toString());
             } else try {
-                value = convert(elementType, value);
+                value = info.convert(elementType, value);
             } catch (UnconvertibleObjectException e) {
                 throw new MetadataStoreException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
                         columnName, elementType, value.getClass()), e);
@@ -947,28 +939,6 @@ public class MetadataSource implements A
         }
         return value;
     }
-
-    /**
-     * Converts the specified non-metadata value into an object of the expected type.
-     * The expected value is an instance of a class outside the metadata package, for example
-     * {@link String}, {@link org.opengis.util.InternationalString}, {@link java.net.URI}, <i>etc.</i>
-     *
-     * @throws UnconvertibleObjectException if the value can not be converter.
-     */
-    @SuppressWarnings({"unchecked","rawtypes"})
-    private Object convert(final Class<?> targetType, Object value) throws UnconvertibleObjectException {
-        final Class<?> sourceType = value.getClass();
-        if (!targetType.isAssignableFrom(sourceType)) {
-            ObjectConverter converter = lastConverter;
-            if (converter == null || !converter.getSourceClass().isAssignableFrom(sourceType) ||
-                                     !targetType.isAssignableFrom(converter.getTargetClass()))
-            {
-                lastConverter = converter = ObjectConverters.find(sourceType, targetType);
-            }
-            value = converter.apply(value);
-        }
-        return value;
-    }
 
     /**
      * Returns the code of the given type and name. This method is defined for avoiding the warning message

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java [UTF-8] Tue May 23 15:43:26 2017
@@ -74,6 +74,10 @@ import org.opengis.referencing.Reference
  *     <th>Value type</th>
  *     <th>Description</th>
  *   </tr><tr>
+ *     <td>{@code "catalog"}</td>
+ *     <td>{@link String}</td>
+ *     <td>The database catalog where the metadata schema is stored.</td>
+ *   </tr><tr>
  *     <td>{@code "classloader"}</td>
  *     <td>{@link ClassLoader}</td>
  *     <td>The class loader to use for creating {@link java.lang.reflect.Proxy} instances.</td>
@@ -285,8 +289,8 @@ public class MetadataWriter extends Meta
         for (final String column : asSingletons.keySet()) {
             if (!columns.contains(column)) {
                 if (colTypes == null) {
-                    colTypes  = standard.asTypeMap(implementationType, KeyNamePolicy.UML_IDENTIFIER, TypeValuePolicy.ELEMENT_TYPE);
-                    colTables = standard.asTypeMap(implementationType, KeyNamePolicy.UML_IDENTIFIER, TypeValuePolicy.DECLARING_INTERFACE);
+                    colTypes  = standard.asTypeMap(implementationType, NAME_POLICY, TypeValuePolicy.ELEMENT_TYPE);
+                    colTables = standard.asTypeMap(implementationType, NAME_POLICY, TypeValuePolicy.DECLARING_INTERFACE);
                 }
                 /*
                  * We have found a column to add. Check if the column actually needs to be added to the parent table
@@ -421,10 +425,10 @@ public class MetadataWriter extends Meta
                                  */
                                 if (referencedTables == null) {
                                     referencedTables = new HashMap<>();
-                                    try (ResultSet rs = stmt.getConnection().getMetaData().getImportedKeys(CATALOG, schema(), table)) {
+                                    try (ResultSet rs = stmt.getConnection().getMetaData().getImportedKeys(catalog, schema(), table)) {
                                         while (rs.next()) {
                                             if ((schema() == null || schema().equals(rs.getString("PKTABLE_SCHEM"))) &&
-                                                (CATALOG  == null || CATALOG.equals(rs.getString("PKTABLE_CAT"))))
+                                                (catalog  == null || catalog.equals(rs.getString("PKTABLE_CAT"))))
                                             {
                                                 referencedTables.put(rs.getString("FKCOLUMN_NAME"),
                                                             new FKey(rs.getString("PKTABLE_NAME"), null,

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/package-info.java [UTF-8] Tue May 23 15:43:26 2017
@@ -33,13 +33,13 @@
  *   <li>{@link org.opengis.util.InternationalString} are stored only for the default locale.</li>
  *   <li>Cyclic graph (<var>A</var> references <var>B</var> which reference <var>A</var>) are not supported,
  *       unless foreigner key constraints are manually disabled for the columns which contain the cyclic references.</li>
- *   <li>Metadata that are sub-interface of other metadata (for example
- *       {@link org.opengis.metadata.extent.GeographicDescription} which extends
- *       {@link org.opengis.metadata.extent.GeographicExtent}) can be stored only
- *       in databases supporting <cite>table inheritance</cite>, like
- *       <a href="http://www.postgresql.org">PostgreSQL</a>.</li>
  * </ul>
  *
+ * If the database supports <cite>table inheritance</cite> (like <a href="http://www.postgresql.org">PostgreSQL</a>),
+ * then this package will leverage that feature for the storage of metadata that are sub-interface of other metadata
+ * (for example {@link org.opengis.metadata.extent.GeographicDescription} which extends
+ * {@link org.opengis.metadata.extent.GeographicExtent}).
+ *
  * @author  Touraïvane (IRD)
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Tue May 23 15:43:26 2017
@@ -389,7 +389,7 @@ class CoordinateOperationRegistry {
         }
         final boolean inverse;
         Collection<CoordinateOperation> operations;
-        Semaphores.queryAndSet(Semaphores.METADATA_ONLY);           // See comment for the same call inside the loop.
+        boolean mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);    // See comment for the same call inside the loop.
         try {
             try {
                 operations = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
@@ -406,7 +406,9 @@ class CoordinateOperationRegistry {
                     }
                 }
             } finally {
-                Semaphores.clear(Semaphores.METADATA_ONLY);
+                if (!mdOnly) {
+                    Semaphores.clear(Semaphores.METADATA_ONLY);
+                }
             }
         } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException e) {
             /*
@@ -442,13 +444,15 @@ class CoordinateOperationRegistry {
                  * The non-public Semaphores.METADATA_ONLY mechanism instructs EPSGDataAccess to
                  * instantiate DeferredCoordinateOperation instead of full coordinate operations.
                  */
-                Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
+                mdOnly = Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
                 try {
                     try {
                         if (!it.hasNext()) break;
                         candidate = it.next();
                     } finally {
-                        Semaphores.clear(Semaphores.METADATA_ONLY);
+                        if (!mdOnly) {
+                            Semaphores.clear(Semaphores.METADATA_ONLY);
+                        }
                     }
                 } catch (BackingStoreException exception) {
                     throw exception.unwrapOrRethrow(FactoryException.class);

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Tue May 23 15:43:26 2017
@@ -63,6 +63,28 @@ public final class CollectionsExt extend
     }
 
     /**
+     * Returns an empty collection of the given type, or {@code null} if the given type is unknown to this method.
+     *
+     * @param  type  the desired collection type.
+     * @return an empty collection of the given type, or {@code null} if the type is unknown.
+     *
+     * @since 0.8
+     */
+    public static Collection<?> empty(final Class<?> type) {
+        if (type.isAssignableFrom(List.class)) {                    // Most common case first.
+            return Collections.EMPTY_LIST;
+        } else if (type.isAssignableFrom(Set.class)) {
+            return Collections.EMPTY_SET;
+        } if (type.isAssignableFrom(SortedSet.class)) {
+            return emptySortedSet();
+        } else if (type.isAssignableFrom(Queue.class)) {
+            return emptyQueue();
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Returns the first element of the given iterable, or {@code null} if none.
      * This method does not emit warning if more than one element is found.
      * Consequently, this method should be used only when multi-occurrence is not ambiguous.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java [UTF-8] Tue May 23 15:43:26 2017
@@ -156,7 +156,7 @@ abstract class ArrayVector<E extends Num
                 int i = 0;
                 double v;
                 do if (i >= length) {
-                    return new Floats(toFloatArray());
+                    return new Floats(floatValues());
                 } while (!(Math.abs((v = doubleValue(i++)) - (float) v) > tolerance));    // Use '!' for accepting NaN.
                 /*
                  * Same try than above loop, but now using base 10 representation.
@@ -164,7 +164,7 @@ abstract class ArrayVector<E extends Num
                  */
                 i = 0;
                 do if (i >= length) {
-                    return new Decimal(toFloatArray());
+                    return new Decimal(floatValues());
                 } while (!(Math.abs((v = doubleValue(i++)) - DecimalFunctions.floatToDouble((float) v)) > tolerance));
             }
         }
@@ -172,17 +172,6 @@ abstract class ArrayVector<E extends Num
     }
 
     /**
-     * Returns a copy of current data as a floating point array.
-     */
-    float[] toFloatArray() {
-        final float[] copy = new float[size()];
-        for (int i=0; i<copy.length; i++) {
-            copy[i] = (float) doubleValue(i);
-        }
-        return copy;
-    }
-
-    /**
      * Default implementation for the convenience of direct sub-types.
      */
     @Override
@@ -272,11 +261,6 @@ abstract class ArrayVector<E extends Num
             return old;
         }
 
-        /** Returns a copy of current data as a floating point array. */
-        @Override float[] toFloatArray() {
-            return Numerics.copyAsFloats(array);
-        }
-
         /** Finds the minimum and maximum values in the array or in a subset of the array. */
         @Override NumberRange<Double> range(final IntSupplier indices, int n) {
             double min = Double.POSITIVE_INFINITY;
@@ -288,6 +272,16 @@ abstract class ArrayVector<E extends Num
             }
             return NumberRange.create(min, true, max, true);
         }
+
+        /** Returns a copy of current data as a floating point array. */
+        @Override public double[] doubleValues() {
+            return array.clone();
+        }
+
+        /** Returns a copy of current data as a floating point array. */
+        @Override public float[] floatValues() {
+            return Numerics.copyAsFloats(array);
+        }
     }
 
     /**
@@ -361,6 +355,11 @@ abstract class ArrayVector<E extends Num
         NumberRange<?> createRange(final float min, final float max) {
             return NumberRange.create(min, true, max, true);
         }
+
+        /** Returns a copy of current data as a floating point array. */
+        @Override public final float[] floatValues() {
+            return array.clone();
+        }
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java [UTF-8] Tue May 23 15:43:26 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.math;
 
+import java.util.Arrays;
 import java.io.Serializable;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.ArgumentChecks;
@@ -86,6 +87,32 @@ abstract class SequenceVector extends Ve
         return this;
     }
 
+    /**
+     * Creates the sequence as a floating point array.
+     */
+    @Override
+    public double[] doubleValues() {
+        if (increment(0).doubleValue() == 0) {
+            final double[] array = new double[size()];
+            Arrays.fill(array, doubleValue(0));
+            return array;
+        }
+        return super.doubleValues();
+    }
+
+    /**
+     * Creates the sequence as a floating point array.
+     */
+    @Override
+    public float[] floatValues() {
+        if (increment(0).doubleValue() == 0) {
+            final float[] array = new float[size()];
+            Arrays.fill(array, floatValue(0));
+            return array;
+        }
+        return super.floatValues();
+    }
+
 
     /**
      * A vector which is a sequence of increasing or decreasing {@code double} values.

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java?rev=1795924&r1=1795923&r2=1795924&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] Tue May 23 15:43:26 2017
@@ -263,6 +263,8 @@ public abstract class Vector extends Abs
      * @throws IndexOutOfBoundsException if the given index is out of bounds.
      * @throws NullPointerException if the value is {@code null} (never happen if this vector wraps an array of primitive type).
      * @throws NumberFormatException if the value is stored as a {@code String} and can not be parsed.
+     *
+     * @see #doubleValues()
      */
     public abstract double doubleValue(int index);
 
@@ -276,6 +278,8 @@ public abstract class Vector extends Abs
      * @throws IndexOutOfBoundsException if the given index is out of bounds.
      * @throws NullPointerException if the value is {@code null} (never happen if this vector wraps an array of primitive type).
      * @throws NumberFormatException if the value is stored as a {@code String} and can not be parsed.
+     *
+     * @see #floatValues()
      */
     public abstract float floatValue(int index);
 
@@ -389,6 +393,8 @@ public abstract class Vector extends Abs
      * @param  index  the index in the [0 … {@linkplain #size() size}-1] range.
      * @return a string representation of the value at the given index (may be {@code null}).
      * @throws IndexOutOfBoundsException if the given index is out of bounds.
+     *
+     * @see #toString()
      */
     public abstract String stringValue(int index);
 
@@ -998,9 +1004,49 @@ public abstract class Vector extends Abs
     }
 
     /**
+     * Copies all values in an array of double precision floating point numbers.
+     * This method is for inter-operability with APIs requiring an array of primitive type.
+     *
+     * <p>The default implementation invokes {@link #doubleValue(int)} for all indices from 0 inclusive
+     * to {@link #size()} exclusive. Subclasses may override with more efficient implementation.</p>
+     *
+     * @return a copy of all floating point values in this vector.
+     *
+     * @see #doubleValue(int)
+     */
+    public double[] doubleValues() {
+        final double[] array = new double[size()];
+        for (int i=0; i<array.length; i++) {
+            array[i] = doubleValue(i);
+        }
+        return array;
+    }
+
+    /**
+     * Copies all values in an array of single precision floating point numbers.
+     * This method is for inter-operability with APIs requiring an array of primitive type.
+     *
+     * <p>The default implementation invokes {@link #floatValue(int)} for all indices from 0 inclusive
+     * to {@link #size()} exclusive. Subclasses may override with more efficient implementation.</p>
+     *
+     * @return a copy of all floating point values in this vector.
+     *
+     * @see #floatValue(int)
+     */
+    public float[] floatValues() {
+        final float[] array = new float[size()];
+        for (int i=0; i<array.length; i++) {
+            array[i] = floatValue(i);
+        }
+        return array;
+    }
+
+    /**
      * Returns a string representation of this vector.
      *
      * @return a string representation of this vector.
+     *
+     * @see #stringValue(int)
      */
     @Override
     public String toString() {



Mime
View raw message