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