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.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.Set; 025 026 import org.apache.camel.RuntimeCamelException; 027 import org.apache.camel.TypeConverter; 028 import org.apache.camel.spi.Injector; 029 import org.apache.camel.spi.TypeConverterAware; 030 import org.apache.camel.util.FactoryFinder; 031 import org.apache.camel.util.NoFactoryAvailableException; 032 import org.apache.camel.util.ObjectHelper; 033 import org.apache.commons.logging.Log; 034 import org.apache.commons.logging.LogFactory; 035 036 /** 037 * Default implementation of a type converter registry used for 038 * <a href="http://activemq.apache.org/camel/type-converter.html">type converters</a> in Camel. 039 * 040 * @version $Revision: 659849 $ 041 */ 042 public class DefaultTypeConverter implements TypeConverter, TypeConverterRegistry { 043 private static final transient Log LOG = LogFactory.getLog(DefaultTypeConverter.class); 044 private final Map<TypeMapping, TypeConverter> typeMappings = new HashMap<TypeMapping, TypeConverter>(); 045 private Injector injector; 046 private List<TypeConverterLoader> typeConverterLoaders = new ArrayList<TypeConverterLoader>(); 047 private List<TypeConverter> fallbackConverters = new ArrayList<TypeConverter>(); 048 private boolean loaded; 049 050 public DefaultTypeConverter(Injector injector) { 051 typeConverterLoaders.add(new AnnotationTypeConverterLoader()); 052 this.injector = injector; 053 addFallbackConverter(new AsyncProcessorTypeConverter()); 054 addFallbackConverter(new PropertyEditorTypeConverter()); 055 addFallbackConverter(new ToStringTypeConverter()); 056 addFallbackConverter(new ArrayTypeConverter()); 057 addFallbackConverter(new EnumTypeConverter()); 058 } 059 060 public <T> T convertTo(Class<T> toType, Object value) { 061 if (toType.isInstance(value)) { 062 return toType.cast(value); 063 } 064 checkLoaded(); 065 TypeConverter converter = getOrFindTypeConverter(toType, value); 066 if (converter != null) { 067 return converter.convertTo(toType, value); 068 } 069 070 for (TypeConverter fallback : fallbackConverters) { 071 T rc = fallback.convertTo(toType, value); 072 if (rc != null) { 073 return rc; 074 } 075 } 076 077 // lets avoid NullPointerException when converting to boolean for null values 078 if (boolean.class.isAssignableFrom(toType)) { 079 return (T) Boolean.FALSE; 080 } 081 if (toType.isPrimitive()) { 082 Class primitiveType = ObjectHelper.convertPrimitiveTypeToWrapperType(toType); 083 if (primitiveType != toType) { 084 return (T) convertTo(primitiveType, value); 085 } 086 } 087 return null; 088 } 089 090 public void addTypeConverter(Class toType, Class fromType, TypeConverter typeConverter) { 091 TypeMapping key = new TypeMapping(toType, fromType); 092 synchronized (typeMappings) { 093 TypeConverter converter = typeMappings.get(key); 094 if (converter != null) { 095 LOG.warn("Overriding type converter from: " + converter + " to: " + typeConverter); 096 } 097 typeMappings.put(key, typeConverter); 098 } 099 } 100 101 public void addFallbackConverter(TypeConverter converter) { 102 fallbackConverters.add(converter); 103 if (converter instanceof TypeConverterAware) { 104 TypeConverterAware typeConverterAware = (TypeConverterAware)converter; 105 typeConverterAware.setTypeConverter(this); 106 } 107 } 108 109 public TypeConverter getTypeConverter(Class toType, Class fromType) { 110 TypeMapping key = new TypeMapping(toType, fromType); 111 synchronized (typeMappings) { 112 return typeMappings.get(key); 113 } 114 } 115 116 public Injector getInjector() { 117 return injector; 118 } 119 120 public void setInjector(Injector injector) { 121 this.injector = injector; 122 } 123 124 protected <T> TypeConverter getOrFindTypeConverter(Class toType, Object value) { 125 Class fromType = null; 126 if (value != null) { 127 fromType = value.getClass(); 128 } 129 TypeMapping key = new TypeMapping(toType, fromType); 130 TypeConverter converter; 131 synchronized (typeMappings) { 132 converter = typeMappings.get(key); 133 if (converter == null) { 134 converter = findTypeConverter(toType, fromType, value); 135 if (converter != null) { 136 typeMappings.put(key, converter); 137 } 138 } 139 } 140 return converter; 141 } 142 143 /** 144 * Tries to auto-discover any available type converters 145 */ 146 protected TypeConverter findTypeConverter(Class toType, Class fromType, Object value) { 147 // lets try the super classes of the from type 148 if (fromType != null) { 149 Class fromSuperClass = fromType.getSuperclass(); 150 if (fromSuperClass != null && !fromSuperClass.equals(Object.class)) { 151 152 TypeConverter converter = getTypeConverter(toType, fromSuperClass); 153 if (converter == null) { 154 converter = findTypeConverter(toType, fromSuperClass, value); 155 } 156 if (converter != null) { 157 return converter; 158 } 159 } 160 for (Class type : fromType.getInterfaces()) { 161 TypeConverter converter = getTypeConverter(toType, type); 162 if (converter != null) { 163 return converter; 164 } 165 } 166 167 // lets test for arrays 168 if (fromType.isArray() && !fromType.getComponentType().isPrimitive()) { 169 // TODO can we try walking the inheritance-tree for the element types? 170 if (!fromType.equals(Object[].class)) { 171 fromSuperClass = Object[].class; 172 173 TypeConverter converter = getTypeConverter(toType, fromSuperClass); 174 if (converter == null) { 175 converter = findTypeConverter(toType, fromSuperClass, value); 176 } 177 if (converter != null) { 178 return converter; 179 } 180 } 181 } 182 183 // lets test for Object based converters 184 if (!fromType.equals(Object.class)) { 185 TypeConverter converter = getTypeConverter(toType, Object.class); 186 if (converter != null) { 187 return converter; 188 } 189 } 190 } 191 192 // lets try classes derived from this toType 193 if (fromType != null) { 194 Set<Map.Entry<TypeMapping, TypeConverter>> entries = typeMappings.entrySet(); 195 for (Map.Entry<TypeMapping, TypeConverter> entry : entries) { 196 TypeMapping key = entry.getKey(); 197 Class aToType = key.getToType(); 198 if (toType.isAssignableFrom(aToType)) { 199 if (key.getFromType().isAssignableFrom(fromType)) { 200 return entry.getValue(); 201 } 202 } 203 } 204 } 205 206 // TODO look at constructors of toType? 207 return null; 208 } 209 210 /** 211 * Checks if the registry is loaded and if not lazily load it 212 */ 213 protected synchronized void checkLoaded() { 214 if (!loaded) { 215 loaded = true; 216 try { 217 for (TypeConverterLoader typeConverterLoader : typeConverterLoaders) { 218 typeConverterLoader.load(this); 219 } 220 221 // lets try load any other fallback converters 222 try { 223 loadFallbackTypeConverters(); 224 } catch (NoFactoryAvailableException e) { 225 // ignore its fine to have none 226 } 227 } catch (Exception e) { 228 throw new RuntimeCamelException(e); 229 } 230 } 231 } 232 233 protected void loadFallbackTypeConverters() throws IOException, ClassNotFoundException { 234 FactoryFinder finder = new FactoryFinder(); 235 List<TypeConverter> converters = finder.newInstances("FallbackTypeConverter", getInjector(), 236 TypeConverter.class); 237 for (TypeConverter converter : converters) { 238 addFallbackConverter(converter); 239 } 240 } 241 242 /** 243 * Represents a mapping from one type (which can be null) to another 244 */ 245 protected static class TypeMapping { 246 Class toType; 247 Class fromType; 248 249 public TypeMapping(Class toType, Class fromType) { 250 this.toType = toType; 251 this.fromType = fromType; 252 } 253 254 public Class getFromType() { 255 return fromType; 256 } 257 258 public Class getToType() { 259 return toType; 260 } 261 262 @Override 263 public boolean equals(Object object) { 264 if (object instanceof TypeMapping) { 265 TypeMapping that = (TypeMapping)object; 266 return ObjectHelper.equal(this.fromType, that.fromType) 267 && ObjectHelper.equal(this.toType, that.toType); 268 } 269 return false; 270 } 271 272 @Override 273 public int hashCode() { 274 int answer = toType.hashCode(); 275 if (fromType != null) { 276 answer *= 37 + fromType.hashCode(); 277 } 278 return answer; 279 } 280 281 @Override 282 public String toString() { 283 return "[" + fromType + "=>" + toType + "]"; 284 } 285 } 286 }