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 }