org.geotoolkit.metadata.FactoryMethod Maven / Gradle / Ivy
Show all versions of geotk-metadata Show documentation
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2011, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2011, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.metadata;
import java.util.Map;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.lang.reflect.Type;
import java.lang.reflect.Method;
import java.lang.reflect.WildcardType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.InvocationTargetException;
import net.jcip.annotations.Immutable;
import org.opengis.util.FactoryException;
/**
* The method used for creating an instance of some specific class using a factory.
* This is used by {@link MetadataFactory} only.
*
* Factory method starts with {@link #create}, and their first parameter type is {@code Map},
* {@code Map} or {@code Map}.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.03
*
* @since 3.03
* @module
*/
@Immutable
final class FactoryMethod {
/**
* A dummy {@code FactoryMethod} used by {@link MetadataFactory} as a sentinel
* values when we have determined that there is no factory method available.
*/
static final FactoryMethod NULL = new FactoryMethod(null, null);
/**
* The method to invoke on a factory.
*/
private final Method method;
/**
* The factory instance on which to invoke the method.
*/
private final Object factory;
/**
* Creates a new {@code FactoryMethod} object.
*
* @param method The method to invoke on a factory.
* @param factory The factory instance on which to invoke the method.
*/
private FactoryMethod(final Method method, final Object factory) {
this.method = method;
this.factory = factory;
}
/**
* Creates a new {@code FactoryMethod} instance for creating an object of the given type
* from the given factory. The first suitable factory is used. If no suitable factory is
* found, then this method returns {@code null}.
*
* @param type The type of the object to create.
* @param factories The factories to try.
* @return A new {@code FactoryMethod}, or {@code null} if none.
*/
static FactoryMethod find(final Class> type, final Object[] factories) {
for (final Object factory : factories) {
for (final Method method : factory.getClass().getMethods()) {
final String name = method.getName();
if (!name.startsWith("create")) {
continue;
}
if (!type.isAssignableFrom(method.getReturnType())) {
continue;
}
Type[] types = method.getGenericParameterTypes();
if (types.length == 0) {
continue;
}
/*
* We have found a "create" method with a suitable return type. Now check
* the parameters. The first one must be either the Map raw parameter type,
* the Map type or the Map one.
*/
final Type mapType = types[0];
if (mapType != Map.class) {
if (!(mapType instanceof ParameterizedType)) {
continue;
}
types = ((ParameterizedType) mapType).getActualTypeArguments();
if (types.length != 2 ||
!bounds(types[0]).isAssignableFrom(String.class) ||
!bounds(types[1]).isAssignableFrom(Object.class))
{
continue;
}
return new FactoryMethod(method, factory);
}
}
}
return null;
}
/**
* Returns the upper bounds of the given type. The type must be either a (@link Class} or
* a {@link WildcardType}. We assume that no other type can occur in this implementation.
*/
private static Class> bounds(Type type) {
while (type instanceof WildcardType) {
final Type[] types = ((WildcardType) type).getUpperBounds();
if (types.length == 1) {
type = types[0];
}
}
return (Class>) type;
}
/**
* Invokes the {@code Factory.create(Map, ...)} method with the given properties.
* The values in the given map that are assignable to the parameter type expected
* by the factory method are extracted from the map and assigned to the parameter
* array.
*
* In the special case where this {@code FactoryMethod} is {@link #NULL}, this
* method returns {@code null}.
*/
Object create(Map properties) throws FactoryException {
if (method == null) {
return null;
}
final Class>[] types = method.getParameterTypes();
final Object[] parameters = new Object[types.length];
if (types.length > 1) {
final Map reduced = new LinkedHashMap(properties);
properties = reduced;
for (int i=1; i expected = types[i];
for (final Iterator