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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 4f2555bbe9 Add a `Classes.boundOfParameterizedDeclaration(…)\ method
and use it for making `ParameterDescriptor.getValueType()` conform to
specification, which is to return the type elements when using array or
collection.
4f2555bbe9 is described below
commit 4f2555bbe92561145f254f243ad1a6d526c22d87
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Nov 12 21:14:51 2022 +0100
Add a `Classes.boundOfParameterizedDeclaration(…)\ method and use it for
making `ParameterDescriptor.getValueType()` conform to specification,
which is to return the type elements when using array or collection.
---
.../org/apache/sis/feature/AbstractAttribute.java | 3 +-
.../apache/sis/feature/DefaultAttributeType.java | 3 +-
.../java/org/apache/sis/util/iso/TypeNames.java | 8 +-
.../sis/parameter/DefaultParameterDescriptor.java | 18 +-
.../parameter/DefaultParameterDescriptorTest.java | 50 +++---
.../src/main/java/org/apache/sis/util/Classes.java | 190 +++++++++++++++------
.../test/java/org/apache/sis/util/ClassesTest.java | 86 +++++++---
7 files changed, 251 insertions(+), 107 deletions(-)
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
b/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
index e897bb4829..ea8ec1c0ab 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
@@ -69,7 +69,8 @@ import org.opengis.feature.MultiValuedPropertyException;
* @author Martin Desruisseaux (Geomatys)
* @version 0.8
*
- * @param <V> the type of attribute values.
+ * @param <V> the type of attribute values. If the attribute supports
multi-occurrences,
+ * then this is the type of elements (not the collection type).
*
* @see AbstractFeature
* @see DefaultAttributeType
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
b/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
index 2c1fc7a681..65788e7c41 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
@@ -97,7 +97,8 @@ import org.opengis.feature.AttributeType;
* @author Martin Desruisseaux (Geomatys)
* @version 0.8
*
- * @param <V> the type of attribute values.
+ * @param <V> the type of attribute values. If the attribute supports
multi-occurrences,
+ * then this is the type of elements (not the collection type).
*
* @see DefaultFeatureType
* @see AbstractAttribute
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 48325ec7d5..a4da980c77 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
@@ -37,7 +37,7 @@ import org.apache.sis.util.Numbers;
* Implements the mapping between {@link Class} and {@link TypeName}
documented in {@link DefaultTypeName}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.3
* @since 0.5
* @module
*/
@@ -97,9 +97,9 @@ final class TypeNames {
final TypeName toTypeName(final NameFactory factory, final Class<?>
valueClass) {
String name;
NameSpace ns = ogcNS;
- if (CharSequence.class.isAssignableFrom(valueClass)) {
+search: if (CharSequence.class.isAssignableFrom(valueClass)) {
name = InternationalString.class.isAssignableFrom(valueClass) ?
"FreeText" : "CharacterString";
- } else if (Number.class.isAssignableFrom(valueClass)) {
+ } else if (Numbers.isNumber(valueClass) ||
Number.class.isAssignableFrom(valueClass)) {
name = Numbers.isInteger(valueClass) ? "Integer" : "Real";
} else {
/*
@@ -113,7 +113,7 @@ final class TypeNames {
base = entry.getValue();
if (base.isAssignableFrom(valueClass)) {
name = entry.getKey();
- return factory.createTypeName(ns, name);
+ break search;
}
} while (base != Boolean.class); // See MAPPING javadoc for the
role of Boolean as a sentinel value.
/*
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
index e73d118369..16cc89e3a7 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
@@ -316,14 +316,28 @@ public class DefaultParameterDescriptor<T> extends
AbstractParameterDescriptor i
/**
* Returns the name that describes the type of parameter values.
+ * This is closely related to the {@link Class} returned by {@link
#getValueClass()}:
*
- * @return value type of the parameter.
+ * <ul>
+ * <li>If the value class is a collection ({@link java.util.Map}, {@link
Set}, {@link java.util.List} or array),
+ * then this method returns the type of <em>elements</em> in the
collection.</li>
+ * <li>Otherwise this method returns the value class using the mapping
documented in
+ * {@link org.apache.sis.util.iso.DefaultTypeName} javadoc.</li>
+ * </ul>
+ *
+ * @return the type name of value component(s) in this parameter.
*
* @since 1.3
*/
@Override
public final TypeName getValueType() {
- return Names.createTypeName(valueClass);
+ Class<?> type = valueClass;
+ if (Iterable.class.isAssignableFrom(type) ||
Map.class.isAssignableFrom(type)) {
+ type = Classes.boundOfParameterizedDeclaration(valueClass);
+ } else if (type.isArray()) {
+ type = type.getComponentType();
+ }
+ return Names.createTypeName(type);
}
/**
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java
index 6aef74bef8..3667a0ff5b 100644
---
a/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java
+++
b/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java
@@ -42,7 +42,7 @@ import static org.apache.sis.test.ReferencingAssert.*;
* Tests the {@link DefaultParameterDescriptor} class.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.3
* @since 0.4
* @module
*/
@@ -159,6 +159,7 @@ public final strictfp class DefaultParameterDescriptorTest
extends TestCase {
public void testOptionalInteger() {
final ParameterDescriptor<Integer> descriptor =
createSimpleOptional("Simple param", Integer.class);
assertEquals("name", "Simple param",
descriptor.getName().getCode());
+ assertEquals("valueType", "Integer",
descriptor.getValueType().toString());
assertEquals("valueClass", Integer.class, descriptor.getValueClass());
assertNull ("validValues", descriptor.getValidValues());
assertNull ("defaultValue",
descriptor.getDefaultValue());
@@ -184,6 +185,7 @@ public final strictfp class DefaultParameterDescriptorTest
extends TestCase {
}
final ParameterDescriptor<Integer> descriptor = create("Test range",
4, 20, 12);
assertEquals("name", "Test range",
descriptor.getName().getCode());
+ assertEquals("valueType", "Integer",
descriptor.getValueType().toString());
assertEquals("valueClass", Integer.class,
descriptor.getValueClass());
assertNull ("validValues",
descriptor.getValidValues());
assertEquals("defaultValue", Integer.valueOf(12),
descriptor.getDefaultValue());
@@ -215,11 +217,12 @@ public final strictfp class
DefaultParameterDescriptorTest extends TestCase {
public void testDoubleType() {
final ParameterDescriptor<Double> descriptor = create("Length
measure", 4, 20, 12, Units.METRE);
assertEquals("name", "Length measure",
descriptor.getName().getCode());
- assertEquals("unit", Units.METRE,
descriptor.getUnit());
+ assertEquals("valueType", "Real",
descriptor.getValueType().toString());
assertEquals("class", Double.class,
descriptor.getValueClass());
assertEquals("defaultValue", Double.valueOf(12),
descriptor.getDefaultValue());
assertEquals("minimum", Double.valueOf( 4),
descriptor.getMinimumValue());
assertEquals("maximum", Double.valueOf(20),
descriptor.getMaximumValue());
+ assertEquals("unit", Units.METRE, descriptor.getUnit());
validate(descriptor);
}
@@ -231,16 +234,17 @@ public final strictfp class
DefaultParameterDescriptorTest extends TestCase {
final Range<String> valueDomain = new Range<>(String.class, "AAA",
true, "BBB", true);
final DefaultParameterDescriptor<String> descriptor = new
DefaultParameterDescriptor<>(
properties("String param"), 0, 1, String.class, valueDomain,
null, "ABC");
- assertEquals("name", "String param",
descriptor.getName().getCode());
- assertEquals("valueClass", String.class, descriptor.getValueClass());
- assertNull ("validValues", descriptor.getValidValues());
- assertSame ("valueDomain", valueDomain, descriptor.getValueDomain());
- assertEquals("defaultValue", "ABC", descriptor.getDefaultValue());
- assertEquals("minimumValue", "AAA", descriptor.getMinimumValue());
- assertEquals("maximumValue", "BBB", descriptor.getMaximumValue());
- assertEquals("minimumOccurs", 0,
descriptor.getMinimumOccurs());
- assertEquals("maximumOccurs", 1,
descriptor.getMaximumOccurs());
- assertNull ("unit", descriptor.getUnit());
+ assertEquals("name", "String param",
descriptor.getName().getCode());
+ assertEquals("valueType", "CharacterString",
descriptor.getValueType().toString());
+ assertEquals("valueClass", String.class,
descriptor.getValueClass());
+ assertNull ("validValues",
descriptor.getValidValues());
+ assertSame ("valueDomain", valueDomain,
descriptor.getValueDomain());
+ assertEquals("defaultValue", "ABC",
descriptor.getDefaultValue());
+ assertEquals("minimumValue", "AAA",
descriptor.getMinimumValue());
+ assertEquals("maximumValue", "BBB",
descriptor.getMaximumValue());
+ assertEquals("minimumOccurs", 0,
descriptor.getMinimumOccurs());
+ assertEquals("maximumOccurs", 1,
descriptor.getMaximumOccurs());
+ assertNull ("unit", descriptor.getUnit());
}
/**
@@ -252,15 +256,16 @@ public final strictfp class
DefaultParameterDescriptorTest extends TestCase {
final String[] enumeration = {"Apple", "Orange", "りんご"};
final ParameterDescriptor<String> descriptor = create(
"Enumeration param", String.class, enumeration, "Apple");
- assertEquals ("name", "Enumeration param",
descriptor.getName().getCode());
- assertEquals ("valueClass", String.class,
descriptor.getValueClass());
- assertArrayEquals("validValues", enumeration,
descriptor.getValidValues().toArray());
- assertEquals ("defaultValue", "Apple",
descriptor.getDefaultValue());
- assertNull ("minimumValue",
descriptor.getMinimumValue());
- assertNull ("maximumValue",
descriptor.getMaximumValue());
- assertEquals ("minimumOccurs", 1,
descriptor.getMinimumOccurs());
- assertEquals ("maximumOccurs", 1,
descriptor.getMaximumOccurs());
- assertNull ("unit", descriptor.getUnit());
+ assertEquals ("name", "Enumeration param",
descriptor.getName().getCode());
+ assertEquals ("valueType", "CharacterString",
descriptor.getValueType().toString());
+ assertEquals ("valueClass", String.class,
descriptor.getValueClass());
+ assertArrayEquals("validValues", enumeration,
descriptor.getValidValues().toArray());
+ assertEquals ("defaultValue", "Apple",
descriptor.getDefaultValue());
+ assertNull ("minimumValue",
descriptor.getMinimumValue());
+ assertNull ("maximumValue",
descriptor.getMaximumValue());
+ assertEquals ("minimumOccurs", 1,
descriptor.getMinimumOccurs());
+ assertEquals ("maximumOccurs", 1,
descriptor.getMaximumOccurs());
+ assertNull ("unit",
descriptor.getUnit());
/*
* Invalid operation: element not in the list of valid elements.
*/
@@ -281,8 +286,9 @@ public final strictfp class DefaultParameterDescriptorTest
extends TestCase {
public void testArrayType() {
final DefaultParameterDescriptor<double[]> descriptor =
createForArray("Array param", 4, 9, Units.METRE);
assertEquals("name", "Array param",
descriptor.getName().getCode());
+ assertEquals("valueType", "Real",
descriptor.getValueType().toString());
assertEquals("valueClass", double[].class, descriptor.getValueClass());
- assertEquals("unit", Units.METRE, descriptor.getUnit());
+ assertEquals("unit", Units.METRE, descriptor.getUnit());
assertNull ("validValues",
descriptor.getValidValues());
assertNull ("defaultValue",
descriptor.getDefaultValue());
assertNull ("minimumValue",
descriptor.getMinimumValue());
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java
b/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java
index fb676eebdc..66dcb2ac9a 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.util;
+import java.util.Map;
import java.util.Set;
import java.util.Arrays;
import java.util.Iterator;
@@ -25,8 +26,10 @@ import java.util.LinkedHashSet;
import java.lang.reflect.Type;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Modifier;
import org.opengis.annotation.UML;
@@ -53,7 +56,7 @@ import static
org.apache.sis.internal.system.Modules.INTERNAL_CLASSNAME_PREFIX;
* </ul>
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.1
+ * @version 1.3
* @since 0.3
* @module
*/
@@ -140,23 +143,30 @@ public final class Classes extends Static {
/**
* Returns the upper bounds of the parameterized type of the given
property.
* If the property does not have a parameterized type, returns {@code
null}.
- *
- * <p>This method is typically used for fetching the type of elements in a
collection.
- * We do not provide a method working from a {@link Class} instance
because of the way
- * parameterized types are implemented in Java (by erasure).</p>
- *
- * <b>Examples:</b> When invoking this method for a field of the type
below:
+ * If the property has more than one parameterized type, then the parameter
+ * examined by this method depends on the property type:
* <ul>
- * <li>{@code Set<Number>} returns {@code Number.class}.</li>
- *
- * <li>{@code Set<? extends Number>} returns {@code Number.class} as
well, since that
- * collection can not (in theory) contain instances of
super-classes; {@code Number}
- * is the <cite>upper bound</cite>.</li>
+ * <li>If {@link Map}, then this method returns the type of keys in map
entries.</li>
+ * <li>For all other types, this method expects exactly one
parameterized type
+ * for avoiding ambiguity. If this is not the case, {@code null} is
returned.</li>
+ * </ul>
*
- * <li>{@code Set<? super Number>} returns {@code Object.class}, because
that collection
- * is allowed to contain such elements.</li>
+ * This method is used for fetching the type of elements in a collection.
+ * This information can not be obtained from a {@link Class} instance
+ * because of the way parameterized types are implemented in Java (by
erasure).
*
- * <li>{@code Set} returns {@code null} because that collection is
un-parameterized.</li>
+ * <h4>Examples</h4>
+ * When invoking this method for a field of the following types:
+ * <ul>
+ * <li>{@code Map<String,Number>}: returns {@code String.class}, the
type of keys.</li>
+ * <li>{@code Set<Number>}: returns {@code Number.class}.</li>
+ * <li>{@code Set<? extends Number>}: returns {@code Number.class} as
well,
+ * because that collection can not contain instances of
super-classes.
+ * {@code Number} is the <cite>upper bound</cite>.</li>
+ * <li>{@code Set<? super Number>}: returns {@code Object.class},
+ * because that collection is allowed to contain such elements.</li>
+ * <li>{@code Set}: returns {@code null} because that collection is
declared with raw type.</li>
+ * <li>{@code Long}: returns {@code null} because that type is not
parameterized.</li>
* </ul>
*
* @param field the field for which to obtain the parameterized type.
@@ -168,70 +178,148 @@ public final class Classes extends Static {
}
/**
- * If the given method is a getter or a setter for a parameterized
property, returns the
- * upper bounds of the parameterized type. Otherwise returns {@code null}.
This method
- * provides the same semantic than {@link
#boundOfParameterizedProperty(Field)}, but
- * works on a getter or setter method rather then the field. See the
javadoc of above
- * method for more details.
+ * If the given method is a getter or a setter for a parameterized
property,
+ * returns the upper bounds of the parameterized type.
+ * Otherwise returns {@code null}.
+ * This method provides the same semantic than {@link
#boundOfParameterizedProperty(Field)},
+ * but works on a getter or setter method rather than a field.
+ * See {@link #boundOfParameterizedProperty(Field)} javadoc for details.
*
- * <p>This method is typically used for fetching the type of elements in a
collection.
- * We do not provide a method working from a {@link Class} instance
because of the way
- * parameterized types are implemented in Java (by erasure).</p>
+ * <p>This method is used for fetching the type of elements in a
collection.
+ * This information can not be obtained from a {@link Class} instance
+ * because of the way parameterized types are implemented in Java (by
erasure).</p>
*
* @param method the getter or setter method for which to obtain the
parameterized type.
* @return the upper bound of parameterized type, or {@code null} if the
given method
- * does not operate on an object of a parameterized type.
+ * is not a getter or setter for a property of a parameterized
type.
*/
public static Class<?> boundOfParameterizedProperty(final Method method) {
- Class<?> c = getActualTypeArgument(method.getGenericReturnType());
- if (c == null) {
- final Type[] parameters = method.getGenericParameterTypes();
- if (parameters != null && parameters.length == 1) {
- c = getActualTypeArgument(parameters[0]);
+ final Type[] parameters = method.getGenericParameterTypes();
+ final Type type;
+ switch (parameters.length) {
+ case 0: type = method.getGenericReturnType(); break;
+ case 1: type = parameters[0]; break;
+ default: return null;
+ }
+ return getActualTypeArgument(type);
+ }
+
+ /**
+ * Returns a single bound declared in a parameterized class or a
parameterized method.
+ * The {@code typeOrMethod} argument is usually a {@link Class} for a
collection type.
+ * If the given argument is a non-parameterized class, then this method
searches for
+ * the first parameterized super-class (see example below).
+ * If no parameterized declaration is found, then this method returns
{@code null}.
+ * If the declaration has more than one parameterized type, then this
method applies
+ * the same heuristic rule as {@link #boundOfParameterizedProperty(Field)}
+ * (see the javadoc of that method for details).
+ *
+ * <h4>Examples</h4>
+ * When invoking this method with the following {@link Class} argument
values:
+ * <ul>
+ * <li>{@code List.class}: returns {@code Object.class} because {@link
java.util.List} is declared as
+ * {@code List<E>} (implicitly {@code <E extends Object>}).</li>
+ * <li>{@code Map.class}: returns {@code Object.class} because {@link
java.util.Map} is declared as
+ * {@code Map<K,V>} and, as an heuristic rule, we return the key
type of map entry.</li>
+ * <li>{@code PrinterStateReasons.class}: returns {@code
PrinterStateReason.class} because
+ * {@link javax.print.attribute.standard.PrinterStateReasons} is not
parameterized but extends
+ * {@code HashMap<PrinterStateReason,Severity>}.</li>
+ * <li>{@code Long.class}: returns {@code null} because that type is not
parameterized.</li>
+ * </ul>
+ *
+ * This method is used as a fallback when {@code
boundOfParameterizedProperty(…)} can not be used.
+ *
+ * @param typeOrMethod the {@link Class} or {@link Method} from which to
get the bounds of its parameter.
+ * @return the upper bound of parameterized class or method, or {@code
null} if this method can not identify
+ * a single parameterized type to return.
+ *
+ * @see #boundOfParameterizedProperty(Field)
+ * @see #boundOfParameterizedProperty(Method)
+ *
+ * @since 1.3
+ */
+ public static Class<?> boundOfParameterizedDeclaration(final
GenericDeclaration typeOrMethod) {
+ final TypeVariable<?>[] parameters = typeOrMethod.getTypeParameters();
+ final int i = chooseSingleType(typeOrMethod, parameters.length);
+ if (i >= 0) {
+ Class<?> bounds = null;
+ for (final Type p : parameters[i].getBounds()) {
+ if (p instanceof Class<?>) {
+ bounds = findCommonClass(bounds, (Class<?>) p);
+ }
}
+ return bounds;
}
- return c;
+ return getActualTypeArgument(typeOrMethod);
}
/**
- * Delegates to {@link ParameterizedType#getActualTypeArguments()} and
returns the result as a
- * {@link Class}, provided that every objects are of the expected classes
and the result was
- * an array of length 1 (so there is no ambiguity). Otherwise returns
{@code null}.
+ * Returns the type argument of the given type or the first parameterized
parent type.
+ * For example if the given type is {@code List<String>}, then this method
returns {@code String.class}.
+ * This method expects a fixed amount of parameterized types (currently 2
if the given type is {@code Map}
+ * and 1 for all other types), otherwise it returns {@code null}.
+ *
+ * @see ParameterizedType#getActualTypeArguments()
*/
- private static Class<?> getActualTypeArgument(Type type) {
- if (type instanceof ParameterizedType) {
- Type[] p = ((ParameterizedType) type).getActualTypeArguments();
- while (p != null && p.length == 1) {
- type = p[0];
- if (type instanceof WildcardType) {
- p = ((WildcardType) type).getUpperBounds();
- continue;
+ private static Class<?> getActualTypeArgument(Object typeOrMethod) {
+ while (typeOrMethod instanceof Class<?>) {
+ typeOrMethod = ((Class<?>) typeOrMethod).getGenericSuperclass();
+ }
+ if (typeOrMethod instanceof ParameterizedType) {
+ final ParameterizedType p = (ParameterizedType) typeOrMethod;
+ final Type[] parameters = p.getActualTypeArguments();
+ final int i = chooseSingleType(p.getRawType(), parameters.length);
+ if (i >= 0) {
+ Type type = parameters[i];
+ while (type instanceof WildcardType) {
+ final Type[] bounds = ((WildcardType)
type).getUpperBounds();
+ if (bounds.length != 1) return null;
+ type = bounds[0];
}
/*
- * At this point we are not going to continue the loop anymore.
- * Check if we have an array, then check the (component) class.
+ * If we have an array, unroll it until we get the class of
array components.
+ * The array will be reconstructed at the end of this method,
but as a class
+ * instead of as a generic type (i.e. we convert Type[][]… to
Class[][]…).
*/
- if (type instanceof ParameterizedType) {
- /*
- * Example: replace ParameterDescriptor<?> by
ParameterDescriptor
- * before we test if (type instanceof Class<?>).
- */
- type = ((ParameterizedType) type).getRawType();
- }
int dimension = 0;
while (type instanceof GenericArrayType) {
type = ((GenericArrayType) type).getGenericComponentType();
dimension++;
}
+ /*
+ * Then replace (for example) `ParameterDescriptor<?>` by
`ParameterDescriptor`
+ * in order to get an instance of `Class` instead of other
kind of `Type`.
+ */
+ if (type instanceof ParameterizedType) {
+ type = ((ParameterizedType) type).getRawType();
+ }
if (type instanceof Class<?>) {
return changeArrayDimension((Class<?>) type, dimension);
}
- break; // Unknown type.
}
}
return null;
}
+ /**
+ * Chooses (using heuristic rules) a single element in an array of type
arguments.
+ * The given raw type should be a {@link Class} instance when possible,
usually a collection type.
+ * The given count shall be the number of parameters, for example 2 in
{@code Map<String,Integer>}.
+ *
+ * @param rawType the parameterized class, as a {@link Class} instance
if possible.
+ * @param count length of the array of type parameters in which to
select a single type.
+ * @return index of the parameter to select in an array of length {@code
count}, or -1 if none.
+ */
+ @SuppressWarnings("fallthrough")
+ private static int chooseSingleType(final Object rawType, final int count)
{
+ switch (count) {
+ case 2: if (!(rawType instanceof Class<?>) &&
Map.class.isAssignableFrom((Class<?>) rawType)) break;
+ // Else fallthrough.
+ case 1: return 0;
+ }
+ return -1;
+ }
+
/**
* Returns the class of the specified object, or {@code null} if {@code
object} is null.
* This method is also useful for fetching the class of an object known
only by its bound
diff --git
a/core/sis-utility/src/test/java/org/apache/sis/util/ClassesTest.java
b/core/sis-utility/src/test/java/org/apache/sis/util/ClassesTest.java
index 7d608db323..d6903c1f67 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/util/ClassesTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/util/ClassesTest.java
@@ -17,6 +17,8 @@
package org.apache.sis.util;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.GenericDeclaration;
import org.junit.Test;
import org.apache.sis.test.TestCase;
@@ -28,6 +30,7 @@ import static org.apache.sis.util.Classes.*;
* The are used only as various Class<?> arguments
* given to the methods to test.
*/
+import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.HashSet;
@@ -44,6 +47,8 @@ import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.io.Serializable;
import java.awt.geom.Point2D;
+import javax.print.attribute.standard.PrinterStateReason;
+import javax.print.attribute.standard.PrinterStateReasons;
import org.opengis.util.InternationalString;
import org.opengis.metadata.extent.Extent;
import org.opengis.referencing.IdentifiedObject;
@@ -62,7 +67,7 @@ import org.opengis.referencing.operation.CoordinateOperation;
* Tests the {@link Classes} static methods.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.3
* @since 0.3
* @module
*/
@@ -94,7 +99,7 @@ public final strictfp class ClassesTest extends TestCase {
* Tests {@link Classes#getAllInterfaces(Class)}.
*/
@Test
- @SuppressWarnings("")
+ @SuppressWarnings("rawtypes")
public void testGetAllInterfaces() {
assertArrayEquals(new Class[] {
GeographicCRS.class,
@@ -221,25 +226,39 @@ public final strictfp class ClassesTest extends TestCase {
/**
* Tests the {@link Classes#boundOfParameterizedProperty(Field)} method.
*
- * @throws NoSuchFieldException if there is an error in a field name.
+ * @throws NoSuchFieldException if there is an error in a field name.
+ */
+ @Test
+ public void testBoundOfParameterizedField() throws NoSuchFieldException {
+ final Class<Parameterized> c = Parameterized.class;
+ assertNull(
boundOfParameterizedProperty(c.getField("attrib1")));
+ assertEquals(Long .class,
boundOfParameterizedProperty(c.getField("attrib2")));
+ assertEquals(String.class,
boundOfParameterizedProperty(c.getField("attrib3")));
+ }
+
+ /**
+ * Tests the {@link Classes#boundOfParameterizedProperty(Method)} method.
+ *
* @throws NoSuchMethodException if there is an error in a method name.
*/
@Test
- public void testBoundOfParameterizedProperty() throws
NoSuchFieldException, NoSuchMethodException {
- final Class<?>[] g = null;
- final Class<?>[] s = new Class<?>[] {Set.class};
+ public void testBoundOfParameterizedProperty() throws
NoSuchMethodException {
+ final Class<?>[] getter = null;
+ final Class<?>[] setter = new Class<?>[] {Set.class};
final Class<Parameterized> c = Parameterized.class;
- assertNull(
boundOfParameterizedProperty(c.getMethod("getter0", g)));
- assertNull(
boundOfParameterizedProperty(c.getMethod("setter0", s)));
- assertEquals(Long .class, boundOfParameterizedProperty(c.getField
("attrib2" )));
- assertEquals(Integer .class,
boundOfParameterizedProperty(c.getMethod("getter1", g)));
- assertEquals(Byte .class,
boundOfParameterizedProperty(c.getMethod("getter2", g)));
- assertEquals(Object .class,
boundOfParameterizedProperty(c.getMethod("getter3", g)));
- assertEquals(short[] .class,
boundOfParameterizedProperty(c.getMethod("getter4", g)));
- assertEquals(Comparable.class,
boundOfParameterizedProperty(c.getMethod("getter5", g)));
- assertEquals(String .class,
boundOfParameterizedProperty(c.getMethod("setter1", s)));
- assertEquals(Short .class,
boundOfParameterizedProperty(c.getMethod("setter2", s)));
- assertEquals(Object .class,
boundOfParameterizedProperty(c.getMethod("setter3", s)));
+ assertNull(
boundOfParameterizedProperty(c.getMethod("getter0", getter)));
+ assertNull(
boundOfParameterizedProperty(c.getMethod("setter0", setter)));
+ assertEquals(Integer .class,
boundOfParameterizedProperty(c.getMethod("getter1", getter)));
+ assertEquals(Byte .class,
boundOfParameterizedProperty(c.getMethod("getter2", getter)));
+ assertEquals(Object .class,
boundOfParameterizedProperty(c.getMethod("getter3", getter)));
+ assertEquals(short[] .class,
boundOfParameterizedProperty(c.getMethod("getter4", getter)));
+ assertEquals(Comparable .class,
boundOfParameterizedProperty(c.getMethod("getter5", getter)));
+ assertEquals(Comparable[].class,
boundOfParameterizedProperty(c.getMethod("getter6", getter)));
+ assertEquals(String .class,
boundOfParameterizedProperty(c.getMethod("setter1", setter)));
+ assertEquals(Short .class,
boundOfParameterizedProperty(c.getMethod("setter2", setter)));
+ assertEquals(Object .class,
boundOfParameterizedProperty(c.getMethod("setter3", setter)));
+
+ assertEquals(PrinterStateReason.class,
boundOfParameterizedProperty(c.getMethod("getter7", getter)));
}
/**
@@ -247,20 +266,35 @@ public final strictfp class ClassesTest extends TestCase {
*/
@SuppressWarnings("rawtypes")
private static final class Parameterized {
- public Set<? extends Long> attrib2 = null;
- public Set getter0() {return null;} //
Intentionnaly unparameterized.
- public Set< Integer> getter1() {return null;}
- public Set<? extends Byte> getter2() {return null;}
- public Set<? super Float> getter3() {return null;}
- public Set< short[]> getter4() {return null;}
- public Set<Comparable<?>> getter5() {return null;}
-
- public void setter0(Set dummy) {} //
Intentionnaly unparameterized.
+ public Long attrib1;
+ public Set<? extends Long> attrib2;
+ public Map<String,Integer> attrib3;
+ public Set getter0() {return null;} //
Intentionnaly unparameterized.
+ public Set< Integer> getter1() {return null;}
+ public Set<? extends Byte> getter2() {return null;}
+ public Set<? super Float> getter3() {return null;}
+ public Set< short[]> getter4() {return null;}
+ public Set<Comparable<?>> getter5() {return null;}
+ public Set<Comparable<?>[]> getter6() {return null;}
+ public PrinterStateReasons getter7() {return null;}
+
+ public void setter0(Set dummy) {} //
Intentionnaly unparameterized.
public void setter1(Set< String> dummy) {}
public void setter2(Set<? extends Short> dummy) {}
public void setter3(Set<? super Double> dummy) {}
}
+ /**
+ * Tests the {@link
Classes#boundOfParameterizedDeclaration(GenericDeclaration)} method.
+ */
+ @Test
+ public void testBoundOfParameterizedDeclaration() {
+ assertNull (
boundOfParameterizedDeclaration(Long.class));
+ assertEquals(Object.class,
boundOfParameterizedDeclaration(List.class));
+ assertEquals(Object.class, boundOfParameterizedDeclaration(Map.class));
+ assertEquals(PrinterStateReason.class,
boundOfParameterizedDeclaration(PrinterStateReasons.class));
+ }
+
/**
* Tests the {@link Classes#getShortName(Class)}, in particular the
example values given in the javadoc.
*/