001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.messaging.amf.io.convert;
023
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Type;
027import java.lang.reflect.TypeVariable;
028import java.util.ArrayList;
029import java.util.List;
030
031import org.granite.util.TypeUtil;
032
033/**
034 * @author Franck WOLFF
035 *
036 * @see Converter
037 * @see Reverter
038 */
039public class Converters {
040
041    /** Array of all configured converters */
042    private Converter[] converters;
043
044    /** Array of all configured reverters */
045    private Reverter[] reverters;
046
047    /**
048     * Constructs a new Converters instance with the supplied list of converters (possibly reverters).
049     *
050     * @param converterClasses the list of all used converters.
051     * @throws NoSuchMethodException if one of the Converter does not have a constructor with a
052     *          Converters parameter.
053     * @throws IllegalAccessException if something goes wrong when creating an instance of one
054     *          of the supplied Converter classes.
055     * @throws InvocationTargetException if something goes wrong when creating an instance of one
056     *          of the supplied Converter classes.
057     * @throws InstantiationException if something goes wrong when creating an instance of one
058     *          of the supplied Converter classes.
059     */
060    public Converters(List<Class<? extends Converter>> converterClasses)
061        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
062
063        List<Converter> converters = new ArrayList<Converter>();
064        List<Reverter> reverters = new ArrayList<Reverter>();
065
066        if (converterClasses != null) {
067            for (Class<? extends Converter> converterClass : converterClasses) {
068                Constructor<? extends Converter> constructor = converterClass.getConstructor(Converters.class);
069                Converter converter = constructor.newInstance(this);
070                converters.add(converter);
071                if (converter instanceof Reverter)
072                    reverters.add((Reverter)converter);
073            }
074        }
075
076        this.converters = converters.toArray(new Converter[converters.size()]);
077        this.reverters = reverters.toArray(new Reverter[reverters.size()]);
078    }
079    
080    public void addConverter(Class<? extends Converter> converterClass) 
081        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
082        
083        Converter[] converters = new Converter[this.converters.length+1];
084        System.arraycopy(this.converters, 0, converters, 1, this.converters.length);
085        Constructor<? extends Converter> constructor = converterClass.getConstructor(Converters.class);
086        converters[0] = constructor.newInstance(this);
087        this.converters = converters;
088        
089        if (converters[0] instanceof Reverter) {
090                Reverter[] reverters = new Reverter[this.reverters.length+1];
091                System.arraycopy(this.reverters, 0, reverters, 1, this.reverters.length);
092                reverters[0] = (Reverter)converters[0];
093                this.reverters = reverters;
094        }
095    }
096
097    /**
098     * Returns a suitable converter for supplied parameters or null if no converter
099     * can be found. This method is equivalent to the
100     * {@link Converters#getConverter(Object, Type, boolean)} method with the
101     * throwNotFoundException parameter set to false.
102     *
103     * @param value the value to be converted
104     * @param targetType the type of the converted value
105     * @return a Converter instance or null if no suitable converter can be found
106     */
107    public Converter getConverter(Object value, Type targetType) {
108        return getConverter(value, targetType, false);
109    }
110
111    /**
112     * Returns a suitable converter for supplied parameters or either returns null if no converter
113     * can be found or throws a {@link NoConverterFoundException}.
114     *
115     * @param value the value to be converted
116     * @param targetType the type of the converted value
117     * @param throwNotFoundException should an exception be thrown if no converter is found?
118     * @return a Converter instance or null if no suitable converter can be found
119     * @throws NoConverterFoundException if the throwNotFoundException parameter is set to true
120     *          and no converter can be found.
121     */
122    public Converter getConverter(Object value, Type targetType, boolean throwNotFoundException)
123        throws NoConverterFoundException {
124        
125        // Small optimization: this avoids to make TypeVariable conversion in all converters...
126        if (targetType instanceof TypeVariable<?>)
127                targetType = TypeUtil.getBoundType((TypeVariable<?>)targetType);
128        
129        for (Converter converter : converters) {
130            if (converter.canConvert(value, targetType))
131                return converter;
132        }
133
134        if (!throwNotFoundException)
135            return null;
136
137        throw new NoConverterFoundException(value, targetType);
138    }
139
140    /**
141     * Converts the supplied object to the supplied target type. This method is
142     * a simple shortcut for: <tt>this.getConverter(value, target, true).convert(value, targetType)</tt>.
143     *
144     * @param value the object to be converted.
145     * @param targetType the target type.
146     * @return the converted object.
147     * @throws NoConverterFoundException if no suitable converter can be found.
148     */
149    public Object convert(Object value, Type targetType) throws NoConverterFoundException {
150        return getConverter(value, targetType, true).convert(value, targetType);
151    }
152
153    /**
154     * Returns true if at least one reverter is configured for this Converters instance.
155     *
156     * @return true if at least one reverter is configured for this Converters instance.
157     */
158    public boolean hasReverters() {
159        return reverters.length > 0;
160    }
161
162    /**
163     * Revert back to standard, AMF3 known Java type the supplied value. This method iterates
164     * on all configured Reverters and returns the {@link Reverter#revert(Object)} method result
165     * if the {@link Reverter#canRevert(Object)} method returns true for the current Reverter
166     * instance.
167     *
168     * @param value the value to be reverted.
169     * @return the reverted value (same instance if none of the configured reverters apply).
170     */
171    public Object revert(Object value) {
172        for (Reverter reverter : reverters) {
173            if (reverter.canRevert(value))
174                return reverter.revert(value);
175        }
176        return value;
177    }
178    
179    public Converter[] getConverters() {
180        Converter[] copy = new Converter[converters.length];
181        System.arraycopy(converters, 0, copy, 0, converters.length);
182        return copy;
183    }
184}