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     */
022    package org.granite.messaging.amf.io.convert;
023    
024    import java.lang.reflect.Constructor;
025    import java.lang.reflect.InvocationTargetException;
026    import java.lang.reflect.Type;
027    import java.lang.reflect.TypeVariable;
028    import java.util.ArrayList;
029    import java.util.List;
030    
031    import org.granite.util.TypeUtil;
032    
033    /**
034     * @author Franck WOLFF
035     *
036     * @see Converter
037     * @see Reverter
038     */
039    public 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    }