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