001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.impl.converter; 018 019 import java.io.IOException; 020 import java.util.ArrayList; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.Set; 024 import java.util.concurrent.ConcurrentHashMap; 025 026 import org.apache.camel.Exchange; 027 import org.apache.camel.NoTypeConversionAvailableException; 028 import org.apache.camel.TypeConverter; 029 import org.apache.camel.spi.Injector; 030 import org.apache.camel.spi.TypeConverterAware; 031 import org.apache.camel.util.FactoryFinder; 032 import org.apache.camel.util.NoFactoryAvailableException; 033 import org.apache.camel.util.ObjectHelper; 034 import org.apache.commons.logging.Log; 035 import org.apache.commons.logging.LogFactory; 036 037 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException; 038 039 040 /** 041 * Default implementation of a type converter registry used for 042 * <a href="http://activemq.apache.org/camel/type-converter.html">type converters</a> in Camel. 043 * 044 * @version $Revision: 52365 $ 045 */ 046 public class DefaultTypeConverter implements TypeConverter, TypeConverterRegistry { 047 private static final transient Log LOG = LogFactory.getLog(DefaultTypeConverter.class); 048 private final Map<TypeMapping, TypeConverter> typeMappings = new ConcurrentHashMap<TypeMapping, TypeConverter>(); 049 private Injector injector; 050 private List<TypeConverterLoader> typeConverterLoaders = new ArrayList<TypeConverterLoader>(); 051 private List<TypeConverter> fallbackConverters = new ArrayList<TypeConverter>(); 052 private boolean loaded; 053 054 public DefaultTypeConverter(Injector injector) { 055 typeConverterLoaders.add(new AnnotationTypeConverterLoader()); 056 this.injector = injector; 057 addFallbackConverter(new AsyncProcessorTypeConverter()); 058 addFallbackConverter(new PropertyEditorTypeConverter()); 059 addFallbackConverter(new ToStringTypeConverter()); 060 addFallbackConverter(new ArrayTypeConverter()); 061 addFallbackConverter(new EnumTypeConverter()); 062 } 063 064 public <T> T convertTo(Class<T> type, Object value) { 065 return convertTo(type, null, value); 066 } 067 068 @SuppressWarnings("unchecked") 069 public <T> T convertTo(Class<T> type, Exchange exchange, Object value) { 070 if (LOG.isTraceEnabled()) { 071 LOG.trace("Converting " + (value == null ? "null" : value.getClass().getCanonicalName()) 072 + " -> " + type.getCanonicalName() + " with value: " + value); 073 } 074 075 if (value == null) { 076 // lets avoid NullPointerException when converting to boolean for null values 077 if (boolean.class.isAssignableFrom(type)) { 078 return (T) Boolean.FALSE; 079 } 080 return null; 081 } 082 083 // same instance type 084 if (type.isInstance(value)) { 085 return type.cast(value); 086 } 087 088 // make sure we have loaded the converters 089 checkLoaded(); 090 091 // try to find a suitable type converter 092 TypeConverter converter = getOrFindTypeConverter(type, value); 093 if (converter != null) { 094 T rc = converter.convertTo(type, exchange, value); 095 if (rc != null) { 096 return rc; 097 } 098 } 099 100 // fallback converters 101 for (TypeConverter fallback : fallbackConverters) { 102 T rc = fallback.convertTo(type, exchange, value); 103 if (rc != null) { 104 return rc; 105 } 106 } 107 108 // primitives 109 if (type.isPrimitive()) { 110 Class primitiveType = ObjectHelper.convertPrimitiveTypeToWrapperType(type); 111 if (primitiveType != type) { 112 return (T) convertTo(primitiveType, exchange, value); 113 } 114 } 115 116 // Could not find suitable conversion 117 throw new NoTypeConversionAvailableException(value, type); 118 } 119 120 public void addTypeConverter(Class toType, Class fromType, TypeConverter typeConverter) { 121 TypeMapping key = new TypeMapping(toType, fromType); 122 synchronized (typeMappings) { 123 TypeConverter converter = typeMappings.get(key); 124 if (converter != null) { 125 LOG.warn("Overriding type converter from: " + converter + " to: " + typeConverter); 126 } 127 typeMappings.put(key, typeConverter); 128 } 129 } 130 131 public void addFallbackConverter(TypeConverter converter) { 132 fallbackConverters.add(converter); 133 if (converter instanceof TypeConverterAware) { 134 TypeConverterAware typeConverterAware = (TypeConverterAware)converter; 135 typeConverterAware.setTypeConverter(this); 136 } 137 } 138 139 public TypeConverter getTypeConverter(Class toType, Class fromType) { 140 TypeMapping key = new TypeMapping(toType, fromType); 141 return typeMappings.get(key); 142 } 143 144 public Injector getInjector() { 145 return injector; 146 } 147 148 public void setInjector(Injector injector) { 149 this.injector = injector; 150 } 151 152 protected <T> TypeConverter getOrFindTypeConverter(Class toType, Object value) { 153 Class fromType = null; 154 if (value != null) { 155 fromType = value.getClass(); 156 } 157 TypeMapping key = new TypeMapping(toType, fromType); 158 TypeConverter converter; 159 synchronized (typeMappings) { 160 converter = typeMappings.get(key); 161 if (converter == null) { 162 converter = findTypeConverter(toType, fromType, value); 163 if (converter != null) { 164 typeMappings.put(key, converter); 165 } 166 } 167 } 168 return converter; 169 } 170 171 /** 172 * Tries to auto-discover any available type converters 173 */ 174 protected TypeConverter findTypeConverter(Class toType, Class fromType, Object value) { 175 // lets try the super classes of the from type 176 if (fromType != null) { 177 Class fromSuperClass = fromType.getSuperclass(); 178 if (fromSuperClass != null && !fromSuperClass.equals(Object.class)) { 179 180 TypeConverter converter = getTypeConverter(toType, fromSuperClass); 181 if (converter == null) { 182 converter = findTypeConverter(toType, fromSuperClass, value); 183 } 184 if (converter != null) { 185 return converter; 186 } 187 } 188 for (Class type : fromType.getInterfaces()) { 189 TypeConverter converter = getTypeConverter(toType, type); 190 if (converter != null) { 191 return converter; 192 } 193 } 194 195 // lets test for arrays 196 if (fromType.isArray() && !fromType.getComponentType().isPrimitive()) { 197 // TODO can we try walking the inheritance-tree for the element types? 198 if (!fromType.equals(Object[].class)) { 199 fromSuperClass = Object[].class; 200 201 TypeConverter converter = getTypeConverter(toType, fromSuperClass); 202 if (converter == null) { 203 converter = findTypeConverter(toType, fromSuperClass, value); 204 } 205 if (converter != null) { 206 return converter; 207 } 208 } 209 } 210 211 // lets test for Object based converters 212 if (!fromType.equals(Object.class)) { 213 TypeConverter converter = getTypeConverter(toType, Object.class); 214 if (converter != null) { 215 return converter; 216 } 217 } 218 } 219 220 // lets try classes derived from this toType 221 if (fromType != null) { 222 Set<Map.Entry<TypeMapping, TypeConverter>> entries = typeMappings.entrySet(); 223 for (Map.Entry<TypeMapping, TypeConverter> entry : entries) { 224 TypeMapping key = entry.getKey(); 225 Class aToType = key.getToType(); 226 if (toType.isAssignableFrom(aToType)) { 227 if (key.getFromType().isAssignableFrom(fromType)) { 228 return entry.getValue(); 229 } 230 } 231 } 232 } 233 234 // TODO look at constructors of toType? 235 return null; 236 } 237 238 /** 239 * Checks if the registry is loaded and if not lazily load it 240 */ 241 protected synchronized void checkLoaded() { 242 if (!loaded) { 243 loaded = true; 244 try { 245 for (TypeConverterLoader typeConverterLoader : typeConverterLoaders) { 246 typeConverterLoader.load(this); 247 } 248 249 // lets try load any other fallback converters 250 try { 251 loadFallbackTypeConverters(); 252 } catch (NoFactoryAvailableException e) { 253 // ignore its fine to have none 254 } 255 } catch (Exception e) { 256 throw wrapRuntimeCamelException(e); 257 } 258 } 259 } 260 261 protected void loadFallbackTypeConverters() throws IOException, ClassNotFoundException { 262 FactoryFinder finder = new FactoryFinder(); 263 List<TypeConverter> converters = finder.newInstances("FallbackTypeConverter", getInjector(), 264 TypeConverter.class); 265 for (TypeConverter converter : converters) { 266 addFallbackConverter(converter); 267 } 268 } 269 270 /** 271 * Represents a mapping from one type (which can be null) to another 272 */ 273 protected static class TypeMapping { 274 Class toType; 275 Class fromType; 276 277 public TypeMapping(Class toType, Class fromType) { 278 this.toType = toType; 279 this.fromType = fromType; 280 } 281 282 public Class getFromType() { 283 return fromType; 284 } 285 286 public Class getToType() { 287 return toType; 288 } 289 290 @Override 291 public boolean equals(Object object) { 292 if (object instanceof TypeMapping) { 293 TypeMapping that = (TypeMapping)object; 294 return ObjectHelper.equal(this.fromType, that.fromType) 295 && ObjectHelper.equal(this.toType, that.toType); 296 } 297 return false; 298 } 299 300 @Override 301 public int hashCode() { 302 int answer = toType.hashCode(); 303 if (fromType != null) { 304 answer *= 37 + fromType.hashCode(); 305 } 306 return answer; 307 } 308 309 @Override 310 public String toString() { 311 return "[" + fromType + "=>" + toType + "]"; 312 } 313 } 314 }