/* * Copyright (c) 2001-2007, Inversoft Inc., All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the License. */ package org.primeframework.mvc.parameter.convert; import java.lang.annotation.Annotation; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import org.primeframework.mvc.parameter.convert.annotation.ConverterAnnotation; import com.google.inject.Inject; import com.google.inject.Injector; import static java.util.Arrays.*; /** * This class is the manager for all the Converters. It loads the global type converters from Guice. Therefore, if you * want to supply a global custom type converter, just add it to a Guice module and place it in your classpath. Prime * will discover the module and load it up. * <p/> * A converter for a given type will be retrieved when the manager is queried for that type and all sub class of that * type, unless another converter is registered for a sub class of the type. For example, registering a convert for the * type Number would ensure that Integer, Double, Float, etc. used that converter for conversions. If a converter was * registered for Number and another converter for Double, the converter for Number would handle all sub-class of Number * (Integer, etc.) except Double. * * @author Brian Pontarelli */ public class DefaultConverterProvider implements ConverterProvider { private final Injector injector; private final Map<Class<?>, GlobalConverter> converters; @Inject public DefaultConverterProvider(Injector injector, Map<Class<?>, GlobalConverter> converters) { this.injector = injector; this.converters = converters; } /** * Returns the global type converter for the given type. This converter is either the converter associated with the * given type of associated with a super class of the given type. This principle also works with arrays. If the type * is an array, then what happens is that the array type is asked for its component type using the method * getComponentType and this type is used to query the manager. So, the converter registered for Number is returned * Double[] is queried (because Double is queried and since no converter was register for it, then Number is * checked). * <p/> * Normal types work the exact same way. First the type is checked and then its parents are checked until Object is * reached, in which case null is returned. * <p/> * Primitive values are treated as their wrapper classes. So, if int.class is passed into this method (queried) then * either a converter registered for Integer, or Number or null is returned depending on what converters have been * registered so far. * * @param type The type to start with when looking for converters * @return The converter or null if one was not found */ public GlobalConverter lookup(Class<?> type) { Class<?> localType = type; // If it is an array, just use the component type because TypeConverters // can convert to arrays while (localType.isArray()) { localType = localType.getComponentType(); } // The local type becomes null when it is an interface or a primitive and the // super class is asked for while (localType != null && localType != Object.class) { GlobalConverter converter = converters.get(localType); if (converter == null) { localType = localType.getSuperclass(); } else { return converter; } } localType = type; Queue<Class<?>> interfaces = new LinkedList<Class<?>>(asList(type.getInterfaces())); Class<?> inter; while ((inter = interfaces.poll()) != null) { // First, check the interface GlobalConverter converter = converters.get(inter); if (converter != null) { return converter; } // Next, append the interfaces for this interface interfaces.addAll(asList(inter.getInterfaces())); // If there are no more, go up to the super class if (interfaces.size() == 0 && !localType.isInterface()) { localType = localType.getSuperclass(); if (localType != Object.class) { interfaces.addAll(asList(localType.getInterfaces())); } } } return null; } /** * Returns the Converter for the given annotation. * * @param annotation The annotation. * @return The Converter. */ public AnnotationConverter lookup(Annotation annotation) { ConverterAnnotation ra = annotation.annotationType().getAnnotation(ConverterAnnotation.class); return injector.getInstance(ra.value()); } }