/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services 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; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.messaging.amf.io.convert; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.List; import org.granite.util.TypeUtil; /** * @author Franck WOLFF * * @see Converter * @see Reverter */ public class Converters { /** Array of all configured converters */ private Converter[] converters; /** Array of all configured reverters */ private Reverter[] reverters; /** * Constructs a new Converters instance with the supplied list of converters (possibly reverters). * * @param converterClasses the list of all used converters. * @throws NoSuchMethodException if one of the Converter does not have a constructor with a * Converters parameter. * @throws IllegalAccessException if something goes wrong when creating an instance of one * of the supplied Converter classes. * @throws InvocationTargetException if something goes wrong when creating an instance of one * of the supplied Converter classes. * @throws InstantiationException if something goes wrong when creating an instance of one * of the supplied Converter classes. */ public Converters(List<Class<? extends Converter>> converterClasses) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { List<Converter> converters = new ArrayList<Converter>(); List<Reverter> reverters = new ArrayList<Reverter>(); if (converterClasses != null) { for (Class<? extends Converter> converterClass : converterClasses) { Constructor<? extends Converter> constructor = converterClass.getConstructor(Converters.class); Converter converter = constructor.newInstance(this); converters.add(converter); if (converter instanceof Reverter) reverters.add((Reverter)converter); } } this.converters = converters.toArray(new Converter[converters.size()]); this.reverters = reverters.toArray(new Reverter[reverters.size()]); } public void addConverter(Class<? extends Converter> converterClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Converter[] converters = new Converter[this.converters.length+1]; System.arraycopy(this.converters, 0, converters, 1, this.converters.length); Constructor<? extends Converter> constructor = converterClass.getConstructor(Converters.class); converters[0] = constructor.newInstance(this); this.converters = converters; if (converters[0] instanceof Reverter) { Reverter[] reverters = new Reverter[this.reverters.length+1]; System.arraycopy(this.reverters, 0, reverters, 1, this.reverters.length); reverters[0] = (Reverter)converters[0]; this.reverters = reverters; } } /** * Returns a suitable converter for supplied parameters or null if no converter * can be found. This method is equivalent to the * {@link Converters#getConverter(Object, Type, boolean)} method with the * throwNotFoundException parameter set to false. * * @param value the value to be converted * @param targetType the type of the converted value * @return a Converter instance or null if no suitable converter can be found */ public Converter getConverter(Object value, Type targetType) { return getConverter(value, targetType, false); } /** * Returns a suitable converter for supplied parameters or either returns null if no converter * can be found or throws a {@link NoConverterFoundException}. * * @param value the value to be converted * @param targetType the type of the converted value * @param throwNotFoundException should an exception be thrown if no converter is found? * @return a Converter instance or null if no suitable converter can be found * @throws NoConverterFoundException if the throwNotFoundException parameter is set to true * and no converter can be found. */ public Converter getConverter(Object value, Type targetType, boolean throwNotFoundException) throws NoConverterFoundException { // Small optimization: this avoids to make TypeVariable conversion in all converters... if (targetType instanceof TypeVariable<?>) targetType = TypeUtil.getBoundType((TypeVariable<?>)targetType); for (Converter converter : converters) { if (converter.canConvert(value, targetType)) return converter; } if (!throwNotFoundException) return null; throw new NoConverterFoundException(value, targetType); } /** * Converts the supplied object to the supplied target type. This method is * a simple shortcut for: <tt>this.getConverter(value, target, true).convert(value, targetType)</tt>. * * @param value the object to be converted. * @param targetType the target type. * @return the converted object. * @throws NoConverterFoundException if no suitable converter can be found. */ public Object convert(Object value, Type targetType) throws NoConverterFoundException { if (value != null) { Class<?> valueClass = value.getClass(); if (valueClass == targetType) return value; if (targetType instanceof Class) { Class<?> targetClass = (Class<?>)targetType; if (targetClass.isPrimitive()) { if (targetClass == int.class && valueClass == Integer.class) return value; if (targetClass == boolean.class && valueClass == Boolean.class) return value; if (targetClass == double.class && valueClass == Double.class) return value; if (targetClass == long.class && valueClass == Long.class) return value; if (targetClass == byte.class && valueClass == Byte.class) return value; if (targetClass == char.class && valueClass == Character.class) return value; if (targetClass == float.class && valueClass == Float.class) return value; if (targetClass == short.class && valueClass == Short.class) return value; } } } else if (targetType instanceof Class && !((Class<?>)targetType).isPrimitive()) return value; return getConverter(value, targetType, true).convert(value, targetType); } /** * Returns true if at least one reverter is configured for this Converters instance. * * @return true if at least one reverter is configured for this Converters instance. */ public boolean hasReverters() { return reverters.length > 0; } /** * Revert back to standard (AMF3 known Java type) the supplied value. This method iterates * on all configured Reverters and returns the {@link Reverter#revert(Object)} method result * if the {@link Reverter#canRevert(Object)} method returns true for the current Reverter * instance. * * @param value the value to be reverted. * @return the reverted value (same instance if none of the configured reverters apply). */ public Object revert(Object value) { for (Reverter reverter : reverters) { if (reverter.canRevert(value)) return reverter.revert(value); } return value; } public Converter[] getConverters() { Converter[] copy = new Converter[converters.length]; System.arraycopy(converters, 0, copy, 0, converters.length); return copy; } }