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 17package com.google.inject.internal; 18 19import com.google.inject.TypeLiteral; 20import com.google.inject.internal.util.SourceProvider; 21import com.google.inject.matcher.AbstractMatcher; 22import com.google.inject.matcher.Matcher; 23import com.google.inject.matcher.Matchers; 24import com.google.inject.spi.TypeConverter; 25import com.google.inject.spi.TypeConverterBinding; 26 27import java.lang.reflect.InvocationTargetException; 28import java.lang.reflect.Method; 29import java.lang.reflect.Type; 30 31/** 32 * Handles {@code Binder.convertToTypes} commands. 33 * 34 * @author crazybob@google.com (Bob Lee) 35 * @author jessewilson@google.com (Jesse Wilson) 36 */ 37final 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