Home | History | Annotate | Download | only in internal
      1 /**
      2  * Copyright (C) 2008 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.internal;
     18 
     19 import com.google.inject.TypeLiteral;
     20 import com.google.inject.internal.util.SourceProvider;
     21 import com.google.inject.matcher.AbstractMatcher;
     22 import com.google.inject.matcher.Matcher;
     23 import com.google.inject.matcher.Matchers;
     24 import com.google.inject.spi.TypeConverter;
     25 import com.google.inject.spi.TypeConverterBinding;
     26 
     27 import java.lang.reflect.InvocationTargetException;
     28 import java.lang.reflect.Method;
     29 import java.lang.reflect.Type;
     30 
     31 /**
     32  * Handles {@code Binder.convertToTypes} commands.
     33  *
     34  * @author crazybob (at) google.com (Bob Lee)
     35  * @author jessewilson (at) google.com (Jesse Wilson)
     36  */
     37 final class TypeConverterBindingProcessor extends AbstractProcessor {
     38 
     39   TypeConverterBindingProcessor(Errors errors) {
     40     super(errors);
     41   }
     42 
     43   /** Installs default converters for primitives, enums, and class literals. */
     44   static void prepareBuiltInConverters(InjectorImpl injector) {
     45     // Configure type converters.
     46     convertToPrimitiveType(injector, int.class, Integer.class);
     47     convertToPrimitiveType(injector, long.class, Long.class);
     48     convertToPrimitiveType(injector, boolean.class, Boolean.class);
     49     convertToPrimitiveType(injector, byte.class, Byte.class);
     50     convertToPrimitiveType(injector, short.class, Short.class);
     51     convertToPrimitiveType(injector, float.class, Float.class);
     52     convertToPrimitiveType(injector, double.class, Double.class);
     53 
     54     convertToClass(injector, Character.class, new TypeConverter() {
     55       public Object convert(String value, TypeLiteral<?> toType) {
     56         value = value.trim();
     57         if (value.length() != 1) {
     58           throw new RuntimeException("Length != 1.");
     59         }
     60         return value.charAt(0);
     61       }
     62 
     63       @Override public String toString() {
     64         return "TypeConverter<Character>";
     65       }
     66     });
     67 
     68     convertToClasses(injector, Matchers.subclassesOf(Enum.class), new TypeConverter() {
     69       @SuppressWarnings("unchecked")
     70       public Object convert(String value, TypeLiteral<?> toType) {
     71           return Enum.valueOf((Class) toType.getRawType(), value);
     72         }
     73 
     74         @Override public String toString() {
     75           return "TypeConverter<E extends Enum<E>>";
     76         }
     77       });
     78 
     79     internalConvertToTypes(injector, new AbstractMatcher<TypeLiteral<?>>() {
     80         public boolean matches(TypeLiteral<?> typeLiteral) {
     81           return typeLiteral.getRawType() == Class.class;
     82         }
     83 
     84         @Override public String toString() {
     85           return "Class<?>";
     86         }
     87       },
     88       new TypeConverter() {
     89         @SuppressWarnings("unchecked")
     90         public Object convert(String value, TypeLiteral<?> toType) {
     91           try {
     92             return Class.forName(value);
     93           } catch (ClassNotFoundException e) {
     94             throw new RuntimeException(e.getMessage());
     95           }
     96         }
     97 
     98         @Override public String toString() {
     99           return "TypeConverter<Class<?>>";
    100         }
    101       }
    102     );
    103   }
    104 
    105   private static <T> void convertToPrimitiveType(InjectorImpl injector, Class<T> primitiveType,
    106       final Class<T> wrapperType) {
    107     try {
    108       final Method parser = wrapperType.getMethod(
    109           "parse" + capitalize(primitiveType.getName()), String.class);
    110 
    111       TypeConverter typeConverter = new TypeConverter() {
    112         @SuppressWarnings("unchecked")
    113         public Object convert(String value, TypeLiteral<?> toType) {
    114           try {
    115             return parser.invoke(null, value);
    116           } catch (IllegalAccessException e) {
    117             throw new AssertionError(e);
    118           } catch (InvocationTargetException e) {
    119             throw new RuntimeException(e.getTargetException().getMessage());
    120           }
    121         }
    122 
    123         @Override public String toString() {
    124           return "TypeConverter<" + wrapperType.getSimpleName() + ">";
    125         }
    126       };
    127 
    128       convertToClass(injector, wrapperType, typeConverter);
    129     } catch (NoSuchMethodException e) {
    130       throw new AssertionError(e);
    131     }
    132   }
    133 
    134   private static <T> void convertToClass(InjectorImpl injector, Class<T> type,
    135       TypeConverter converter) {
    136     convertToClasses(injector, Matchers.identicalTo(type), converter);
    137   }
    138 
    139   private static void convertToClasses(InjectorImpl injector,
    140       final Matcher<? super Class<?>> typeMatcher, TypeConverter converter) {
    141     internalConvertToTypes(injector, new AbstractMatcher<TypeLiteral<?>>() {
    142       public boolean matches(TypeLiteral<?> typeLiteral) {
    143         Type type = typeLiteral.getType();
    144         if (!(type instanceof Class)) {
    145           return false;
    146         }
    147         Class<?> clazz = (Class<?>) type;
    148         return typeMatcher.matches(clazz);
    149       }
    150 
    151       @Override public String toString() {
    152         return typeMatcher.toString();
    153       }
    154     }, converter);
    155   }
    156 
    157   private static void internalConvertToTypes(InjectorImpl injector,
    158       Matcher<? super TypeLiteral<?>> typeMatcher,
    159       TypeConverter converter) {
    160     injector.state.addConverter(
    161         new TypeConverterBinding(SourceProvider.UNKNOWN_SOURCE, typeMatcher, converter));
    162   }
    163 
    164   @Override public Boolean visit(TypeConverterBinding command) {
    165     injector.state.addConverter(new TypeConverterBinding(
    166         command.getSource(), command.getTypeMatcher(), command.getTypeConverter()));
    167     return true;
    168   }
    169 
    170   private static String capitalize(String s) {
    171     if (s.length() == 0) {
    172       return s;
    173     }
    174     char first = s.charAt(0);
    175     char capitalized = Character.toUpperCase(first);
    176     return (first == capitalized)
    177         ? s
    178         : capitalized + s.substring(1);
    179   }
    180 
    181 }
    182