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    }