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: 37863 $ 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 inheritance-tree for the element types? 168 if (!fromType.equals(Object[].class)) { 169 fromSuperClass = Object[].class; 170 171 TypeConverter converter = getTypeConverter(toType, fromSuperClass); 172 if (converter == null) { 173 converter = findTypeConverter(toType, fromSuperClass, value); 174 } 175 if (converter != null) { 176 return converter; 177 } 178 } 179 } 180 181 // lets test for Object based converters 182 if (!fromType.equals(Object.class)) { 183 TypeConverter converter = getTypeConverter(toType, Object.class); 184 if (converter != null) { 185 return converter; 186 } 187 } 188 } 189 190 // lets try classes derived from this toType 191 if (fromType != null) { 192 Set<Map.Entry<TypeMapping, TypeConverter>> entries = typeMappings.entrySet(); 193 for (Map.Entry<TypeMapping, TypeConverter> entry : entries) { 194 TypeMapping key = entry.getKey(); 195 Class aToType = key.getToType(); 196 if (toType.isAssignableFrom(aToType)) { 197 if (key.getFromType().isAssignableFrom(fromType)) { 198 return entry.getValue(); 199 } 200 } 201 } 202 } 203 204 // TODO look at constructors of toType? 205 return null; 206 } 207 208 /** 209 * Checks if the registry is loaded and if not lazily load it 210 */ 211 protected synchronized void checkLoaded() { 212 if (!loaded) { 213 loaded = true; 214 try { 215 for (TypeConverterLoader typeConverterLoader : typeConverterLoaders) { 216 typeConverterLoader.load(this); 217 } 218 219 // lets try load any other fallback converters 220 try { 221 loadFallbackTypeConverters(); 222 } catch (NoFactoryAvailableException e) { 223 // ignore its fine to have none 224 } 225 } catch (Exception e) { 226 throw new RuntimeCamelException(e); 227 } 228 } 229 } 230 231 protected void loadFallbackTypeConverters() throws IOException, ClassNotFoundException { 232 FactoryFinder finder = new FactoryFinder(); 233 List<TypeConverter> converters = finder.newInstances("FallbackTypeConverter", getInjector(), 234 TypeConverter.class); 235 for (TypeConverter converter : converters) { 236 addFallbackConverter(converter); 237 } 238 } 239 240 /** 241 * Represents a mapping from one type (which can be null) to another 242 */ 243 protected static class TypeMapping { 244 Class toType; 245 Class fromType; 246 247 public TypeMapping(Class toType, Class fromType) { 248 this.toType = toType; 249 this.fromType = fromType; 250 } 251 252 public Class getFromType() { 253 return fromType; 254 } 255 256 public Class getToType() { 257 return toType; 258 } 259 260 @Override 261 public boolean equals(Object object) { 262 if (object instanceof TypeMapping) { 263 TypeMapping that = (TypeMapping)object; 264 return ObjectHelper.equal(this.fromType, that.fromType) 265 && ObjectHelper.equal(this.toType, that.toType); 266 } 267 return false; 268 } 269 270 @Override 271 public int hashCode() { 272 int answer = toType.hashCode(); 273 if (fromType != null) { 274 answer *= 37 + fromType.hashCode(); 275 } 276 return answer; 277 } 278 279 @Override 280 public String toString() { 281 return "[" + fromType + "=>" + toType + "]"; 282 } 283 } 284 }