You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2017/05/27 11:46:44 UTC
svn commit: r1796383 - in /sis/branches/JDK8/core:
sis-metadata/src/main/java/org/apache/sis/metadata/
sis-metadata/src/main/java/org/apache/sis/metadata/sql/
sis-utility/src/main/java/org/apache/sis/util/iso/
Author: desruisseaux
Date: Sat May 27 11:46:44 2017
New Revision: 1796383
URL: http://svn.apache.org/viewvc?rev=1796383&view=rev
Log:
When a GeoAPI interface is not defined for an ISO type, fallback on the Apache SIS implementation class.
It does not make a difference for Apache SIS branches using GeoAPI 4.0-SNAPSHOT, but it makes a difference
for Apache SIS trunk using GeoAPI 3.0 releases.
Modified:
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1796383&r1=1796382&r2=1796383&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] Sat May 27 11:46:44 2017
@@ -592,6 +592,7 @@ public class MetadataStandard implements
* Returns the implementation class for the given interface, or {@code null} if none.
* 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).
+ * That no-argument constructor should never throw any checked exception.
*
* <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>
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java?rev=1796383&r1=1796382&r2=1796383&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/Dispatcher.java [UTF-8] Sat May 27 11:46:44 2017
@@ -157,14 +157,7 @@ final class Dispatcher implements Invoca
try {
value = fetchValue(source.getLookupInfo(method.getDeclaringClass()), method);
} 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);
+ throw new BackingStoreException(error(method), e);
}
/*
* At this point we got the metadata property value, which may be null.
@@ -291,6 +284,20 @@ final class Dispatcher implements Invoca
}
/**
+ * Returns the error message for a failure to query the database for the property identified by the given method.
+ */
+ final String error(final Method method) {
+ Class<?> returnType = method.getReturnType();
+ if (Collection.class.isAssignableFrom(returnType)) {
+ final Class<?> elementType = Classes.boundOfParameterizedProperty(method);
+ if (elementType != null) {
+ returnType = elementType;
+ }
+ }
+ return Errors.format(Errors.Keys.DatabaseError_2, returnType, identifier);
+ }
+
+ /**
* Returns a string representation of a metadata of the given type.
*/
private String toString(final Class<?> type) {
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java?rev=1796383&r1=1796382&r2=1796383&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java [UTF-8] Sat May 27 11:46:44 2017
@@ -768,6 +768,25 @@ public class MetadataSource implements A
}
/**
+ * If the given identifier specifies a subtype of the given type, then returns that subtype.
+ * For example if the given type is {@code Party.class} and the given identifier is
+ * {@code "{CI_Organisation}EPSG"}, then this method returns {@code Organisation.class}.
+ * Otherwise this method returns {@code type} unchanged.
+ */
+ private static Class<?> subType(Class<?> type, final String identifier) {
+ if (identifier.charAt(0) == TYPE_OPEN) {
+ final int i = identifier.indexOf(TYPE_CLOSE);
+ if (i >= 0) {
+ final Class<?> subType = Types.forStandardName(identifier.substring(1, i));
+ if (subType != null && type.isAssignableFrom(subType)) {
+ type = subType;
+ }
+ }
+ }
+ return type;
+ }
+
+ /**
* Returns an implementation of the specified metadata interface filled with the data referenced
* by the specified identifier. Alternatively, this method can also return a {@link CodeList} element.
*
@@ -781,24 +800,54 @@ public class MetadataSource implements A
*/
public <T> T lookup(final Class<T> type, final String identifier) throws MetadataStoreException {
ArgumentChecks.ensureNonNull("type", type);
- ArgumentChecks.ensureNonNull("identifier", identifier);
- /*
- * IMPLEMENTATION NOTE: This method must not invoke any method which may access 'statements'.
- * It is not allowed to acquire the lock on 'statements' neither.
- */
+ ArgumentChecks.ensureNonEmpty("identifier", identifier);
Object value;
if (CodeList.class.isAssignableFrom(type)) {
value = getCodeList(type, identifier);
} else {
final CacheKey key = new CacheKey(type, identifier);
+ /*
+ * IMPLEMENTATION NOTE: be careful to not invoke any method that may synchronize on 'this'
+ * inside the block synchronized on 'pool'.
+ */
synchronized (pool) {
value = pool.get(key);
- if (value == null) {
+ if (value == null && type.isInterface()) {
value = Proxy.newProxyInstance(classloader,
new Class<?>[] {type, MetadataProxy.class}, new Dispatcher(identifier, this));
pool.put(key, value);
}
}
+ /*
+ * At this point, a null value means that the given type is a class rather than an interface.
+ * This may happen when a new type defined by a standard has not yet been defined in GeoAPI.
+ * In such case, we only have the implementation class in Apache SIS, not yet the interface.
+ * Since we can not create a Proxy, we have to fetch all property values now. This is not
+ * very efficient and may waste a little bit of memory, but it should not happen too often.
+ */
+ if (value == null) {
+ Method method = null;
+ final Class<?> subType = subType(type, identifier);
+ final Dispatcher toSearch = new Dispatcher(identifier, this);
+ try {
+ value = subType.newInstance();
+ final LookupInfo info = getLookupInfo(subType);
+ final Map<String,Object> map = asValueMap(value);
+ final Map<String,String> methods = standard.asNameMap(subType, NAME_POLICY, KeyNamePolicy.METHOD_NAME);
+ for (final Map.Entry<String,Object> entry : map.entrySet()) {
+ method = subType.getMethod(methods.get(entry.getKey()));
+ info.setMetadataType(subType);
+ final Object p = readColumn(info, method, toSearch);
+ if (p != null) {
+ entry.setValue(p);
+ }
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new MetadataStoreException(Errors.format(Errors.Keys.UnsupportedImplementation_1, subType), e);
+ } catch (SQLException e) {
+ throw new MetadataStoreException(toSearch.error(method), e);
+ }
+ }
}
return type.cast(value);
}
@@ -834,16 +883,7 @@ public class MetadataSource implements A
* 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) {
- final Class<?> subType = Types.forStandardName(toSearch.identifier.substring(1, i));
- if (subType != null && type.isAssignableFrom(subType)) {
- type = subType;
- }
- }
- }
+ final Class<?> type = subType(info.getMetadataType(), toSearch.identifier);
final Class<?> returnType = method.getReturnType();
final boolean wantCollection = Collection.class.isAssignableFrom(returnType);
final Class<?> elementType = wantCollection ? Classes.boundOfParameterizedProperty(method) : returnType;
@@ -931,8 +971,8 @@ public class MetadataSource implements A
}
/**
- * Returns the code of the given type and name. This method is defined for avoiding the warning message
- * when the actual class is unknown (it must have been checked dynamically by the caller however).
+ * Returns the code of the given type and name. This method is defined for avoiding the compiler warning
+ * message when the actual class is unknown (it must have been checked dynamically by the caller however).
*/
@SuppressWarnings({"unchecked","rawtypes"})
private static CodeList<?> getCodeList(final Class<?> type, final String name) {
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java?rev=1796383&r1=1796382&r2=1796383&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java [UTF-8] Sat May 27 11:46:44 2017
@@ -525,6 +525,16 @@ public class MetadataWriter extends Meta
}
/**
+ * Returns the parent of the given type. Normally, {@code type} is an interface, in which case the parent types are
+ * other interfaces that the given type extends. But in some cases (e.g. when Apache SIS implements a new ISO 19115
+ * type not yet defined in GeoAPI), the given type is a class. In such cases we ignore its interface (it usually do
+ * not implement any) and look for its parent class.
+ */
+ private static Class<?>[] getParentTypes(final Class<?> type) {
+ return type.isInterface() ? type.getInterfaces() : new Class<?>[] {type.getSuperclass()};
+ }
+
+ /**
* Returns {@code true} if the given metadata type is a subtype of another metadata.
* If true, then we will need to prefix the identifier by the metadata subtype.
*
@@ -532,7 +542,7 @@ public class MetadataWriter extends Meta
* the result is nevertheless given as a {@code Boolean} wrapper for consistency with {@code createTable(…)}.
*/
private Boolean isChildTable(final Class<?> type) {
- for (final Class<?> candidate : type.getInterfaces()) {
+ for (final Class<?> candidate : getParentTypes(type)) {
if (standard.isMetadata(candidate)) {
return Boolean.TRUE;
}
@@ -559,7 +569,7 @@ public class MetadataWriter extends Meta
if (columns.isEmpty()) {
isChildTable = Boolean.FALSE;
StringBuilder inherits = null;
- for (final Class<?> candidate : type.getInterfaces()) {
+ for (final Class<?> candidate : getParentTypes(type)) {
if (standard.isMetadata(candidate)) {
isChildTable = Boolean.TRUE;
final SQLBuilder helper = helper();
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java?rev=1796383&r1=1796382&r2=1796383&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Types.java [UTF-8] Sat May 27 11:46:44 2017
@@ -504,9 +504,9 @@ public final class Types extends Static
}
/**
- * Returns the GeoAPI interface for the given ISO name, or {@code null} if none.
- * The identifier argument shall be the value documented in the {@link UML#identifier()}
- * annotation associated with the GeoAPI interface.
+ * Returns the Java type (usually a GeoAPI interface) for the given ISO name, or {@code null} if none.
+ * The identifier argument shall be the value documented in the {@link UML#identifier()} annotation on
+ * the Java type.
*
* <div class="note"><b>Examples:</b>
* <ul>
@@ -515,8 +515,16 @@ public final class Types extends Static
* </ul>
* </div>
*
- * Only identifiers for the stable part of GeoAPI are recognized. This method does not handle
- * the identifiers for the {@code geoapi-pending} module.
+ * Only identifiers for the stable part of GeoAPI or for some Apache SIS classes are recognized.
+ * This method does not handle the identifiers for interfaces in the {@code geoapi-pending} module.
+ *
+ * <div class="note"><b>Future evolution:</b>
+ * when a new ISO type does not yet have a corresponding GeoAPI interface,
+ * this method may temporarily return an Apache SIS class instead until a future version can use the interface.
+ * For example {@code forStandardName("CI_Individual")} returns
+ * <code>{@linkplain org.apache.sis.metadata.iso.citation.DefaultIndividual}.class</code> in Apache SIS versions
+ * that depend on GeoAPI 3.0, but the return type may be changed to {@code Individual.class} when Apache SIS will
+ * be upgraded to GeoAPI 3.1.</div>
*
* @param identifier the ISO {@linkplain UML} identifier, or {@code null}.
* @return the GeoAPI interface, or {@code null} if the given identifier is {@code null} or unknown.