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 2014/09/13 01:30:20 UTC
svn commit: r1624681 - in /sis/branches/JDK8/core/sis-utility/src:
main/java/org/apache/sis/util/ main/java/org/apache/sis/util/iso/
test/java/org/apache/sis/test/suite/ test/java/org/apache/sis/util/iso/
Author: desruisseaux
Date: Fri Sep 12 23:30:19 2014
New Revision: 1624681
URL: http://svn.apache.org/r1624681
Log:
Complete the support of a TypeName <-> Class mapping.
Added:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java (with props)
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java (with props)
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java?rev=1624681&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -0,0 +1,64 @@
+/*
+ * 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.util;
+
+
+/**
+ * Thrown when an operation can not complete because a given name is unrecognized.
+ * The unrecognized name may be a {@link org.opengis.util.GenericName}, an
+ * {@link org.opengis.metadata.Identifier} or a {@link String} for instance.
+ *
+ * <p><b>Note:</b> in the particular case of objects created from a {@link org.opengis.util.Factory},
+ * the exception for unrecognized identifiers is rather {@link org.opengis.util.NoSuchIdentifierException}.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.5
+ * @version 0.5
+ * @module
+ */
+public class UnknownNameException extends RuntimeException {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 8184564092008827669L;
+
+ /**
+ * Constructs a new exception with no message.
+ */
+ public UnknownNameException() {
+ super();
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message.
+ *
+ * @param message The detail message, or {@code null} if none.
+ */
+ public UnknownNameException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message and cause.
+ *
+ * @param message The detail message, or {@code null} if none.
+ * @param cause The cause, or {@code null} if none.
+ */
+ public UnknownNameException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/UnsupportedImplementationException.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -20,10 +20,10 @@ import org.apache.sis.util.resources.Err
/**
- * Thrown when an operation can't use arbitrary implementation of an interface, and
- * a given instance doesn't meet the requirement. For example this exception may be
- * thrown when an operation requires an Apache SIS implementation of a
- * <A HREF="http://www.geoapi.org">GeoAPI</A> interface.
+ * Thrown when an operation can not use arbitrary implementation of an interface,
+ * and a given instance does not meet the requirement. For example this exception
+ * may be thrown when an operation requires an Apache SIS implementation of a
+ * <a href="http://www.geoapi.org">GeoAPI</a> interface.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.3 (derived from geotk-2.0)
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -415,7 +415,7 @@ public class DefaultNameFactory extends
* and {@code TypeName} objects as documented in the {@link DefaultTypeName} javadoc.
*
* <p>In order to protect against potential changes in the {@code Class} ↔ {@code TypeName} mapping, users are
- * encouraged to retrieve the {@code valueClass} by invoking the {@link DefaultTypeName#getValueClass()} method
+ * encouraged to retrieve the {@code valueClass} by invoking the {@link DefaultTypeName#toClass()} method
* instead than parsing the name.</p>
*
* @param valueClass The Java class for which to get a type name, or {@code null}.
@@ -424,31 +424,37 @@ public class DefaultNameFactory extends
* @since 0.5
*/
public TypeName toTypeName(final Class<?> valueClass) {
- if (valueClass == null) {
+ if (!TypeNames.isValid(valueClass)) {
return null;
}
/*
* Note: we do not cache the TypeName for the valueClass argument because:
- * - It is not needed (at least in the default implementation) for getting unique instance.
- * - It is not the best place for performance improvement, since TypeName are usually only
- * a step in the creation of bigger object (typically AttributeType). Users are better to
- * cache the bigger object instead.
+ *
+ * - It is not needed (at least in the default implementation) for getting unique instance.
+ * - It is not the best place for performance improvement, since TypeName are usually only
+ * a step in the creation of bigger object (typically AttributeType). Users are better to
+ * cache the bigger object instead.
*/
TypeNames t = typeNames;
if (t == null) {
- synchronized (pool) {
- /*
- * Double-check strategy is ok if 'typeNames' is volatile. We synchronize on 'pool' because
- * the TypeNames constructor will call back methods from this class, which will use the pool.
- * We are better to use only one lock for reducing the risk of dead-lock. Inconvenient is that
- * we potentially invoke user code (since our methods are overrideable) while holding the lock.
- */
+ /*
+ * Create TypeNames outide the synchronized block because the TypeNames constructor will call back
+ * methods from this class. Since those methods are overrideable, this could invoke user's code.
+ * Note also that methods in this class use the 'pool', which is itself synchronized, so we are
+ * better to avoid double synchronization for reducing the risk of dead-lock.
+ */
+ final TypeNames c = new TypeNames(this);
+ synchronized (this) { // Double-check strategy is ok if 'typeNames' is volatile.
t = typeNames;
if (t == null) {
- typeNames = t = new TypeNames(this);
+ typeNames = t = c;
}
}
}
- return t.toTypeName(this, valueClass);
+ final TypeName name = t.toTypeName(this, valueClass);
+ if (name instanceof DefaultTypeName) {
+ ((DefaultTypeName) name).valueClass = valueClass;
+ }
+ return name;
}
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -17,10 +17,10 @@
package org.apache.sis.util.iso;
import java.util.Map;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Collections;
-import java.util.Date;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
import org.opengis.util.Type;
import org.opengis.util.TypeName;
import org.opengis.util.LocalName;
@@ -30,7 +30,6 @@ import org.opengis.util.NameSpace;
import org.opengis.util.RecordSchema;
import org.opengis.util.RecordType;
import org.apache.sis.util.Debug;
-import org.apache.sis.util.Numbers;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.ObjectConverters;
@@ -54,7 +53,7 @@ import org.apache.sis.internal.converter
* by overriding the following methods:
*
* <ul>
- * <li>{@link #toTypeName(Class)}</li>
+ * <li>{@link DefaultNameFactory#toTypeName(Class)} if the factory given to the constructor.</li>
* </ul>
*
* {@section Thread safety}
@@ -86,6 +85,12 @@ public class DefaultRecordSchema impleme
protected final NameFactory nameFactory;
/**
+ * The helper class to use for mapping Java classes to {@code TypeName}Â instances, or {@code null} if not needed.
+ * This helper class is needed only if {@link #nameFactory} is not an instance of {@link DefaultNameFactory}.
+ */
+ private final TypeNames typeFactory;
+
+ /**
* The namespace of {@link RecordType} to be created by this class.
* This is also (indirectly) the {@linkplain #getSchemaName() schema name}.
*/
@@ -98,9 +103,8 @@ public class DefaultRecordSchema impleme
/**
* The pool of attribute types created so far.
- * Shall be used only in synchronized blocks.
*/
- private final Map<Class<?>,Type> attributeTypes;
+ private final ConcurrentMap<Class<?>,Type> attributeTypes;
/**
* The converter to use for converting Java {@link Class} to ISO 19103 {@link Type}.
@@ -126,9 +130,10 @@ public class DefaultRecordSchema impleme
nameFactory = DefaultFactories.NAMES;
}
this.nameFactory = nameFactory;
+ this.typeFactory = (nameFactory instanceof DefaultNameFactory) ? null : new TypeNames(nameFactory);
this.namespace = nameFactory.createNameSpace(nameFactory.createLocalName(parent, schemaName), null);
this.description = new WeakValueHashMap<>(TypeName.class);
- this.attributeTypes = new HashMap<>();
+ this.attributeTypes = new ConcurrentHashMap<>();
}
/**
@@ -189,79 +194,38 @@ public class DefaultRecordSchema impleme
}
/**
- * Suggests an attribute type for the given value class. For a short list of known classes,
- * this method returns the ISO 19103 type as used in XML documents. Examples:
- *
- * <table class="sis">
- * <caption>Attribute types for Java classes (non exhaustive list)</caption>
- * <tr><th>Java class</th> <th>Attribute type</th></tr>
- * <tr><td>{@link String}</td> <td>{@code gco:CharacterString}</td></tr>
- * <tr><td>{@link Date}</td> <td>{@code gco:DateTime}</td></tr>
- * <tr><td>{@link Double}</td> <td>{@code gco:Real}</td></tr>
- * <tr><td>{@link Integer}</td> <td>{@code gco:Integer}</td></tr>
- * <tr><td>{@link Boolean}</td> <td>{@code gco:Boolean}</td></tr>
- * </table>
+ * Suggests an attribute type for the given value class. The {@code TypeName} will use the UML identifier
+ * of OGC/ISO specification when possible, e.g. {@code "GCO:CharacterString"} for {@code java.lang.String}.
+ * See <cite>Mapping Java classes to type names</cite> in {@link DefaultTypeName} javadoc for more information.
*
* @param valueClass The value class to represent as an attribute type.
* @return Attribute type for the given value class.
*/
- private Type toAttributeType(final Class<?> valueClass) {
- Type type;
- synchronized (attributeTypes) {
- type = attributeTypes.get(valueClass);
+ final Type toAttributeType(final Class<?> valueClass) {
+ if (!TypeNames.isValid(valueClass)) {
+ return null;
}
+ Type type = attributeTypes.get(valueClass);
if (type == null) {
- type = new SimpleAttributeType<>(toTypeName(valueClass), valueClass);
- synchronized (attributeTypes) {
- final Type old = attributeTypes.put(valueClass, type);
- if (old != null) { // May happen if the type has been computed concurrently.
- attributeTypes.put(valueClass, old);
- return old;
- }
+ if (valueClass == Void.TYPE) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "valueClass", "void"));
+ }
+ final TypeName name;
+ if (nameFactory instanceof DefaultNameFactory) {
+ name = ((DefaultNameFactory) nameFactory).toTypeName(valueClass);
+ } else {
+ name = typeFactory.toTypeName(nameFactory, valueClass);
+ }
+ type = new SimpleAttributeType<>(name, valueClass);
+ final Type old = attributeTypes.putIfAbsent(valueClass, type);
+ if (old != null) { // May happen if the type has been computed concurrently.
+ return old;
}
}
return type;
}
/**
- * Suggests a type name for the given value class. For a short list of known classes,
- * this method returns the ISO 19103 type name as used in XML documents. Examples:
- *
- * <table class="sis">
- * <caption>Type names for Java classes (non exhaustive list)</caption>
- * <tr><th>Java class</th> <th>Type name</th></tr>
- * <tr><td>{@link String}</td> <td>"{@code gco:CharacterString}"</td></tr>
- * <tr><td>{@link Date}</td> <td>"{@code gco:DateTime}"</td></tr>
- * <tr><td>{@link Double}</td> <td>"{@code gco:Real}"</td></tr>
- * <tr><td>{@link Integer}</td> <td>"{@code gco:Integer}"</td></tr>
- * <tr><td>{@link Boolean}</td> <td>"{@code gco:Boolean}"</td></tr>
- * </table>
- *
- * Subclasses can override this method for defining more type names.
- *
- * @param valueClass The value class for which to get a type name.
- * @return Type name for the given value class.
- */
- protected TypeName toTypeName(final Class<?> valueClass) {
- String ns = "gco";
- final String name;
- if (CharSequence.class.isAssignableFrom(valueClass)) {
- name = "CharacterString";
- } else if (Number.class.isAssignableFrom(valueClass)) {
- name = Numbers.isInteger(valueClass) ? "Integer" : "Real";
- } else if (Date.class.isAssignableFrom(valueClass)) {
- name = "DateTime";
- } else if (valueClass == Boolean.class) {
- name = "Boolean";
- } else {
- ns = "java";
- name = valueClass.getCanonicalName();
- }
- return nameFactory.createTypeName(nameFactory.createNameSpace(
- nameFactory.createLocalName(null, ns), null), name);
- }
-
- /**
* Returns the dictionary of all (<var>name</var>, <var>record type</var>) pairs in this schema.
*
* @return All (<var>name</var>, <var>record type</var>) pairs in this schema.
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -20,7 +20,7 @@ import javax.xml.bind.annotation.XmlType
import javax.xml.bind.annotation.XmlRootElement;
import org.opengis.util.TypeName;
import org.opengis.util.NameSpace;
-import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.UnknownNameException;
/**
@@ -42,7 +42,7 @@ import org.apache.sis.util.resources.Err
* small and is sometime not an exact match.
*
* <table class="sis">
- * <caption>Mapping from Java classes to type names (non exhaustive list)</caption>
+ * <caption>Mapping from Java classes to type names (non-exhaustive list)</caption>
* <tr><th>Java class</th> <th>Type name (unofficial)</th> <th>Definition identifier in OGC namespace</th></tr>
* <tr><td>{@link org.opengis.util.InternationalString}</td> <td>{@code OGC:FreeText}</td> <td></td></tr>
* <tr><td>{@link java.lang.String}</td> <td>{@code OGC:CharacterString}</td> <td>urn:ogc:def:dataType:OGC::string</td></tr>
@@ -58,7 +58,7 @@ import org.apache.sis.util.resources.Err
*
* The mapping defined by Apache SIS may change in any future version depending on standardization progress.
* To protect against such changes, users are encouraged to rely on methods or constructors like
- * {@link DefaultNameFactory#toTypeName(Class)} or {@link #getValueClass()} instead than parsing the name.
+ * {@link DefaultNameFactory#toTypeName(Class)} or {@link #toClass()} instead than parsing the name.
*
*
* {@section Immutability and thread safety}
@@ -85,11 +85,17 @@ public class DefaultTypeName extends Def
private static final long serialVersionUID = 7182126541436753582L;
/**
- * The value class to be returned by {@link #getValueClass()}, or {@code null} if not yet computed.
- * This field is serialized because it may be specified at construction time.
+ * The value class to be returned by {@link #toClass()}, or {@code null} if not yet computed.
* {@link Void#TYPE} is used as a sentinel value meaning explicit {@code null}.
+ *
+ * <p>This value is only computed. We do not allow the user to explicitely specify it, because we
+ * need that {@code DefaultTypeName}s having identical name also have the same {@code valueClass}.
+ * This is necessary {@link DefaultNameFactory#pool} cache integrity. Users who want to explicitely
+ * specify their own value class can override {@link #toClass()} instead.</p>
+ *
+ * @see #toClass()
*/
- private Class<?> valueClass;
+ transient Class<?> valueClass;
/**
* Empty constructor to be used by JAXB only. Despite its "final" declaration,
@@ -99,37 +105,16 @@ public class DefaultTypeName extends Def
}
/**
- * Constructs a type name from the given character sequence. The argument are given unchanged
- * to the {@linkplain DefaultLocalName#DefaultLocalName(NameSpace,CharSequence) super-class
- * constructor}.
+ * Constructs a type name from the given character sequence. The argument are given unchanged to the
+ * {@linkplain DefaultLocalName#DefaultLocalName(NameSpace,CharSequence) super-class constructor}.
*
* @param scope The scope of this name, or {@code null} for a global scope.
* @param name The local name (never {@code null}).
- */
- protected DefaultTypeName(final NameSpace scope, final CharSequence name) {
- super(scope, name);
- }
-
- /**
- * Constructs a type name from the given character sequence and value class.
- * The given value class is stored for retrieval by {@link #getValueClass()}.
*
- * @param scope The scope of this name, or {@code null} for a global scope.
- * @param name The local name (never {@code null}).
- * @param valueClass The Java class associated to this {@code TypeName}, or {@code null}.
- *
- * @see DefaultNameFactory#toTypeName(Class)
- *
- * @since 0.5
+ * @see DefaultNameFactory#createTypeName(NameSpace, CharSequence)
*/
- protected DefaultTypeName(final NameSpace scope, final CharSequence name, Class<?> valueClass) {
+ protected DefaultTypeName(final NameSpace scope, final CharSequence name) {
super(scope, name);
- if (valueClass == null) {
- valueClass = Void.TYPE;
- } else if (valueClass == Void.TYPE) {
- throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "valueClass", "void"));
- }
- this.valueClass = valueClass;
}
/**
@@ -158,23 +143,48 @@ public class DefaultTypeName extends Def
}
/**
- * Returns the Java class associated to this type name. If a Java class has been explicitely specified to the
- * {@linkplain #DefaultTypeName(NameSpace, CharSequence, Class) constructor}, then that class is returned
- * (note that it may be explicitely {@code null}). Otherwise if this {@code TypeName} is as documented in
- * the <cite>Mapping Java classes to type names</cite> section of class javadoc, then the corresponding
- * Java class is returned. Otherwise this method returns {@code null}.
+ * Returns the Java class associated to this type name.
+ * The default implementation performs the following choices:
+ *
+ * <ul>
+ * <li>If the {@linkplain #scope() scope} of this name is not {@code "OGC"} or {@code "class"},
+ * then this method returns {@code null}.</li>
+ * <li>Otherwise this method interprets this {@code TypeName} as documented in the
+ * <cite>Mapping Java classes to type names</cite> section of class javadoc.
+ * The result is either a non-null class or an {@code UnknownNameException}.</li>
+ * </ul>
*
- * @return The Java class associated to this {@code TypeName}, or {@code null} if none.
+ * @return The Java class associated to this {@code TypeName}, or {@code null} if and only if
+ * the {@linkplain #scope() scope} is not one of the supported namespaces.
+ * @throws UnknownNameException if the scope is one of the supported namespaces, but the name is not recognized.
*
* @since 0.5
*/
- public Class<?> getValueClass() {
- if (valueClass == Void.TYPE) {
+ public Class<?> toClass() throws UnknownNameException {
+ /*
+ * No synchronization, because it is not a problem if two threads compute the same value concurrently.
+ * No volatile field neither, because instances of Class are safely published (well, I presume...).
+ */
+ Class<?> c = valueClass;
+ if (c == Void.TYPE) {
return null;
}
- if (valueClass == null) {
- // TODO: infer here.
+ if (c == null) {
+ /*
+ * Invoke super.foo() instead than this.foo() because we do not want to invoke any overridden method.
+ * This is for ensuring that two TypeNames constructed with the same name will map to the same class.
+ * See 'valueClass' javadoc for more information.
+ */
+ try {
+ c = TypeNames.toClass(super.scope().name().toString(), super.toString());
+ } catch (ClassNotFoundException e) {
+ throw new UnknownNameException(TypeNames.unknown(super.toFullyQualifiedName()), e);
+ }
+ if (c == null) {
+ throw new UnknownNameException(TypeNames.unknown(super.toFullyQualifiedName()));
+ }
+ valueClass = c;
}
- return valueClass;
+ return c;
}
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/Names.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -24,6 +24,7 @@ import org.opengis.util.NameSpace;
import org.opengis.util.InternationalString;
import org.apache.sis.util.Static;
import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.util.UnknownNameException;
import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -212,4 +213,49 @@ public final class Names extends Static
return new StringBuilder(ns.length() + localPart.length() + 2)
.append('{').append(ns).append('}').append(localPart).toString();
}
+
+ /**
+ * Returns the Java class associated to the given type name.
+ * The method performs the following choices:
+ *
+ * <ul>
+ * <li>If the given type is {@code null}, then this method returns {@code null}.</li>
+ * <li>Otherwise if the given type is an instance of {@code DefaultTypeName},
+ * then this method delegates to {@link DefaultTypeName#toClass()}.</li>
+ * <li>Otherwise if the {@linkplain #scope() scope} of the given name is not {@code "OGC"}
+ * or {@code "class"}, then this method returns {@code null}.</li>
+ * <li>Otherwise this method interprets the given name as documented in the
+ * <cite>Mapping Java classes to type names</cite> section of {@link DefaultTypeName} javadoc.
+ * The result is either a non-null class or an {@code UnknownNameException}.</li>
+ * </ul>
+ *
+ * @param type The type name from which to infer a Java class.
+ * @return The Java class associated to the given {@code TypeName}, or {@code null} if the given type
+ * is null or if the {@linkplain #scope() scope} is not one of the supported namespaces.
+ * @throws UnknownNameException if the scope is one of the supported namespaces, but the name is not recognized.
+ *
+ * @since 0.5
+ */
+ public static Class<?> toClass(final TypeName type) throws UnknownNameException {
+ if (type == null) {
+ return null;
+ }
+ Class<?> c;
+ if (type instanceof DefaultTypeName) {
+ c = ((DefaultTypeName) type).toClass();
+ } else {
+ try {
+ c = TypeNames.toClass(type.scope().name().toString(), type.toString());
+ } catch (ClassNotFoundException e) {
+ throw new UnknownNameException(TypeNames.unknown(type), e);
+ }
+ if (c == null) {
+ throw new UnknownNameException(TypeNames.unknown(type));
+ }
+ if (c == Void.TYPE) {
+ c = null;
+ }
+ }
+ return c;
+ }
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/TypeNames.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -17,12 +17,17 @@
package org.apache.sis.util.iso;
import java.util.Date;
+import java.util.Locale;
import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.net.URI;
+import org.opengis.util.GenericName;
import org.opengis.util.TypeName;
import org.opengis.util.NameSpace;
import org.opengis.util.NameFactory;
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.Numbers;
@@ -36,6 +41,32 @@ import org.apache.sis.util.Numbers;
*/
final class TypeNames {
/**
+ * The mapping between {@link TypeName} and {@link Class} as documented in {@link DefaultTypeName}.
+ * When searching for a name from a class, the values will be tested in iteration order.
+ *
+ * <p>{@link #toTypeName(NameFactory, Class)} will <strong>not</strong> iterate over all entries.
+ * Numbers and character strings are handled in a special way, so we do not need to iterate on them.
+ * We arbitrarily use {@code Boolean.class} as the sentinel value for detecting when to stop iteration.</p>
+ *
+ * <p>This map shall not be modified after construction.</p>
+ */
+ private static final Map<String,Class<?>> MAPPING = new LinkedHashMap<>(16);
+ static {
+ final Map<String,Class<?>> m = MAPPING;
+ m.put("URI", URI.class);
+ m.put("DateTime", Date.class);
+ m.put("PT_Locale", Locale.class);
+ m.put("Boolean", Boolean.class); // Used as a sentinel value for stopping iteration.
+
+ // Entries below this point are handled in a special way.
+ m.put("FreeText", InternationalString.class);
+ m.put("CharacterString", String.class);
+ m.put("Real", Double.class);
+ m.put("Decimal", Double.class);
+ m.put("Integer", Integer.class);
+ };
+
+ /**
* The "OGC" namespace.
*/
private final NameSpace ogcNS;
@@ -49,42 +80,101 @@ final class TypeNames {
* Creates a new factory of type names.
*/
TypeNames(final NameFactory factory) {
- final Map<String,String> properties = new HashMap<>(4);
- properties.put(DefaultNameFactory.SEPARATOR_KEY, ".");
- properties.put(DefaultNameFactory.HEAD_SEPARATOR_KEY, DefaultNameSpace.DEFAULT_SEPARATOR_STRING);
- ogcNS = factory.createNameSpace(factory.createLocalName(null, "OGC"), properties);
- classNS = factory.createNameSpace(factory.createLocalName(null, "class"), properties);
+ ogcNS = factory.createNameSpace(factory.createLocalName(null, "OGC"), null);
+ classNS = factory.createNameSpace(factory.createLocalName(null, "class"), null);
}
/**
* Infers the type name from the given class.
*
+ * @param factory The same factory than the one given to the constructor.
* @param valueClass The value class for which to get a type name.
+ * @return A type name for the given class (never {@code null}).
*/
final TypeName toTypeName(final NameFactory factory, final Class<?> valueClass) {
String name;
NameSpace ns = ogcNS;
if (CharSequence.class.isAssignableFrom(valueClass)) {
- name = "CharacterString";
+ name = InternationalString.class.isAssignableFrom(valueClass) ? "FreeText" : "CharacterString";
} else if (Number.class.isAssignableFrom(valueClass)) {
name = Numbers.isInteger(valueClass) ? "Integer" : "Real";
- } else if (Date.class.isAssignableFrom(valueClass)) {
- name = "DateTime";
- } else if (valueClass == Boolean.class) {
- name = "Boolean";
} else {
+ /*
+ * Iterate over the special cases, excluding the numbers and character sequences
+ * since they were verified in the above statements.
+ */
+ final Iterator<Map.Entry<String,Class<?>>> it = MAPPING.entrySet().iterator();
+ Class<?> base;
+ do {
+ final Map.Entry<String,Class<?>> entry = it.next();
+ base = entry.getValue();
+ if (base.isAssignableFrom(valueClass)) {
+ name = entry.getKey();
+ return factory.createTypeName(ns, name);
+ }
+ } while (base != Boolean.class); // See MAPPING javadoc for the role of Boolean as a sentinel value.
+ /*
+ * Found no special case. Checks for the UML annotation, to be also formatted in the "OGC:" namespace.
+ * If no UML identifier is found, then we will format the Java class in the "class:" namespace.
+ */
name = Types.getStandardName(valueClass);
if (name == null) {
ns = classNS;
name = valueClass.getCanonicalName();
- final int s = name.lastIndexOf('.');
- if (s >= 0) {
- ns = factory.createNameSpace(factory.parseGenericName(ns, name.substring(0, s)),
- Collections.singletonMap(DefaultNameFactory.SEPARATOR_KEY, "."));
- name = name.substring(s+1);
- }
}
}
return factory.createTypeName(ns, name);
}
+
+ /**
+ * Returns the class for the given name. This method is the converse of {@link #toTypeName(NameFactory, Class)}.
+ * This method returns 3 kind of values:
+ *
+ * <ul>
+ * <li>{@code Void.TYPE} if {@code namespace} is unrecognized.
+ * This is a sentinel value expected by {@link DefaultTypeName#toClass()} for such case.</li>
+ * <li>{@code null} if {@code namespace} is recognized, but not the {@code name}.</li>
+ * <li>Otherwise the class for the given name.</li>
+ * </ul>
+ *
+ * @param namespace The namespace ("OGC" or "class"), case-insensitive.
+ * @param name The name, case-sensitive.
+ * @return The class, or {@code Void.TYPE} if the given namespace is not recognized,
+ * or {@code null} if the namespace is recognized but not the name.
+ * @throws ClassNotFoundException if {@code namespace} is {@code "class"} but {@code name} is not
+ * the name of a reachable class.
+ */
+ static Class<?> toClass(final String namespace, final String name) throws ClassNotFoundException {
+ Class<?> c;
+ if (namespace.equalsIgnoreCase("OGC")) {
+ c = MAPPING.get(name);
+ if (c == null) {
+ c = Types.forStandardName(name);
+ }
+ } else if (namespace.equalsIgnoreCase("class")) {
+ c = Class.forName(name);
+ } else {
+ c = Void.TYPE; // Not an "OGC" or "class" namespace.
+ }
+ return c;
+ }
+
+ /**
+ * Ensures that the given class is not {@link Void#TYPE}.
+ * This is a helper method for callers of {@link #toTypeName(NameFactory, Class)}.
+ */
+ static boolean isValid(final Class<?> valueClass) {
+ if (valueClass == Void.TYPE) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "valueClass", "void"));
+ }
+ return (valueClass != null);
+ }
+
+ /**
+ * Formats the error message for an unknown type.
+ * This is a helper method for callers of {@link #toClass(String, String)}.
+ */
+ static String unknown(final GenericName name) {
+ return Errors.format(Errors.Keys.UnknownType_1, name.toFullyQualifiedName());
+ }
}
Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -86,6 +86,7 @@ import org.junit.BeforeClass;
org.apache.sis.util.iso.AbstractNameTest.class,
org.apache.sis.util.iso.DefaultNameFactoryTest.class,
org.apache.sis.util.iso.NamesTest.class,
+ org.apache.sis.util.iso.TypeNamesTest.class,
org.apache.sis.internal.simple.SimpleReferenceIdentifierTest.class,
org.apache.sis.util.iso.DefaultRecordTypeTest.class,
org.apache.sis.util.iso.DefaultRecordSchemaTest.class,
Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordSchemaTest.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -69,25 +69,25 @@ public final strictfp class DefaultRecor
switch (count) {
case 0: {
expectedName = "city";
- expectedType = "gco:CharacterString";
+ expectedType = "OGC:CharacterString";
expectedClass = String.class;
break;
}
case 1: {
expectedName = "latitude";
- expectedType = "gco:Real";
+ expectedType = "OGC:Real";
expectedClass = Double.class;
break;
}
case 2: {
expectedName = "longitude";
- expectedType = "gco:Real";
+ expectedType = "OGC:Real";
expectedClass = Double.class;
break;
}
case 3: {
expectedName = "population";
- expectedType = "gco:Integer";
+ expectedType = "OGC:Integer";
expectedClass = Integer.class;
break;
}
Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java?rev=1624681&r1=1624680&r2=1624681&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -40,7 +40,7 @@ import static org.apache.sis.test.TestUt
* @version 0.5
* @module
*/
-@DependsOn(AbstractNameTest.class)
+@DependsOn(TypeNamesTest.class)
public final strictfp class DefaultRecordTypeTest extends TestCase {
/** Value of {@link DefaultRecordType#getContainer()}. */ private DefaultRecordSchema container;
/** Value of {@link DefaultRecordType#getTypeName()}. */ private DefaultTypeName recordTypeName;
Added: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java?rev=1624681&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java [UTF-8] Fri Sep 12 23:30:19 2014
@@ -0,0 +1,130 @@
+/*
+ * 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.util.iso;
+
+
+import java.net.URI;
+import java.util.Date;
+import java.util.Locale;
+import org.opengis.util.TypeName;
+import org.opengis.util.InternationalString;
+import org.opengis.metadata.Metadata;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link TypeNames} class. Tests are performed through the {@link DefaultNameFactory#toTypeName(Class)}
+ * method on the {@link DefaultFactories#SIS_NAMES} instance.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.5
+ * @version 0.5
+ * @module
+ */
+@DependsOn({
+ TypesTest.class,
+ DefaultNameFactoryTest.class
+})
+public final strictfp class TypeNamesTest extends TestCase {
+ /**
+ * Verifies that the call to {@link TypeNames#toTypeName(NameFactory, Class)} returns a {@code TypeName} having the
+ * given name and namespace, then tests the reverse operation with {@link TypeNames#toClass(String, String)}.
+ */
+ private static void verifyLookup(final String namespace, final String name, final Class<?> valueClass)
+ throws ClassNotFoundException
+ {
+ final TypeName type = DefaultFactories.SIS_NAMES.toTypeName(valueClass);
+ assertNotNull(name, type);
+ assertSame (name, valueClass, ((DefaultTypeName) type).valueClass);
+ assertEquals (name, namespace, type.scope().name().toString());
+ assertEquals (name, name, type.toString());
+ assertEquals (name, valueClass, TypeNames.toClass(namespace, name));
+ }
+
+ /**
+ * Returns the string representation of the fully qualified path of the type name for the given value.
+ */
+ private static String toTypeName(final Class<?> valueClass) {
+ return DefaultFactories.SIS_NAMES.toTypeName(valueClass).toFullyQualifiedName().toString();
+ }
+
+ /**
+ * Tests the mapping of basic types like strings, URI, dates and numbers.
+ *
+ * @throws ClassNotFoundException Should not happen since we do invoke {@link Class#forName(String)} in this test.
+ */
+ @Test
+ public void testBasicTypes() throws ClassNotFoundException {
+ verifyLookup("OGC", "URI", URI.class);
+ verifyLookup("OGC", "PT_Locale", Locale.class);
+ verifyLookup("OGC", "DateTime", Date.class);
+ verifyLookup("OGC", "FreeText", InternationalString.class);
+ verifyLookup("OGC", "CharacterString", String.class);
+ verifyLookup("OGC", "Boolean", Boolean.class);
+ verifyLookup("OGC", "Real", Double.class);
+ verifyLookup("OGC", "Integer", Integer.class);
+ }
+
+ /**
+ * Tests {@link TypeNames#toTypeName(NameFactory, Class)} with numbers.
+ */
+ @Test
+ public void testNumbers() {
+ assertEquals("Short", "OGC:Integer", toTypeName(Short .class));
+ assertEquals("Long", "OGC:Integer", toTypeName(Long .class));
+ assertEquals("Float", "OGC:Real", toTypeName(Float .class));
+ assertEquals("Double", "OGC:Real", toTypeName(Double.class));
+ }
+
+ /**
+ * Tests the mapping of more complex object that are not basic types.
+ *
+ * @throws ClassNotFoundException Should not happen since we do invoke {@link Class#forName(String)} in this test.
+ */
+ @Test
+ public void testMetadataClasses() throws ClassNotFoundException {
+ verifyLookup("OGC", "MD_Metadata", Metadata.class);
+ verifyLookup("OGC", "SC_CRS", CoordinateReferenceSystem.class);
+ }
+
+ /**
+ * Tests the mapping of objects not defined by OGC.
+ *
+ * @throws ClassNotFoundException If the call to {@link Class#forName(String)} failed.
+ */
+ @Test
+ public void testOtherClasses() throws ClassNotFoundException {
+ verifyLookup("class", "java.util.Random", java.util.Random.class);
+ }
+
+ /**
+ * Checks for the sentinel values in case of invalid names.
+ *
+ * @throws ClassNotFoundException Should not happen since we do invoke {@link Class#forName(String)} in this test.
+ */
+ @Test
+ public void testInvalidNames() throws ClassNotFoundException {
+ assertEquals("Dummy:any", Void.TYPE, TypeNames.toClass("Dummy", "any"));
+ assertNull ("OGC:Dummy", TypeNames.toClass("OGC", "Dummy"));
+ }
+}
Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8