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 2022/11/13 16:30:43 UTC
[sis] 02/02: Save the "TypeName to Java class" association in `DefaultTypeName`.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 7b995facc0023ba36a3fb1785be1b49e2ffcd29a
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Nov 13 17:27:51 2022 +0100
Save the "TypeName to Java class" association in `DefaultTypeName`.
---
.../apache/sis/util/iso/DefaultNameFactory.java | 76 +++++---
.../org/apache/sis/util/iso/DefaultTypeName.java | 195 ++++++++++++---------
.../main/java/org/apache/sis/util/iso/Names.java | 43 +++--
.../java/org/apache/sis/util/iso/TypeNames.java | 33 ++--
.../java/org/apache/sis/util/iso/NamesTest.java | 35 ++--
.../org/apache/sis/util/iso/TypeNamesTest.java | 10 +-
.../org/apache/sis/util/UnknownNameException.java | 7 +-
7 files changed, 218 insertions(+), 181 deletions(-)
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
index a7557d787a..c093d2d7b4 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultNameFactory.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Locale;
import java.util.ArrayList;
import java.util.Collection;
+import java.lang.reflect.Type;
import org.opengis.metadata.Identifier;
import org.opengis.util.TypeName;
import org.opengis.util.NameSpace;
@@ -32,6 +33,7 @@ import org.opengis.util.NameFactory;
import org.opengis.util.InternationalString;
import org.apache.sis.util.SimpleInternationalString;
import org.apache.sis.util.DefaultInternationalString;
+import org.apache.sis.util.UnknownNameException;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.collection.WeakHashSet;
@@ -70,7 +72,7 @@ import org.apache.sis.internal.util.Strings;
* from multiple threads.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.3
*
* @see Names
* @see DefaultNameSpace
@@ -198,27 +200,50 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory {
}
/**
- * Creates a type name from the given character sequence.
+ * Creates a type name from the given character sequence and automatically inferred Java type.
* The default implementation returns a new or an existing {@link DefaultTypeName} instance.
+ * See {@link DefaultTypeName} javadoc for the list of recognized type names.
*
- * @param scope the {@linkplain AbstractName#scope() scope} of the type name to be created,
+ * @param scope the {@linkplain AbstractName#scope() scope} of the type name create,
* or {@code null} for a global namespace.
* @param name the type name as a string or an international string.
- * @return the type name for the given character sequence.
+ * @return the type name for the given scope and character sequence.
+ * @throws UnknownNameException if a mapping from the name to a Java class was expected to exist
+ * (because the specified scope is "OGC" or "class") but the associated Java class can not be found.
*
* @see #toTypeName(Class)
+ * @see DefaultTypeName#DefaultTypeName(NameSpace, CharSequence)
* @see Names#createTypeName(CharSequence, String, CharSequence)
*/
@Override
- public TypeName createTypeName(final NameSpace scope, final CharSequence name) {
+ public TypeName createTypeName(final NameSpace scope, final CharSequence name) throws UnknownNameException {
return pool.unique(new DefaultTypeName(scope, name));
}
+ /**
+ * Creates a type name from the given character sequence and explicit Java type.
+ * The default implementation returns a new or an existing {@link DefaultTypeName} instance.
+ *
+ * @param scope the {@linkplain AbstractName#scope() scope} of the type name to create,
+ * or {@code null} for a global namespace.
+ * @param name the type name as a string or an international string.
+ * @param javaType the value type to be returned by {@link #toJavaType()}, or {@code null} if none.
+ * @return the type name for the given scope, character sequence and Java type.
+ *
+ * @see #toTypeName(Class)
+ * @see DefaultTypeName#DefaultTypeName(NameSpace, CharSequence, Type)
+ *
+ * @since 1.3
+ */
+ public TypeName createTypeName(final NameSpace scope, final CharSequence name, final Type javaType) {
+ return pool.unique(new DefaultTypeName(scope, name, javaType));
+ }
+
/**
* Creates a member name from the given character sequence and attribute type.
* The default implementation returns a new or an existing {@link DefaultMemberName} instance.
*
- * @param scope the {@linkplain AbstractName#scope() scope} of the member name to be created,
+ * @param scope the {@linkplain AbstractName#scope() scope} of the member name to create,
* or {@code null} for a global namespace.
* @param name the member name as a string or an international string.
* @param attributeType the type of the data associated with the record member.
@@ -235,7 +260,7 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory {
* Creates a local name from the given character sequence.
* The default implementation returns a new or an existing {@link DefaultLocalName} instance.
*
- * @param scope the {@linkplain AbstractName#scope() scope} of the local name to be created,
+ * @param scope the {@linkplain AbstractName#scope() scope} of the local name to create,
* or {@code null} for a global namespace.
* @param name the local name as a string or an international string.
* @return the local name for the given character sequence.
@@ -261,7 +286,7 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory {
* array is 1, or an instance of {@link DefaultScopedName} if the length of the array is 2
* or more.
*
- * @param scope the {@linkplain AbstractName#scope() scope} of the generic name to be created,
+ * @param scope the {@linkplain AbstractName#scope() scope} of the generic name to create,
* or {@code null} for a global namespace.
* @param parsedNames the local names as an array of {@link String} or {@link InternationalString} instances.
* This array shall contain at least one element.
@@ -283,7 +308,7 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory {
* This method splits the given name around a separator inferred from the given scope, or the
* {@link DefaultNameSpace#DEFAULT_SEPARATOR ':'} separator if the given scope is null.
*
- * @param scope the {@linkplain AbstractName#scope() scope} of the generic name to be created,
+ * @param scope the {@linkplain AbstractName#scope() scope} of the generic name to create,
* or {@code null} for a global namespace.
* @param name the qualified name, as a sequence of names separated by a scope-dependent separator.
* @return a name parsed from the given string.
@@ -409,15 +434,12 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory {
/**
* Suggests a type name for the given class. Apache SIS provides a mapping between {@code Class}
* 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 Names#toClass(TypeName)} method instead
- * than parsing the name.</p>
+ * The given {@code valueClass} can be fetched back by {@link DefaultTypeName#toJavaType()}.
*
* @param valueClass the Java class for which to get a type name, or {@code null}.
* @return a suggested type name, or {@code null} if the given class was null.
*
- * @see DefaultTypeName#toClass()
+ * @see DefaultTypeName#toJavaType()
* @see Names#toClass(TypeName)
* @see Names#createTypeName(Class)
*
@@ -431,26 +453,26 @@ public class DefaultNameFactory extends AbstractFactory implements NameFactory {
* 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 the best place for performance improvement, because `TypeName` is usually
+ * only a step in the creation of bigger object (typically `AttributeType`).
+ * Callers should cache the bigger object instead.
*/
- TypeNames t = typeNames;
- if (t == null) {
+ TypeNames mapper = typeNames;
+ if (mapper == null) {
/*
- * 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.
+ * Create TypeNames outside the synchronized block because the TypeNames constructor will call back
+ * methods from this class. Since those methods are overrideable, they 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 = c;
+ mapper = typeNames;
+ if (mapper == null) {
+ typeNames = mapper = c;
}
}
}
- return t.toTypeName(this, valueClass);
+ return mapper.toTypeName(this, valueClass);
}
}
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
index deaca620e4..7e00cfc999 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultTypeName.java
@@ -16,6 +16,9 @@
*/
package org.apache.sis.util.iso;
+import java.util.Objects;
+import java.util.Optional;
+import java.lang.reflect.Type;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlRootElement;
import org.opengis.util.TypeName;
@@ -33,7 +36,7 @@ import org.apache.sis.util.UnknownNameException;
* </ul>
*
* <h2>Mapping Java classes to type names</h2>
- * It is sometime useful to establish a mapping between {@link Class} and {@code TypeName}.
+ * A bidirectional mapping is defined between {@code TypeName} and Java {@link Class}.
* When an UML identifier from an OGC standard exists for a given {@code Class},
* Apache SIS uses that identifier prefixed by the {@code "OGC"} namespace.
* Note that this is <strong>not</strong> a standard practice.
@@ -114,7 +117,7 @@ import org.apache.sis.util.UnknownNameException;
*
* 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 #toClass()} instead of parsing the name.
+ * {@link #toJavaType()} or {@link DefaultNameFactory#toTypeName(Class)} instead of parsing the name.
*
*
* <h2>Immutability and thread safety</h2>
@@ -125,7 +128,7 @@ import org.apache.sis.util.UnknownNameException;
* @author Guilhem Legal (Geomatys)
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.3
*
* @see DefaultMemberName
* @see DefaultNameFactory
@@ -139,33 +142,85 @@ public class DefaultTypeName extends DefaultLocalName implements TypeName {
/**
* Serial number for inter-operability with different versions.
*/
- private static final long serialVersionUID = 7182126541436753582L;
+ private static final long serialVersionUID = 7571710679743017926L;
/**
- * 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}.
+ * The value returned by {@link #toJavaType()}, or {@code null} if none.
+ * This is usually a {@link Class}, which is serializable.
+ */
+ @SuppressWarnings("serial") // Not statically typed as Serializable.
+ private final Type javaType;
+
+ /**
+ * Constructs a type name from the given character sequence and infers automatically a Java type.
+ * The scope and name arguments are given unchanged to the
+ * {@linkplain DefaultLocalName#DefaultLocalName(NameSpace,CharSequence) super-class constructor}.
+ * Then the Java type is inferred in a way that depends on the specified scope:
+ *
+ * <ul>
+ * <li>If the scope is {@code "OGC"}, then:
+ * <ul>
+ * <li>If the name is {@code "CharacterString"}, {@code "Integer"}, {@code "Real"} or other recognized names
+ * (see {@linkplain DefaultTypeName class javadoc}),
+ * then the corresponding Java class is associated to this type name.</li>
+ * <li>Otherwise {@link UnknownNameException} is thrown.</li>
+ * </ul>
+ * </li>
+ * <li>Else if the scope is {@code "class"}, then:
+ * <ul>
+ * <li>If the name is accepted by {@link Class#forName(String)},
+ * then that Java class is associated to this type name.</li>
+ * <li>Otherwise {@link UnknownNameException} is thrown.</li>
+ * </ul>
+ * </li>
+ * <li>Else if the scope {@linkplain DefaultNameSpace#isGlobal() is global}, then:
+ * <ul>
+ * <li>If the name is one of the names recognized in {@code "OGC"} scope (see above),
+ * then the corresponding class is associated to this type name.</li>
+ * <li>Otherwise no Java class is associated to this type name.
+ * No exception is thrown because names in the global namespace could be anything;
+ * this constructor can not know if the given name was wrong.</li>
+ * </ul>
+ * </li>
+ * <li>Otherwise no Java class is associated to this type name,
+ * because this method can not check the validity of names in other namespaces.</li>
+ * </ul>
*
- * <p>This value is only computed. We do not allow the user to explicitly 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 explicitly
- * specify their own value class can override {@link #toClass()} instead.</p>
+ * @param scope the scope of this name, or {@code null} for a global scope.
+ * @param name the local name (never {@code null}).
+ * @throws UnknownNameException if a mapping from this name to a Java class was expected to exist
+ * (because the specified scope is "OGC" or "class") but the associated Java class can not be found.
*
- * @see #setValueClass(NameSpace, String, Class)
- * @see #toClass()
+ * @see DefaultNameFactory#createTypeName(NameSpace, CharSequence)
*/
- private transient Class<?> valueClass;
+ protected DefaultTypeName(final NameSpace scope, final CharSequence name) throws UnknownNameException {
+ super(scope, name);
+ try {
+ javaType = TypeNames.toClass(TypeNames.namespace(scope), super.toString());
+ } catch (ClassNotFoundException e) {
+ throw new UnknownNameException(TypeNames.unknown(super.toFullyQualifiedName()), e);
+ }
+ if (javaType == Void.TYPE) {
+ throw new UnknownNameException(TypeNames.unknown(super.toFullyQualifiedName()));
+ }
+ }
/**
- * Constructs a type name from the given character sequence. The argument are given unchanged to the
+ * Constructs a type name from the given character sequence and explicit Java type.
+ * The scope and name arguments 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}).
+ * @param scope the scope of this name, or {@code null} for a global scope.
+ * @param name the local name (never {@code null}).
+ * @param javaType the value type to be returned by {@link #toJavaType()}, or {@code null} if none.
*
- * @see DefaultNameFactory#createTypeName(NameSpace, CharSequence)
+ * @see DefaultNameFactory#createTypeName(NameSpace, CharSequence, Type)
+ *
+ * @since 1.3
*/
- protected DefaultTypeName(final NameSpace scope, final CharSequence name) {
+ protected DefaultTypeName(final NameSpace scope, final CharSequence name, final Type javaType) {
super(scope, name);
+ this.javaType = javaType;
}
/**
@@ -190,88 +245,59 @@ public class DefaultTypeName extends DefaultLocalName implements TypeName {
if (object == null || object instanceof DefaultTypeName) {
return (DefaultTypeName) object;
}
- return new DefaultTypeName(object.scope(), object.toInternationalString());
+ return new DefaultTypeName(object.scope(), object.toInternationalString(), object.toJavaType().orElse(null));
}
/**
- * Sets {@link #valueClass} to the given value, only if the scope and the name of this {@code TypeName}
- * are equal to the given values. The check for scope and name is a protection against renaming that user
- * could apply if they subclass {@link DefaultNameFactory}. If the user performed such renaming, then the
- * value class may be wrong, so we will ignore the given value class and let {@link #toClass()} computes
- * the class itself.
+ * Returns the Java type represented by this name.
+ * This is the type either specified explicitly at construction time or inferred from the type name.
+ *
+ * @return the Java type (usually a {@link Class}) for this type name.
+ *
+ * @see Names#toClass(TypeName)
+ *
+ * @since 1.3
*/
- final void setValueClass(final NameSpace scope, final String name, final Class<?> valueClass) {
- if (scope == super.scope() && name.equals(super.toString())) {
- this.valueClass = valueClass;
- }
+ @Override
+ public Optional<Type> toJavaType() {
+ return Optional.ofNullable(javaType);
}
/**
* Returns the Java class associated to this type name.
- * The default implementation parses this name in different ways depending on the {@linkplain #scope() scope}:
*
- * <ul>
- * <li>If the scope is {@code "OGC"}, then:
- * <ul>
- * <li>If the name is {@code "CharacterString"}, {@code "Integer"}, {@code "Real"} or other recognized names
- * (see {@linkplain DefaultTypeName class javadoc}), then the corresponding class is returned.</li>
- * <li>Otherwise {@link UnknownNameException} is thrown.</li>
- * </ul>
- * </li>
- * <li>Else if the scope is {@code "class"}, then:
- * <ul>
- * <li>If the name is accepted by {@link Class#forName(String)}, then that class is returned.</li>
- * <li>Otherwise {@link UnknownNameException} is thrown.</li>
- * </ul>
- * </li>
- * <li>Else if the scope {@linkplain DefaultNameSpace#isGlobal() is global}, then:
- * <ul>
- * <li>If the name is one of the names recognized in {@code "OGC"} scope (see above),
- * then the corresponding class is returned.</li>
- * <li>Otherwise {@code null} is returned. No exception is thrown because names in the global namespace
- * could be anything, so we can not be sure that the given name was wrong.</li>
- * </ul>
- * </li>
- * <li>Otherwise {@code null} is returned, since this method can not check the validity of names in other
- * namespaces.</li>
- * </ul>
+ * @deprecated Replaced by {@link #toJavaType()}.
*
* @return the Java class associated to this {@code TypeName},
* or {@code null} if there is no mapping from this name to a Java class.
- * @throws UnknownNameException if a mapping from this name to a Java class was expected to exist
- * (typically because of the {@linkplain #scope() scope}) but the operation failed.
- *
- * @see Names#toClass(TypeName)
- * @see DefaultNameFactory#toTypeName(Class)
*
* @since 0.5
*/
- 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 (c == null) {
- /*
- * Invoke super.foo() instead of 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(TypeNames.namespace(super.scope()), 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;
+ @Deprecated
+ public Class<?> toClass() {
+ return (javaType instanceof Class<?>) ? (Class<?>) javaType : null;
+ }
+
+ /**
+ * Compares this type name with the specified object for equality.
+ *
+ * @param object the object to compare with this type for equality.
+ * @return {@code true} if the given object is equal to this name.
+ */
+ @Override
+ public boolean equals(final Object object) {
+ if (super.equals(object)) {
+ return Objects.equals(javaType, ((DefaultTypeName) object).javaType);
}
- return (c != Void.TYPE) ? c : null;
+ return false;
+ }
+
+ /**
+ * Invoked by {@link #hashCode()} for computing the hash code value when first needed.
+ */
+ @Override
+ int computeHashCode() {
+ return super.computeHashCode() ^ Objects.hashCode(javaType);
}
@@ -293,5 +319,6 @@ public class DefaultTypeName extends DefaultLocalName implements TypeName {
* the {@link #name} field will be set by JAXB during unmarshalling.
*/
private DefaultTypeName() {
+ javaType = null;
}
}
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Names.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Names.java
index cdce2bb011..8e80469e5a 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Names.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Names.java
@@ -17,6 +17,7 @@
package org.apache.sis.util.iso;
import java.util.Collections;
+import java.lang.reflect.Type;
import org.opengis.util.TypeName;
import org.opengis.util.LocalName;
import org.opengis.util.MemberName;
@@ -292,7 +293,7 @@ public final class Names extends Static {
ensureNonNull("valueClass", valueClass);
final DefaultNameFactory factory = DefaultFactories.forBuildin(NameFactory.class, DefaultNameFactory.class);
return factory.createMemberName(createNameSpace(factory, namespace, separator), localPart,
- factory.toTypeName(valueClass)); // SIS-specific method.
+ factory.toTypeName(valueClass)); // SIS-specific method.
}
/**
@@ -338,8 +339,7 @@ public final class Names extends Static {
*
* <ul>
* <li>If the given type name is {@code null}, then this method returns {@code null}.</li>
- * <li>Else if the given type name is an instance of {@code DefaultTypeName},
- * then this method delegates to {@link DefaultTypeName#toClass()}.</li>
+ * <li>Else if the value returned by {@link DefaultTypeName#toJavaType()} is a {@link Class}, returns that class.</li>
* <li>Else if the type name {@linkplain DefaultTypeName#scope() scope} is {@code "OGC"}, then:
* <ul>
* <li>If the name is {@code "CharacterString"}, {@code "Integer"}, {@code "Real"} or other recognized names
@@ -358,21 +358,21 @@ public final class Names extends Static {
* <li>If the name is one of the names recognized in {@code "OGC"} scope (see above),
* then the corresponding class is returned.</li>
* <li>Otherwise {@code null} is returned. No exception is thrown because names in the global namespace
- * could be anything, so we can not be sure that the given name was wrong.</li>
+ * could be anything; this method can not be sure that the given name was wrong.</li>
* </ul>
* </li>
- * <li>Otherwise {@code null} is returned, since this method can not check the validity of names in other
- * namespaces.</li>
+ * <li>Otherwise {@code null} is returned,
+ * because this method can not check the validity of names in other namespaces.</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 there is no mapping from the given name to a Java class.
* @throws UnknownNameException if a mapping from the given name to a Java class was expected to exist
- * (typically because of the {@linkplain DefaultTypeName#scope() scope}) but the operation failed.
+ * (typically because of the {@linkplain DefaultTypeName#scope() scope}) but the lookup failed.
*
* @see #createTypeName(Class)
- * @see DefaultTypeName#toClass()
+ * @see DefaultTypeName#toJavaType()
*
* @since 0.5
*/
@@ -380,21 +380,18 @@ public final class Names extends Static {
if (type == null) {
return null;
}
- Class<?> c;
- if (type instanceof DefaultTypeName) {
- c = ((DefaultTypeName) type).toClass();
- } else {
- try {
- c = TypeNames.toClass(TypeNames.namespace(type.scope()), 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;
- }
+ final Type t = type.toJavaType().orElse(null);
+ if (t instanceof Class<?>) {
+ return (Class<?>) t;
+ }
+ final Class<?> c;
+ try {
+ c = TypeNames.toClass(TypeNames.namespace(type.scope()), type.toString());
+ } catch (ClassNotFoundException e) {
+ throw new UnknownNameException(TypeNames.unknown(type), e);
+ }
+ if (c == Void.TYPE) {
+ throw new UnknownNameException(TypeNames.unknown(type));
}
return c;
}
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/TypeNames.java b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/TypeNames.java
index 2c28bad824..356d520f14 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/TypeNames.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/TypeNames.java
@@ -127,34 +127,29 @@ search: if (CharSequence.class.isAssignableFrom(valueClass)) {
name = valueClass.getName(); // See above comment.
}
}
- /*
- * Now create the name and remember the `valueClass` for that name if the implementation allows that.
- */
- final TypeName t = factory.createTypeName(ns, name);
- if (t instanceof DefaultTypeName) {
- ((DefaultTypeName) t).setValueClass(ns, name, valueClass);
+ if (factory instanceof DefaultNameFactory) {
+ return ((DefaultNameFactory) factory).createTypeName(ns, name, valueClass);
}
- return t;
+ return factory.createTypeName(ns, name);
}
/**
* Returns the class for a {@code TypeName} made of the given scope and name.
* This method is the converse of {@link #toTypeName(NameFactory, Class)}.
- * This method returns 3 kind of values:
+ * There is 3 kinds of return value:
*
* <ul>
- * <li>{@code Void.TYPE} if the namespace or the name is unrecognized, without considering that as an error.
- * 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}.
- * This will be considered as an error by {@link DefaultTypeName#toClass()}.</li>
+ * <li>{@code null} if the namespace or the name is unrecognized, without considering that as an error.</li>
+ * <li>{@code Void.TYPE} if {@code namespace} is recognized, but not the {@code name}.
+ * This is a sentinel value to be considered as an error by {@link DefaultTypeName} constructor.</li>
* <li>Otherwise the class for the given name.</li>
* </ul>
*
* @param namespace the namespace, case-insensitive. Can be any value, but this method recognizes
* only {@code "OGC"}, {@code "class"} and {@code null}. Other namespaces will be ignored.
- * @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.
+ * @param name the type name, case-sensitive.
+ * @return the class, or {@code null} if the given namespace is not recognized,
+ * or {@code Void.TYPE} if the namespace is recognized but not the type name.
* @throws ClassNotFoundException if {@code namespace} is {@code "class"} but {@code name} is not
* the name of a reachable class.
*/
@@ -164,20 +159,20 @@ search: if (CharSequence.class.isAssignableFrom(valueClass)) {
c = MAPPING.get(name);
if (c == null) {
c = Types.forStandardName(name);
- if (c == null && namespace == null) {
- c = Void.TYPE; // Unknown name not considered an error if not in "OGC" namespace.
+ if (c == null && namespace != null) {
+ c = Void.TYPE; // Unknown name in OGC namespace.
}
}
} else if (namespace.equalsIgnoreCase("class")) {
c = Class.forName(name);
} else {
- c = Void.TYPE; // Not an "OGC" or "class" namespace.
+ c = null; // Not an "OGC" or "class" namespace.
}
return c;
}
/**
- * Ensures that the given class is not {@link Void#TYPE}.
+ * Ensures that the given class is non-null and not {@link Void#TYPE}.
* This is a helper method for callers of {@link #toTypeName(NameFactory, Class)}.
*/
static boolean isValid(final Class<?> valueClass) {
diff --git a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/NamesTest.java b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/NamesTest.java
index 454f71e0b2..2463f844de 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/NamesTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/NamesTest.java
@@ -39,7 +39,7 @@ import static org.junit.Assert.*;
* Tests the {@link Names} class.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.3
* @since 0.5
* @module
*/
@@ -84,8 +84,12 @@ public final strictfp class NamesTest extends TestCase {
assertValueClassEquals(Random.class, type);
assertValueClassEquals(DefaultNameFactoryTest.class,
new DefaultTypeName(type.scope(), DefaultNameFactoryTest.class.getName()));
- assertValueClassEquals(UnknownNameException.class,
- new DefaultTypeName(type.scope(), "org.apache.sis.Dummy"));
+ try {
+ new DefaultTypeName(type.scope(), "org.apache.sis.Dummy");
+ fail("Expected UnknownNameException.");
+ } catch (UnknownNameException e) {
+ assertTrue(e.getMessage().contains("org.apache.sis.Dummy"));
+ }
}
/**
@@ -100,7 +104,12 @@ public final strictfp class NamesTest extends TestCase {
assertValueClassEquals(String.class, type);
assertValueClassEquals(Double.class, new DefaultTypeName(type.scope(), "Real"));
assertValueClassEquals(InternationalString.class, new DefaultTypeName(type.scope(), "FreeText"));
- assertValueClassEquals(UnknownNameException.class, new DefaultTypeName(type.scope(), "Dummy"));
+ try {
+ new DefaultTypeName(type.scope(), "Dummy");
+ fail("Expected UnknownNameException.");
+ } catch (UnknownNameException e) {
+ assertTrue(e.getMessage().contains("OGC:Dummy"));
+ }
}
/**
@@ -116,29 +125,15 @@ public final strictfp class NamesTest extends TestCase {
assertValueClassEquals(null, Names.createTypeName(null, null, "Dummy"));
}
- /**
- * Invokes {@link Names#toClass(TypeName)}, but catch {@link UnknownNameException}.
- * If the latter exception is caught, then this method returns {@code UnknownNameException.class}.
- */
- private static Class<?> toClass(final TypeName type) {
- try {
- return Names.toClass(type);
- } catch (UnknownNameException e) {
- final String message = e.getMessage();
- assertTrue(message, message.contains(type.toFullyQualifiedName().toString()));
- return UnknownNameException.class;
- }
- }
-
/**
* Asserts that calls to {@link Names#toClass(TypeName)} returns the expected value class.
*/
private static void assertValueClassEquals(final Class<?> expected, final TypeName type) {
- assertEquals(expected, toClass(type));
+ assertEquals(expected, Names.toClass(type));
/*
* Tests detection with an implementation which is not the SIS one.
*/
- assertEquals(expected, toClass(new TypeName() {
+ assertEquals(expected, Names.toClass(new TypeName() {
@Override public int depth() {return type.depth();}
@Override public List<? extends LocalName> getParsedNames() {return type.getParsedNames();}
@Override public LocalName head() {return type.head();}
diff --git a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
index c217af0338..c15347b357 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypeNamesTest.java
@@ -38,7 +38,7 @@ import static org.apache.sis.internal.util.Constants.OGC;
* Tests are performed through the {@link DefaultNameFactory#toTypeName(Class)} method.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.3
* @since 0.5
* @module
*/
@@ -57,7 +57,7 @@ public final strictfp class TypeNamesTest extends TestCase {
final DefaultNameFactory factory = DefaultFactories.forBuildin(NameFactory.class, DefaultNameFactory.class);
final TypeName type = factory.toTypeName(valueClass);
assertNotNull(name, type);
- assertSame (name, valueClass, ((DefaultTypeName) type).toClass());
+ assertSame (name, valueClass, type.toJavaType().get());
assertEquals (name, namespace, type.scope().name().toString());
assertEquals (name, name, type.toString());
assertEquals (name, valueClass, TypeNames.toClass(namespace, name));
@@ -127,10 +127,10 @@ public final strictfp class TypeNamesTest extends TestCase {
*/
@Test
public void testInvalidNames() throws ClassNotFoundException {
- assertEquals("Dummy:Real", Void.TYPE, TypeNames.toClass("Dummy", "Real"));
+ assertNull ("Dummy:Real", TypeNames.toClass("Dummy", "Real"));
assertEquals(OGC+":Real", Double.class, TypeNames.toClass(OGC, "Real"));
assertEquals("Real", Double.class, TypeNames.toClass(null, "Real"));
- assertEquals("Dummy", Void.TYPE, TypeNames.toClass(null, "Dummy")); // Considered not an error.
- assertNull (OGC+":Dummy", TypeNames.toClass(OGC, "Dummy")); // Considered an error.
+ assertNull ("Dummy", TypeNames.toClass(null, "Dummy")); // Considered not an error.
+ assertEquals(OGC+":Dummy", Void.TYPE, TypeNames.toClass(OGC, "Dummy")); // Considered an error.
}
}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java b/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
index 824a817fa8..6ce8c284ab 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/UnknownNameException.java
@@ -24,14 +24,15 @@ package org.apache.sis.util;
* or any other objects with similar purpose.
*
* <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>
+ * the exception for unrecognized identifiers is rather {@link org.opengis.util.NoSuchIdentifierException}.
+ * This {@code UnknownNameException} differs in being an unchecked exception.</p>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.5
+ * @version 1.3
* @since 0.5
* @module
*/
-public class UnknownNameException extends RuntimeException {
+public class UnknownNameException extends IllegalArgumentException {
/**
* For cross-version compatibility.
*/