All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotoolkit.metadata.FactoryMethod Maven / Gradle / Ivy

Go to download

Implementations of metadata derived from ISO 19115. This module provides both an implementation of the metadata interfaces defined in GeoAPI, and a framework for handling those metadata through Java reflection.

There is a newer version: 3.20-geoapi-3.0
Show newest version
/*
 *    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 it=reduced.values().iterator(); it.hasNext();) { final Object value = it.next(); if (expected.isInstance(value)) { parameters[i] = value; it.remove(); break; } } } } parameters[0] = properties; try { return method.invoke(factory, parameters); } catch (IllegalAccessException e) { // Should never happen, because we asked for public methods only. throw new AssertionError(e); } catch (InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof FactoryException) { throw (FactoryException) cause; } throw new FactoryException(cause.getLocalizedMessage(), cause); } } /** * Returns {@code true} if the given object is equals to this factory method. */ @Override public boolean equals(final Object other) { if (other instanceof FactoryMethod) { final FactoryMethod that = (FactoryMethod) other; return method.equals(that.method) && factory == that.factory; } return false; } /** * Defined for consistency with {@link #equals} but not used. */ @Override public int hashCode() { return method.hashCode() ^ System.identityHashCode(factory); } }