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