crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1 | /** |
| 2 | * Copyright (C) 2006 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; |
| 18 | |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 19 | import com.google.inject.internal.*; |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 20 | import com.google.inject.spi.*; |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 21 | import com.google.inject.util.Providers; |
kevinb9n | 225310e | 2007-02-20 04:12:01 +0000 | [diff] [blame] | 22 | import net.sf.cglib.reflect.FastClass; |
| 23 | import net.sf.cglib.reflect.FastMethod; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 24 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 25 | import java.lang.annotation.Annotation; |
| 26 | import java.lang.reflect.*; |
| 27 | import java.util.*; |
| 28 | |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 29 | /** |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 30 | * Default {@link Injector} implementation. |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 31 | * |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 32 | * @author crazybob@google.com (Bob Lee) |
limpbizkit | 3d58d6b | 2008-03-08 16:11:47 +0000 | [diff] [blame] | 33 | * @see InjectorBuilder |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 34 | */ |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 35 | class InjectorImpl implements Injector { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 36 | |
crazyboblee | fc9337f | 2007-01-26 00:51:34 +0000 | [diff] [blame] | 37 | /** |
| 38 | * Maps between primitive types and their wrappers and vice versa. |
| 39 | */ |
| 40 | private static final Map<Class<?>, Class<?>> PRIMITIVE_COUNTERPARTS; |
| 41 | static { |
| 42 | Map<Class<?>, Class<?>> primitiveToWrapper = |
| 43 | new HashMap<Class<?>, Class<?>>() {{ |
| 44 | put(int.class, Integer.class); |
| 45 | put(long.class, Long.class); |
| 46 | put(boolean.class, Boolean.class); |
| 47 | put(byte.class, Byte.class); |
| 48 | put(short.class, Short.class); |
| 49 | put(float.class, Float.class); |
| 50 | put(double.class, Double.class); |
| 51 | put(char.class, Character.class); |
| 52 | }}; |
| 53 | |
| 54 | Map<Class<?>, Class<?>> counterparts = new HashMap<Class<?>, Class<?>>(); |
| 55 | for (Map.Entry<Class<?>, Class<?>> entry : primitiveToWrapper.entrySet()) { |
| 56 | Class<?> key = entry.getKey(); |
| 57 | Class<?> value = entry.getValue(); |
| 58 | counterparts.put(key, value); |
| 59 | counterparts.put(value, key); |
| 60 | } |
| 61 | |
| 62 | PRIMITIVE_COUNTERPARTS = Collections.unmodifiableMap(counterparts); |
| 63 | } |
| 64 | |
dan.halem | 5d18743 | 2008-02-22 22:52:28 +0000 | [diff] [blame] | 65 | final Injector parentInjector; |
limpbizkit | 3d58d6b | 2008-03-08 16:11:47 +0000 | [diff] [blame] | 66 | final Map<Key<?>, BindingImpl<?>> explicitBindings |
| 67 | = new HashMap<Key<?>, BindingImpl<?>>(); |
| 68 | final BindingsMultimap bindingsMultimap = new BindingsMultimap(); |
| 69 | final Map<Class<? extends Annotation>, Scope> scopes |
| 70 | = new HashMap<Class<? extends Annotation>, Scope>(); |
| 71 | final List<MatcherAndConverter<?>> converters |
| 72 | = new ArrayList<MatcherAndConverter<?>>(); |
dan.halem | 5d18743 | 2008-02-22 22:52:28 +0000 | [diff] [blame] | 73 | final Map<Key<?>, BindingImpl<?>> parentBindings |
| 74 | = new HashMap<Key<?>, BindingImpl<?>>(); |
limpbizkit | 51515b5 | 2008-03-11 03:46:25 +0000 | [diff] [blame] | 75 | final Map<Object, Void> outstandingInjections |
| 76 | = new IdentityHashMap<Object, Void>(); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 77 | |
limpbizkit | b1d8ab4 | 2008-04-27 08:11:37 +0000 | [diff] [blame] | 78 | final ErrorHandler errorHandler; |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 79 | Reflection reflection; |
crazyboblee | 4727ee2 | 2007-01-30 03:13:38 +0000 | [diff] [blame] | 80 | |
limpbizkit | b1d8ab4 | 2008-04-27 08:11:37 +0000 | [diff] [blame] | 81 | InjectorImpl(Injector parentInjector, ErrorHandler errorHandler) { |
dan.halem | 5d18743 | 2008-02-22 22:52:28 +0000 | [diff] [blame] | 82 | this.parentInjector = parentInjector; |
limpbizkit | b1d8ab4 | 2008-04-27 08:11:37 +0000 | [diff] [blame] | 83 | this.errorHandler = errorHandler; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 84 | } |
| 85 | |
limpbizkit | 51515b5 | 2008-03-11 03:46:25 +0000 | [diff] [blame] | 86 | void validateOustandingInjections() { |
| 87 | for (Object toInject : outstandingInjections.keySet()) { |
| 88 | injectors.get(toInject.getClass()); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Performs creation-time injections on all objects that require it. Whenever |
| 94 | * fulfilling an injection depends on another object that requires injection, |
| 95 | * we use {@link InternalContext#ensureMemberInjected} to inject that member |
| 96 | * first. |
| 97 | * |
| 98 | * <p>If the two objects are codependent (directly or transitively), ordering |
| 99 | * of injection is arbitrary. |
| 100 | */ |
| 101 | void fulfillOutstandingInjections() { |
| 102 | callInContext(new ContextualCallable<Void>() { |
| 103 | public Void call(InternalContext context) { |
| 104 | // loop over a defensive copy, since ensureMemberInjected() mutates the |
| 105 | // outstandingInjections set |
| 106 | for (Object toInject : new ArrayList<Object>(outstandingInjections.keySet())) { |
| 107 | context.ensureMemberInjected(toInject); |
| 108 | } |
| 109 | return null; |
| 110 | } |
| 111 | }); |
| 112 | |
| 113 | if (!outstandingInjections.isEmpty()) { |
| 114 | throw new IllegalStateException("failed to satisfy " + outstandingInjections); |
| 115 | } |
| 116 | } |
| 117 | |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 118 | /** |
| 119 | * Indexes bindings by type. |
| 120 | */ |
| 121 | void index() { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 122 | for (BindingImpl<?> binding : explicitBindings.values()) { |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 123 | index(binding); |
| 124 | } |
| 125 | } |
| 126 | |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 127 | <T> void index(BindingImpl<T> binding) { |
crazyboblee | c3e8849 | 2007-02-25 22:36:58 +0000 | [diff] [blame] | 128 | bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding); |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 129 | } |
| 130 | |
kevinb9n | 225310e | 2007-02-20 04:12:01 +0000 | [diff] [blame] | 131 | // not test-covered |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 132 | public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) { |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 133 | return Collections.<Binding<T>>unmodifiableList( |
| 134 | bindingsMultimap.getAll(type)); |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 135 | } |
| 136 | |
kevinb9n | 225310e | 2007-02-20 04:12:01 +0000 | [diff] [blame] | 137 | // not test-covered |
crazyboblee | 4602a6f | 2007-02-15 02:45:18 +0000 | [diff] [blame] | 138 | <T> List<String> getNamesOfBindingAnnotations(TypeLiteral<T> type) { |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 139 | List<String> names = new ArrayList<String>(); |
| 140 | for (Binding<T> binding : findBindingsByType(type)) { |
crazyboblee | b1f2e68 | 2007-02-15 05:14:32 +0000 | [diff] [blame] | 141 | Key<T> key = binding.getKey(); |
| 142 | if (!key.hasAnnotationType()) { |
| 143 | names.add("[no annotation]"); |
| 144 | } else { |
| 145 | names.add(key.getAnnotationName()); |
| 146 | } |
crazyboblee | 62fcdde | 2007-02-03 02:10:13 +0000 | [diff] [blame] | 147 | } |
| 148 | return names; |
| 149 | } |
| 150 | |
crazyboblee | 9a3861b | 2007-02-20 03:37:31 +0000 | [diff] [blame] | 151 | /** |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 152 | * This is only used during Injector building. |
crazyboblee | 9a3861b | 2007-02-20 03:37:31 +0000 | [diff] [blame] | 153 | */ |
| 154 | void withDefaultSource(Object defaultSource, Runnable runnable) { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 155 | SourceProviders.withDefault(defaultSource, runnable); |
crazyboblee | 4727ee2 | 2007-01-30 03:13:38 +0000 | [diff] [blame] | 156 | } |
| 157 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 158 | /** |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 159 | * Returns the binding for {@code key}, or {@code null} if that binding |
| 160 | * cannot be resolved. |
| 161 | */ |
| 162 | public <T> BindingImpl<T> getBinding(Key<T> key) { |
| 163 | try { |
| 164 | return getBindingOrThrow(key); |
| 165 | } catch(ResolveFailedException e) { |
| 166 | return null; |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | /** |
dan.halem | 5d18743 | 2008-02-22 22:52:28 +0000 | [diff] [blame] | 171 | * Gets a binding implementation. First, it check to see if the parent has |
| 172 | * a binding. If the parent has a binding and the binding is scoped, it |
| 173 | * will use that binding. Otherwise, this checks for an explicit binding. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 174 | * If no explicit binding is found, it looks for a just-in-time binding. |
| 175 | */ |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 176 | public <T> BindingImpl<T> getBindingOrThrow(Key<T> key) throws ResolveFailedException { |
dan.halem | 5d18743 | 2008-02-22 22:52:28 +0000 | [diff] [blame] | 177 | if (parentInjector != null) { |
| 178 | BindingImpl<T> bindingImpl = getParentBinding(key); |
| 179 | if (bindingImpl != null) { |
| 180 | return bindingImpl; |
| 181 | } |
| 182 | } |
| 183 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 184 | // Check explicit bindings, i.e. bindings created by modules. |
| 185 | BindingImpl<T> binding = getExplicitBindingImpl(key); |
crazyboblee | a6e7398 | 2007-02-02 00:21:07 +0000 | [diff] [blame] | 186 | if (binding != null) { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 187 | return binding; |
crazyboblee | 07e4182 | 2006-11-21 01:27:08 +0000 | [diff] [blame] | 188 | } |
| 189 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 190 | // Look for an on-demand binding. |
| 191 | return getJitBindingImpl(key); |
| 192 | } |
crazyboblee | f33d23e | 2007-02-12 04:17:48 +0000 | [diff] [blame] | 193 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 194 | |
dan.halem | 5d18743 | 2008-02-22 22:52:28 +0000 | [diff] [blame] | 195 | /** |
| 196 | * Checks the parent injector for a scoped binding, and if available, creates |
| 197 | * an appropriate binding local to this injector and remembers it. |
| 198 | */ |
| 199 | @SuppressWarnings("unchecked") |
| 200 | private <T> BindingImpl<T> getParentBinding(Key<T> key) { |
| 201 | BindingImpl<T> bindingImpl; |
| 202 | synchronized(parentBindings) { |
| 203 | // null values will mean that the parent doesn't have this binding |
| 204 | if (!parentBindings.containsKey(key)) { |
| 205 | Binding<T> binding = null; |
| 206 | try { |
| 207 | binding = parentInjector.getBinding(key); |
| 208 | } catch (ConfigurationException e) { |
| 209 | // if this happens, the parent can't create this key, and we ignore it |
| 210 | } |
| 211 | if (binding != null |
| 212 | && binding.getScope() != null |
| 213 | && !binding.getScope().equals(Scopes.NO_SCOPE)) { |
| 214 | bindingImpl = new ProviderInstanceBindingImpl( |
| 215 | this, |
| 216 | key, |
| 217 | binding.getSource(), |
| 218 | new InternalFactoryToProviderAdapter(binding.getProvider(), |
| 219 | binding.getSource()), |
| 220 | Scopes.NO_SCOPE, |
| 221 | binding.getProvider()); |
| 222 | } else { |
| 223 | bindingImpl = null; |
| 224 | } |
| 225 | parentBindings.put(key, bindingImpl); |
| 226 | } else { |
| 227 | bindingImpl = (BindingImpl<T>) parentBindings.get(key); |
| 228 | } |
| 229 | } |
| 230 | return bindingImpl; |
| 231 | } |
| 232 | |
crazyboblee | 552472f | 2007-09-07 16:52:39 +0000 | [diff] [blame] | 233 | public <T> Binding<T> getBinding(Class<T> type) { |
| 234 | return getBinding(Key.get(type)); |
| 235 | } |
| 236 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 237 | /** |
| 238 | * Gets a binding which was specified explicitly in a module. |
| 239 | */ |
| 240 | @SuppressWarnings("unchecked") |
| 241 | <T> BindingImpl<T> getExplicitBindingImpl(Key<T> key) { |
| 242 | return (BindingImpl<T>) explicitBindings.get(key); |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Gets a just-in-time binding. This could be an injectable class (including |
| 247 | * those with @ImplementedBy), an automatically converted constant, a |
| 248 | * Provider<X> binding, etc. |
| 249 | */ |
| 250 | @SuppressWarnings("unchecked") |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 251 | <T> BindingImpl<T> getJitBindingImpl(Key<T> key) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 252 | synchronized (jitBindings) { |
| 253 | // Support null values. |
| 254 | if (!jitBindings.containsKey(key)) { |
| 255 | BindingImpl<T> binding = createBindingJustInTime(key); |
| 256 | jitBindings.put(key, binding); |
| 257 | return binding; |
| 258 | } else { |
| 259 | return (BindingImpl<T>) jitBindings.get(key); |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 260 | } |
limpbizkit | c808df0 | 2007-08-25 03:25:13 +0000 | [diff] [blame] | 261 | } |
crazyboblee | 07e4182 | 2006-11-21 01:27:08 +0000 | [diff] [blame] | 262 | } |
| 263 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 264 | /** Just-in-time binding cache. */ |
| 265 | final Map<Key<?>, BindingImpl<?>> jitBindings |
| 266 | = new HashMap<Key<?>, BindingImpl<?>>(); |
| 267 | |
| 268 | /** |
| 269 | * Returns true if the key type is Provider<?> (but not a subclass of |
| 270 | * Provider<?>). |
| 271 | */ |
| 272 | static boolean isProvider(Key<?> key) { |
| 273 | return key.getTypeLiteral().getRawType().equals(Provider.class); |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Creates a synthetic binding to Provider<T>, i.e. a binding to the provider |
| 278 | * from Binding<T>. |
| 279 | */ |
| 280 | private <T> BindingImpl<Provider<T>> createProviderBinding( |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 281 | Key<Provider<T>> key) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 282 | Type providerType = key.getTypeLiteral().getType(); |
| 283 | |
| 284 | // If the Provider has no type parameter (raw Provider)... |
| 285 | if (!(providerType instanceof ParameterizedType)) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 286 | throw new ResolveFailedException(ErrorMessages.CANNOT_INJECT_RAW_PROVIDER); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | Type entryType |
| 290 | = ((ParameterizedType) providerType).getActualTypeArguments()[0]; |
| 291 | |
| 292 | // This cast is safe. |
| 293 | @SuppressWarnings("unchecked") |
| 294 | Key<T> providedKey = (Key<T>) key.ofType(entryType); |
| 295 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 296 | return new ProviderBindingImpl<T>(this, key, getBindingOrThrow(providedKey)); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 297 | } |
| 298 | |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 299 | void handleMissingBinding(Object source, Key<?> key) { |
| 300 | List<String> otherNames = getNamesOfBindingAnnotations(key.getTypeLiteral()); |
| 301 | |
| 302 | if (source instanceof Member) { |
| 303 | source = StackTraceElements.forMember((Member) source); |
| 304 | } |
| 305 | |
| 306 | if (otherNames.isEmpty()) { |
| 307 | errorHandler.handle(source, ErrorMessages.MISSING_BINDING, key); |
| 308 | } |
| 309 | else { |
| 310 | errorHandler.handle(source, |
| 311 | ErrorMessages.MISSING_BINDING_BUT_OTHERS_EXIST, key, otherNames); |
| 312 | } |
| 313 | } |
| 314 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 315 | static class ProviderBindingImpl<T> extends BindingImpl<Provider<T>> |
| 316 | implements ProviderBinding<T> { |
| 317 | |
| 318 | final Binding<T> providedBinding; |
| 319 | |
| 320 | ProviderBindingImpl(InjectorImpl injector, Key<Provider<T>> key, |
| 321 | Binding<T> providedBinding) { |
| 322 | super(injector, key, SourceProviders.UNKNOWN_SOURCE, |
| 323 | createInternalFactory(providedBinding), Scopes.NO_SCOPE); |
| 324 | this.providedBinding = providedBinding; |
| 325 | } |
| 326 | |
| 327 | static <T> InternalFactory<Provider<T>> createInternalFactory( |
| 328 | Binding<T> providedBinding) { |
| 329 | final Provider<T> provider = providedBinding.getProvider(); |
| 330 | return new InternalFactory<Provider<T>>() { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 331 | public Provider<T> get(InternalContext context, |
| 332 | InjectionPoint injectionPoint) { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 333 | return provider; |
| 334 | } |
| 335 | }; |
| 336 | } |
| 337 | |
| 338 | public void accept(BindingVisitor<? super Provider<T>> bindingVisitor) { |
| 339 | bindingVisitor.visit(this); |
| 340 | } |
| 341 | |
| 342 | public Binding<T> getTarget() { |
| 343 | return providedBinding; |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | <T> BindingImpl<T> invalidBinding(Class<T> clazz) { |
| 348 | return invalidBinding(Key.get(clazz)); |
| 349 | } |
| 350 | |
| 351 | <T> BindingImpl<T> invalidBinding(Key<T> key) { |
| 352 | return new InvalidBindingImpl<T>( |
| 353 | this, key, SourceProviders.defaultSource()); |
| 354 | } |
| 355 | |
| 356 | /** |
| 357 | * Gets the binding corresponding to a primitives wrapper type or a wrapper |
| 358 | * type's primitive. The compiler treats them interchangeably, so we do, too. |
| 359 | */ |
| 360 | <T> BindingImpl<T> getBoxedOrUnboxedBinding(Key<T> key) { |
| 361 | // This is a safe cast, just as this is safe: Class<Integer> c = int.class; |
| 362 | @SuppressWarnings("unchecked") |
| 363 | Class<T> primitiveCounterpart |
| 364 | = (Class<T>) PRIMITIVE_COUNTERPARTS.get(key.getRawType()); |
| 365 | if (primitiveCounterpart != null) { |
| 366 | // Do we need to search more than explicit bindings? I don't think so. |
| 367 | // Constant type conversion already supports both primitives and their |
| 368 | // wrappers, and limiting this to explicit bindings means we don't have |
| 369 | // to worry about recursion. |
| 370 | return getExplicitBindingImpl(key.ofType(primitiveCounterpart)); |
| 371 | } |
| 372 | |
| 373 | return null; |
| 374 | } |
| 375 | |
| 376 | /** |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 377 | * Converts a constant string binding to the required type. |
| 378 | * |
| 379 | * <p>If the required type is elligible for conversion and a constant string |
| 380 | * binding is found but the actual conversion fails, an error is generated. |
| 381 | * |
| 382 | * <p>If the type is not elligible for conversion or a constant string |
| 383 | * binding is not found, this method returns null. |
| 384 | */ |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 385 | private <T> BindingImpl<T> convertConstantStringBinding(Key<T> key) |
| 386 | throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 387 | // Find a constant string binding. |
| 388 | Key<String> stringKey = key.ofType(String.class); |
| 389 | BindingImpl<String> stringBinding = getExplicitBindingImpl(stringKey); |
| 390 | if (stringBinding == null || !stringBinding.isConstant()) { |
| 391 | // No constant string binding found. |
| 392 | return null; |
| 393 | } |
| 394 | |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 395 | String stringValue = stringBinding.getProvider().get(); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 396 | |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 397 | // Find a matching type converter. |
| 398 | TypeLiteral<T> type = key.getTypeLiteral(); |
| 399 | MatcherAndConverter<?> matchingConverter = null; |
| 400 | for (MatcherAndConverter<?> converter : converters) { |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 401 | if (converter.getTypeMatcher().matches(type)) { |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 402 | if (matchingConverter != null) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 403 | throw new ResolveFailedException(ErrorMessages.AMBIGUOUS_TYPE_CONVERSION, |
| 404 | stringValue, type, matchingConverter, converter); |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 405 | } |
| 406 | |
| 407 | matchingConverter = converter; |
| 408 | } |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 409 | } |
| 410 | |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 411 | if (matchingConverter == null) { |
| 412 | // No converter can handle the given type. |
| 413 | return null; |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 414 | } |
| 415 | |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 416 | // Try to convert the string. A failed conversion results in an error. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 417 | try { |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 418 | // This cast is safe because we double check below. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 419 | @SuppressWarnings("unchecked") |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 420 | T converted = (T) matchingConverter.getTypeConverter() |
| 421 | .convert(stringValue, key.getTypeLiteral()); |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 422 | |
| 423 | if (converted == null) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 424 | throw new ResolveFailedException(ErrorMessages.CONVERTER_RETURNED_NULL); |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | // We have to filter out primitive types because an Integer is not an |
| 428 | // instance of int, and we provide converters for all the primitive types |
| 429 | // and know that they work anyway. |
| 430 | if (!type.rawType.isPrimitive() |
| 431 | && !type.getRawType().isInstance(converted)) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 432 | throw new ResolveFailedException(ErrorMessages.CONVERSION_TYPE_ERROR, converted, type); |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 433 | } |
| 434 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 435 | return new ConvertedConstantBindingImpl<T>( |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 436 | this, key, converted, stringBinding); |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 437 | } catch (ResolveFailedException e) { |
| 438 | throw e; |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 439 | } catch (Exception e) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 440 | throw new ResolveFailedException(ErrorMessages.CONVERSION_ERROR, stringValue, |
crazyboblee | 7c9d779 | 2007-09-09 03:41:05 +0000 | [diff] [blame] | 441 | stringBinding.getSource(), type, matchingConverter, e.getMessage()); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 442 | } |
| 443 | } |
| 444 | |
| 445 | private static class ConvertedConstantBindingImpl<T> extends BindingImpl<T> |
| 446 | implements ConvertedConstantBinding<T> { |
| 447 | |
| 448 | final T value; |
| 449 | final Provider<T> provider; |
| 450 | final Binding<String> originalBinding; |
| 451 | |
| 452 | ConvertedConstantBindingImpl(InjectorImpl injector, Key<T> key, T value, |
| 453 | Binding<String> originalBinding) { |
| 454 | super(injector, key, SourceProviders.UNKNOWN_SOURCE, |
| 455 | new ConstantFactory<T>(value), Scopes.NO_SCOPE); |
| 456 | this.value = value; |
| 457 | this.provider = Providers.of(value); |
| 458 | this.originalBinding = originalBinding; |
| 459 | } |
| 460 | |
| 461 | @Override |
| 462 | public Provider<T> getProvider() { |
| 463 | return this.provider; |
| 464 | } |
| 465 | |
| 466 | public void accept(BindingVisitor<? super T> bindingVisitor) { |
| 467 | bindingVisitor.visit(this); |
| 468 | } |
| 469 | |
| 470 | public T getValue() { |
| 471 | return this.value; |
| 472 | } |
| 473 | |
| 474 | public Binding<String> getOriginal() { |
| 475 | return this.originalBinding; |
| 476 | } |
| 477 | |
| 478 | @Override |
| 479 | public String toString() { |
| 480 | return new ToStringBuilder(ConvertedConstantBinding.class) |
| 481 | .add("key", key) |
| 482 | .add("value", value) |
| 483 | .add("original", originalBinding) |
| 484 | .toString(); |
| 485 | } |
| 486 | } |
| 487 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 488 | <T> BindingImpl<T> createBindingFromType(Class<T> type) |
| 489 | throws ResolveFailedException { |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 490 | return createBindingFromType(type, null, SourceProviders.defaultSource()); |
| 491 | } |
| 492 | |
| 493 | <T> BindingImpl<T> createBindingFromType(Class<T> type, Scope scope, |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 494 | Object source) throws ResolveFailedException { |
limpbizkit | 5141187 | 2008-05-13 22:15:29 +0000 | [diff] [blame] | 495 | BindingImpl<T> binding = createUnitializedBinding(type, scope, source); |
| 496 | initializeBinding(binding); |
| 497 | return binding; |
| 498 | } |
| 499 | |
| 500 | <T> void initializeBinding(BindingImpl<T> binding) throws ResolveFailedException { |
| 501 | // Put the partially constructed binding in the map a little early. This |
| 502 | // enables us to handle circular dependencies. |
| 503 | // Example: FooImpl -> BarImpl -> FooImpl. |
| 504 | // Note: We don't need to synchronize on jitBindings during injector |
| 505 | // creation. |
| 506 | if (binding instanceof ClassBindingImpl<?>) { |
| 507 | Key<T> key = binding.getKey(); |
| 508 | jitBindings.put(key, binding); |
| 509 | boolean successful = false; |
| 510 | try { |
| 511 | binding.initialize(this); |
| 512 | successful = true; |
| 513 | } finally { |
| 514 | if (!successful) { |
| 515 | jitBindings.remove(key); |
| 516 | } |
| 517 | } |
| 518 | } |
| 519 | } |
| 520 | |
| 521 | /** |
| 522 | * Creates a binding for an injectable type with the given scope. Looks for |
| 523 | * a scope on the type if none is specified. |
| 524 | */ |
| 525 | <T> BindingImpl<T> createUnitializedBinding(Class<T> type, |
| 526 | Scope scope, Object source) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 527 | // Don't try to inject primitives, arrays, or enums. |
| 528 | if (type.isArray() || type.isEnum() || type.isPrimitive()) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 529 | throw new ResolveFailedException(ErrorMessages.MISSING_BINDING, type); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 530 | } |
| 531 | |
| 532 | // Handle @ImplementedBy |
| 533 | ImplementedBy implementedBy = type.getAnnotation(ImplementedBy.class); |
| 534 | if (implementedBy != null) { |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 535 | // TODO: Scope internal factory. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 536 | return createImplementedByBinding(type, implementedBy); |
| 537 | } |
| 538 | |
| 539 | // Handle @ProvidedBy. |
| 540 | ProvidedBy providedBy = type.getAnnotation(ProvidedBy.class); |
| 541 | if (providedBy != null) { |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 542 | // TODO: Scope internal factory. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 543 | return createProvidedByBinding(type, providedBy); |
| 544 | } |
| 545 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 546 | // We can't inject abstract classes. |
| 547 | // TODO: Method interceptors could actually enable us to implement |
| 548 | // abstract types. Should we remove this restriction? |
| 549 | if (Modifier.isAbstract(type.getModifiers())) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 550 | throw new ResolveFailedException(ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, type); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 551 | } |
| 552 | |
| 553 | // Error: Inner class. |
| 554 | if (Classes.isInnerClass(type)) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 555 | throw new ResolveFailedException(ErrorMessages.CANNOT_INJECT_INNER_CLASS, type); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 556 | } |
| 557 | |
| 558 | if (scope == null) { |
| 559 | scope = Scopes.getScopeForType(type, scopes, errorHandler); |
| 560 | } |
| 561 | |
| 562 | Key<T> key = Key.get(type); |
| 563 | |
| 564 | LateBoundConstructor<T> lateBoundConstructor |
| 565 | = new LateBoundConstructor<T>(); |
| 566 | InternalFactory<? extends T> scopedFactory |
| 567 | = Scopes.scope(key, this, lateBoundConstructor, scope); |
| 568 | |
limpbizkit | 5141187 | 2008-05-13 22:15:29 +0000 | [diff] [blame] | 569 | return new ClassBindingImpl<T>(this, key, source, scopedFactory, scope, lateBoundConstructor); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 570 | } |
| 571 | |
| 572 | static class LateBoundConstructor<T> implements InternalFactory<T> { |
| 573 | |
| 574 | ConstructorInjector<T> constructorInjector; |
| 575 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 576 | void bind(InjectorImpl injector, Class<T> implementation) |
| 577 | throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 578 | this.constructorInjector = injector.getConstructor(implementation); |
| 579 | } |
| 580 | |
| 581 | @SuppressWarnings("unchecked") |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 582 | public T get(InternalContext context, InjectionPoint<?> injectionPoint) { |
limpbizkit | 00d513f | 2008-05-17 07:51:07 +0000 | [diff] [blame] | 583 | if (constructorInjector == null) { |
| 584 | throw new IllegalStateException("Construct before bind, " + constructorInjector); |
| 585 | } |
| 586 | |
crazyboblee | 552472f | 2007-09-07 16:52:39 +0000 | [diff] [blame] | 587 | // This may not actually be safe because it could return a super type |
| 588 | // of T (if that's all the client needs), but it should be OK in |
| 589 | // practice thanks to the wonders of erasure. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 590 | return (T) constructorInjector.construct( |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 591 | context, injectionPoint.getKey().getRawType()); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 592 | } |
| 593 | } |
| 594 | |
| 595 | /** |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 596 | * Creates a binding for a type annotated with @ProvidedBy. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 597 | */ |
| 598 | <T> BindingImpl<T> createProvidedByBinding(final Class<T> type, |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 599 | ProvidedBy providedBy) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 600 | final Class<? extends Provider<?>> providerType = providedBy.value(); |
| 601 | |
| 602 | // Make sure it's not the same type. TODO: Can we check for deeper loops? |
| 603 | if (providerType == type) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 604 | throw new ResolveFailedException(ErrorMessages.RECURSIVE_PROVIDER_TYPE); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 605 | } |
| 606 | |
| 607 | // TODO: Make sure the provided type extends type. We at least check |
| 608 | // the type at runtime below. |
| 609 | |
| 610 | // Assume the provider provides an appropriate type. We double check at |
| 611 | // runtime. |
| 612 | @SuppressWarnings("unchecked") |
| 613 | Key<? extends Provider<T>> providerKey |
| 614 | = (Key<? extends Provider<T>>) Key.get(providerType); |
| 615 | final BindingImpl<? extends Provider<?>> providerBinding |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 616 | = getBindingOrThrow(providerKey); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 617 | |
| 618 | InternalFactory<T> internalFactory = new InternalFactory<T>() { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 619 | public T get(InternalContext context, InjectionPoint injectionPoint) { |
| 620 | Provider<?> provider |
| 621 | = providerBinding.internalFactory.get(context, injectionPoint); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 622 | Object o = provider.get(); |
| 623 | try { |
| 624 | return type.cast(o); |
| 625 | } catch (ClassCastException e) { |
| 626 | errorHandler.handle(StackTraceElements.forType(type), |
| 627 | ErrorMessages.SUBTYPE_NOT_PROVIDED, providerType, type); |
| 628 | throw new AssertionError(); |
| 629 | } |
| 630 | } |
| 631 | }; |
| 632 | |
| 633 | return new LinkedProviderBindingImpl<T>(this, Key.get(type), |
| 634 | StackTraceElements.forType(type), internalFactory, Scopes.NO_SCOPE, |
| 635 | providerKey); |
| 636 | } |
| 637 | |
| 638 | /** |
| 639 | * Creates a binding for a type annotated with @ImplementedBy. |
| 640 | */ |
| 641 | <T> BindingImpl<T> createImplementedByBinding(Class<T> type, |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 642 | ImplementedBy implementedBy) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 643 | // TODO: Use scope annotation on type if present. Right now, we always |
| 644 | // use NO_SCOPE. |
| 645 | |
| 646 | Class<?> implementationType = implementedBy.value(); |
| 647 | |
| 648 | // Make sure it's not the same type. TODO: Can we check for deeper cycles? |
| 649 | if (implementationType == type) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 650 | throw new ResolveFailedException(ErrorMessages.RECURSIVE_IMPLEMENTATION_TYPE); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 651 | } |
| 652 | |
| 653 | // Make sure implementationType extends type. |
| 654 | if (!type.isAssignableFrom(implementationType)) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 655 | throw new ResolveFailedException(ErrorMessages.NOT_A_SUBTYPE, implementationType, type); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 656 | } |
| 657 | |
| 658 | // After the preceding check, this cast is safe. |
| 659 | @SuppressWarnings("unchecked") |
| 660 | Class<? extends T> subclass = (Class<? extends T>) implementationType; |
| 661 | |
| 662 | // Look up the target binding. |
| 663 | final BindingImpl<? extends T> targetBinding |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 664 | = getBindingOrThrow(Key.get(subclass)); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 665 | |
| 666 | InternalFactory<T> internalFactory = new InternalFactory<T>() { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 667 | public T get(InternalContext context, InjectionPoint<?> injectionPoint) { |
| 668 | return targetBinding.internalFactory.get(context, injectionPoint); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 669 | } |
| 670 | }; |
| 671 | |
| 672 | return new LinkedBindingImpl<T>(this, Key.get(type), |
| 673 | StackTraceElements.forType(type), internalFactory, Scopes.NO_SCOPE, |
| 674 | Key.get(subclass)); |
| 675 | } |
| 676 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 677 | <T> BindingImpl<T> createBindingJustInTime(Key<T> key) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 678 | // Handle cases where T is a Provider<?>. |
| 679 | if (isProvider(key)) { |
crazyboblee | 5d57569 | 2007-09-07 17:42:28 +0000 | [diff] [blame] | 680 | // These casts are safe. We know T extends Provider<X> and that given |
| 681 | // Key<Provider<X>>, createProviderBinding() will return |
| 682 | // BindingImpl<Provider<X>>. |
| 683 | @SuppressWarnings({ "UnnecessaryLocalVariable", "unchecked" }) |
| 684 | BindingImpl<T> binding |
| 685 | = (BindingImpl<T>) createProviderBinding((Key) key); |
| 686 | return binding; |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 687 | } |
| 688 | |
| 689 | // Treat primitive types and their wrappers interchangeably. |
| 690 | BindingImpl<T> boxedOrUnboxed = getBoxedOrUnboxedBinding(key); |
| 691 | if (boxedOrUnboxed != null) { |
| 692 | return boxedOrUnboxed; |
| 693 | } |
| 694 | |
| 695 | // Try to convert a constant string binding to the requested type. |
| 696 | BindingImpl<T> convertedBinding = convertConstantStringBinding(key); |
| 697 | if (convertedBinding != null) { |
| 698 | return convertedBinding; |
| 699 | } |
| 700 | |
| 701 | // If the key has an annotation... |
| 702 | if (key.hasAnnotationType()) { |
| 703 | // Look for a binding without annotation attributes or return null. |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 704 | if (key.hasAttributes()) { |
| 705 | try { |
| 706 | return getBindingOrThrow(key.withoutAttributes()); |
| 707 | } catch (ResolveFailedException ignored) { |
| 708 | // throw with a more appropriate message below |
| 709 | } |
| 710 | } |
| 711 | throw new ResolveFailedException(ErrorMessages.MISSING_BINDING, key); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 712 | } |
| 713 | |
limpbizkit | 9b0be9f | 2008-03-28 06:12:41 +0000 | [diff] [blame] | 714 | // Create a binding based on the raw type. |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 715 | @SuppressWarnings("unchecked") |
limpbizkit | 9b0be9f | 2008-03-28 06:12:41 +0000 | [diff] [blame] | 716 | Class<T> clazz = (Class<T>) key.getTypeLiteral().getRawType(); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 717 | return createBindingFromType(clazz); |
| 718 | } |
| 719 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 720 | <T> InternalFactory<? extends T> getInternalFactory(Key<T> key) |
| 721 | throws ResolveFailedException { |
| 722 | return getBindingOrThrow(key).internalFactory; |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 723 | } |
| 724 | |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 725 | /** |
| 726 | * Field and method injectors. |
| 727 | */ |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 728 | final Map<Class<?>, List<SingleMemberInjector>> injectors |
| 729 | = new ReferenceCache<Class<?>, List<SingleMemberInjector>>() { |
| 730 | protected List<SingleMemberInjector> create(Class<?> key) { |
| 731 | List<SingleMemberInjector> injectors |
| 732 | = new ArrayList<SingleMemberInjector>(); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 733 | addInjectors(key, injectors); |
| 734 | return injectors; |
| 735 | } |
| 736 | }; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 737 | |
| 738 | /** |
| 739 | * Recursively adds injectors for fields and methods from the given class to |
| 740 | * the given list. Injects parent classes before sub classes. |
| 741 | */ |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 742 | void addInjectors(Class clazz, List<SingleMemberInjector> injectors) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 743 | if (clazz == Object.class) { |
| 744 | return; |
| 745 | } |
| 746 | |
| 747 | // Add injectors for superclass first. |
| 748 | addInjectors(clazz.getSuperclass(), injectors); |
| 749 | |
| 750 | // TODO (crazybob): Filter out overridden members. |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 751 | addSingleInjectorsForFields(clazz.getDeclaredFields(), false, injectors); |
| 752 | addSingleInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 753 | } |
| 754 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 755 | void addSingleInjectorsForMethods(Method[] methods, boolean statics, |
| 756 | List<SingleMemberInjector> injectors) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 757 | addInjectorsForMembers(Arrays.asList(methods), statics, injectors, |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 758 | new SingleInjectorFactory<Method>() { |
| 759 | public SingleMemberInjector create(InjectorImpl injector, |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 760 | Method method) throws ResolveFailedException { |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 761 | return new SingleMethodInjector(injector, method); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 762 | } |
| 763 | }); |
| 764 | } |
| 765 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 766 | void addSingleInjectorsForFields(Field[] fields, boolean statics, |
| 767 | List<SingleMemberInjector> injectors) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 768 | addInjectorsForMembers(Arrays.asList(fields), statics, injectors, |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 769 | new SingleInjectorFactory<Field>() { |
| 770 | public SingleMemberInjector create(InjectorImpl injector, |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 771 | Field field) throws ResolveFailedException { |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 772 | return new SingleFieldInjector(injector, field); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 773 | } |
| 774 | }); |
| 775 | } |
| 776 | |
| 777 | <M extends Member & AnnotatedElement> void addInjectorsForMembers( |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 778 | List<M> members, boolean statics, List<SingleMemberInjector> injectors, |
| 779 | SingleInjectorFactory<M> injectorFactory) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 780 | for (M member : members) { |
| 781 | if (isStatic(member) == statics) { |
crazyboblee | 9a3861b | 2007-02-20 03:37:31 +0000 | [diff] [blame] | 782 | Inject inject = member.getAnnotation(Inject.class); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 783 | if (inject != null) { |
| 784 | try { |
crazyboblee | 4602a6f | 2007-02-15 02:45:18 +0000 | [diff] [blame] | 785 | injectors.add(injectorFactory.create(this, member)); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 786 | } |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 787 | catch (ResolveFailedException e) { |
crazyboblee | 4602a6f | 2007-02-15 02:45:18 +0000 | [diff] [blame] | 788 | if (!inject.optional()) { |
crazyboblee | 4727ee2 | 2007-01-30 03:13:38 +0000 | [diff] [blame] | 789 | // TODO: Report errors for more than one parameter per member. |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 790 | errorHandler.handle(member, e.getMessage()); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 791 | } |
| 792 | } |
| 793 | } |
| 794 | } |
| 795 | } |
| 796 | } |
| 797 | |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 798 | Map<Key<?>, BindingImpl<?>> internalBindings() { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 799 | return explicitBindings; |
crazyboblee | a6e7398 | 2007-02-02 00:21:07 +0000 | [diff] [blame] | 800 | } |
| 801 | |
kevinb9n | 225310e | 2007-02-20 04:12:01 +0000 | [diff] [blame] | 802 | // not test-covered |
crazyboblee | a6e7398 | 2007-02-02 00:21:07 +0000 | [diff] [blame] | 803 | public Map<Key<?>, Binding<?>> getBindings() { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 804 | return Collections.<Key<?>, Binding<?>>unmodifiableMap(explicitBindings); |
crazyboblee | a6e7398 | 2007-02-02 00:21:07 +0000 | [diff] [blame] | 805 | } |
| 806 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 807 | interface SingleInjectorFactory<M extends Member & AnnotatedElement> { |
| 808 | SingleMemberInjector create(InjectorImpl injector, M member) |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 809 | throws ResolveFailedException; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 810 | } |
| 811 | |
| 812 | private boolean isStatic(Member member) { |
| 813 | return Modifier.isStatic(member.getModifiers()); |
| 814 | } |
| 815 | |
kevinb9n | 48d1307 | 2007-02-12 18:21:26 +0000 | [diff] [blame] | 816 | private static class BindingsMultimap { |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 817 | private final Map<TypeLiteral<?>, List<? extends BindingImpl<?>>> map |
| 818 | = new HashMap<TypeLiteral<?>, List<? extends BindingImpl<?>>>(); |
kevinb9n | 48d1307 | 2007-02-12 18:21:26 +0000 | [diff] [blame] | 819 | |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 820 | public <T> void put(TypeLiteral<T> type, BindingImpl<T> binding) { |
| 821 | List<BindingImpl<T>> bindingsForThisType = getFromMap(type); |
kevinb9n | 48d1307 | 2007-02-12 18:21:26 +0000 | [diff] [blame] | 822 | if (bindingsForThisType == null) { |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 823 | bindingsForThisType = new ArrayList<BindingImpl<T>>(); |
kevinb9n | 48d1307 | 2007-02-12 18:21:26 +0000 | [diff] [blame] | 824 | // We only put matching entries into the map |
| 825 | map.put(type, bindingsForThisType); |
| 826 | } |
| 827 | bindingsForThisType.add(binding); |
| 828 | } |
| 829 | |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 830 | public <T> List<BindingImpl<T>> getAll(TypeLiteral<T> type) { |
| 831 | List<BindingImpl<T>> list = getFromMap(type); |
| 832 | return list == null ? Collections.<BindingImpl<T>>emptyList() : list; |
kevinb9n | 48d1307 | 2007-02-12 18:21:26 +0000 | [diff] [blame] | 833 | } |
| 834 | |
| 835 | // safe because we only put matching entries into the map |
| 836 | @SuppressWarnings("unchecked") |
crazyboblee | 1fc4978 | 2007-02-25 21:02:47 +0000 | [diff] [blame] | 837 | private <T> List<BindingImpl<T>> getFromMap(TypeLiteral<T> type) { |
| 838 | return (List<BindingImpl<T>>) map.get(type); |
kevinb9n | 48d1307 | 2007-02-12 18:21:26 +0000 | [diff] [blame] | 839 | } |
| 840 | } |
| 841 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 842 | class SingleFieldInjector implements SingleMemberInjector { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 843 | |
| 844 | final Field field; |
| 845 | final InternalFactory<?> factory; |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 846 | final InjectionPoint<?> injectionPoint; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 847 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 848 | public SingleFieldInjector(final InjectorImpl injector, Field field) |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 849 | throws ResolveFailedException { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 850 | this.field = field; |
crazyboblee | 4727ee2 | 2007-01-30 03:13:38 +0000 | [diff] [blame] | 851 | |
| 852 | // Ewwwww... |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 853 | field.setAccessible(true); |
| 854 | |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 855 | final Key<?> key = Keys.get( |
crazyboblee | 4602a6f | 2007-02-15 02:45:18 +0000 | [diff] [blame] | 856 | field.getGenericType(), field, field.getAnnotations(), errorHandler); |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 857 | factory = new ResolvingCallable<InternalFactory<?>>() { |
| 858 | public InternalFactory<?> call() throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 859 | return injector.getInternalFactory(key); |
| 860 | } |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 861 | }.runWithDefaultSource(StackTraceElements.forMember(field)); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 862 | |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 863 | this.injectionPoint = InjectionPoint.newInstance(field, |
limpbizkit | c808df0 | 2007-08-25 03:25:13 +0000 | [diff] [blame] | 864 | Nullability.forAnnotations(field.getAnnotations()), key, injector); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 865 | } |
| 866 | |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 867 | public Collection<Dependency<?>> getDependencies() { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 868 | return Collections.<Dependency<?>>singleton(injectionPoint); |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 869 | } |
| 870 | |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 871 | public void inject(InternalContext context, Object o) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 872 | context.setInjectionPoint(injectionPoint); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 873 | try { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 874 | Object value = factory.get(context, injectionPoint); |
crazyboblee | 4602a6f | 2007-02-15 02:45:18 +0000 | [diff] [blame] | 875 | field.set(o, value); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 876 | } |
| 877 | catch (IllegalAccessException e) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 878 | throw new AssertionError(e); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 879 | } |
kevinb9n | 9119a63 | 2007-02-22 01:28:24 +0000 | [diff] [blame] | 880 | catch (ConfigurationException e) { |
| 881 | throw e; |
| 882 | } |
limpbizkit | 1dabcfd | 2007-08-25 08:06:30 +0000 | [diff] [blame] | 883 | catch (ProvisionException provisionException) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 884 | provisionException.addContext(injectionPoint); |
limpbizkit | 1dabcfd | 2007-08-25 08:06:30 +0000 | [diff] [blame] | 885 | throw provisionException; |
| 886 | } |
| 887 | catch (RuntimeException runtimeException) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 888 | throw new ProvisionException(runtimeException, |
| 889 | ErrorMessages.ERROR_INJECTING_FIELD); |
kevinb9n | 9119a63 | 2007-02-22 01:28:24 +0000 | [diff] [blame] | 890 | } |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 891 | finally { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 892 | context.setInjectionPoint(null); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 893 | } |
| 894 | } |
| 895 | } |
| 896 | |
| 897 | /** |
| 898 | * Gets parameter injectors. |
| 899 | * |
| 900 | * @param member to which the parameters belong |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 901 | * @return injections |
| 902 | */ |
kevinb9n | f73ca5d | 2007-03-13 02:39:45 +0000 | [diff] [blame] | 903 | SingleParameterInjector<?>[] getParametersInjectors(Member member, |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 904 | List<Parameter<?>> parameters) |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 905 | throws ResolveFailedException { |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 906 | SingleParameterInjector<?>[] parameterInjectors |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 907 | = new SingleParameterInjector<?>[parameters.size()]; |
crazyboblee | 1596921 | 2007-02-01 02:01:59 +0000 | [diff] [blame] | 908 | int index = 0; |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 909 | for (Parameter<?> parameter : parameters) { |
| 910 | parameterInjectors[index] = createParameterInjector(parameter, member); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 911 | index++; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 912 | } |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 913 | return parameterInjectors; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 914 | } |
| 915 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 916 | <T> SingleParameterInjector<T> createParameterInjector( |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 917 | final Parameter<T> parameter, Member member) |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 918 | throws ResolveFailedException { |
| 919 | InternalFactory<? extends T> factory |
| 920 | = new ResolvingCallable<InternalFactory<? extends T>>() { |
| 921 | public InternalFactory<? extends T> call() throws ResolveFailedException { |
| 922 | return getInternalFactory(parameter.getKey()); |
| 923 | } |
| 924 | }.runWithDefaultSource(StackTraceElements.forMember(member)); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 925 | |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 926 | InjectionPoint<T> injectionPoint = InjectionPoint.newInstance( |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 927 | member, parameter.getIndex(), parameter.getNullability(), parameter.getKey(), this); |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 928 | return new SingleParameterInjector<T>(injectionPoint, factory); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 929 | } |
| 930 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 931 | static class SingleMethodInjector implements SingleMemberInjector { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 932 | |
crazyboblee | 0b3189c | 2007-02-24 00:14:51 +0000 | [diff] [blame] | 933 | final MethodInvoker methodInvoker; |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 934 | final SingleParameterInjector<?>[] parameterInjectors; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 935 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 936 | public SingleMethodInjector(InjectorImpl injector, final Method method) |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 937 | throws ResolveFailedException { |
crazyboblee | 0b3189c | 2007-02-24 00:14:51 +0000 | [diff] [blame] | 938 | // We can't use FastMethod if the method is private. |
crazyboblee | 77bf3b2 | 2007-02-25 21:37:02 +0000 | [diff] [blame] | 939 | if (Modifier.isPrivate(method.getModifiers()) |
| 940 | || Modifier.isProtected(method.getModifiers())) { |
crazyboblee | 0b3189c | 2007-02-24 00:14:51 +0000 | [diff] [blame] | 941 | method.setAccessible(true); |
| 942 | this.methodInvoker = new MethodInvoker() { |
| 943 | public Object invoke(Object target, Object... parameters) throws |
| 944 | IllegalAccessException, InvocationTargetException { |
| 945 | return method.invoke(target, parameters); |
| 946 | } |
| 947 | }; |
| 948 | } |
| 949 | else { |
| 950 | FastClass fastClass = GuiceFastClass.create(method.getDeclaringClass()); |
| 951 | final FastMethod fastMethod = fastClass.getMethod(method); |
| 952 | |
| 953 | this.methodInvoker = new MethodInvoker() { |
| 954 | public Object invoke(Object target, Object... parameters) |
| 955 | throws IllegalAccessException, InvocationTargetException { |
| 956 | return fastMethod.invoke(target, parameters); |
| 957 | } |
| 958 | }; |
| 959 | } |
| 960 | |
crazyboblee | 7c5b2c4 | 2007-01-20 02:05:20 +0000 | [diff] [blame] | 961 | Type[] parameterTypes = method.getGenericParameterTypes(); |
crazyboblee | 4727ee2 | 2007-01-30 03:13:38 +0000 | [diff] [blame] | 962 | parameterInjectors = parameterTypes.length > 0 |
limpbizkit | 916f548 | 2008-04-16 20:51:14 +0000 | [diff] [blame] | 963 | ? injector.getParametersInjectors(method, |
| 964 | Parameter.forMethod(injector.errorHandler, method)) |
crazyboblee | 4727ee2 | 2007-01-30 03:13:38 +0000 | [diff] [blame] | 965 | : null; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 966 | } |
| 967 | |
| 968 | public void inject(InternalContext context, Object o) { |
| 969 | try { |
crazyboblee | 0b3189c | 2007-02-24 00:14:51 +0000 | [diff] [blame] | 970 | methodInvoker.invoke(o, getParameters(context, parameterInjectors)); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 971 | } |
limpbizkit | 1dabcfd | 2007-08-25 08:06:30 +0000 | [diff] [blame] | 972 | catch (IllegalAccessException e) { |
| 973 | throw new AssertionError(e); |
| 974 | } |
limpbizkit | c808df0 | 2007-08-25 03:25:13 +0000 | [diff] [blame] | 975 | catch (ProvisionException e) { |
| 976 | throw e; |
| 977 | } |
limpbizkit | c808df0 | 2007-08-25 03:25:13 +0000 | [diff] [blame] | 978 | catch (InvocationTargetException e) { |
limpbizkit | 1dabcfd | 2007-08-25 08:06:30 +0000 | [diff] [blame] | 979 | Throwable cause = e.getCause() != null ? e.getCause() : e; |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 980 | throw new ProvisionException(cause, |
| 981 | ErrorMessages.ERROR_INJECTING_METHOD); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 982 | } |
| 983 | } |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 984 | |
| 985 | public Collection<Dependency<?>> getDependencies() { |
| 986 | List<Dependency<?>> dependencies = new ArrayList<Dependency<?>>(); |
| 987 | for (SingleParameterInjector<?> parameterInjector : parameterInjectors) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 988 | dependencies.add(parameterInjector.injectionPoint); |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 989 | } |
| 990 | return Collections.unmodifiableList(dependencies); |
| 991 | } |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 992 | } |
| 993 | |
crazyboblee | 0b3189c | 2007-02-24 00:14:51 +0000 | [diff] [blame] | 994 | /** |
| 995 | * Invokes a method. |
| 996 | */ |
| 997 | interface MethodInvoker { |
| 998 | Object invoke(Object target, Object... parameters) throws |
| 999 | IllegalAccessException, InvocationTargetException; |
| 1000 | } |
| 1001 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1002 | final Map<Class<?>, Object> constructors |
| 1003 | = new ReferenceCache<Class<?>, Object>() { |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1004 | @SuppressWarnings("unchecked") |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1005 | protected Object create(Class<?> implementation) { |
crazyboblee | d0c4b8b | 2007-09-06 02:47:04 +0000 | [diff] [blame] | 1006 | if (!Classes.isConcrete(implementation)) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1007 | return new ResolveFailedException( |
crazyboblee | 6c7720d | 2007-03-01 21:49:19 +0000 | [diff] [blame] | 1008 | ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, implementation); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1009 | } |
crazyboblee | d0c4b8b | 2007-09-06 02:47:04 +0000 | [diff] [blame] | 1010 | if (Classes.isInnerClass(implementation)) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1011 | return new ResolveFailedException(ErrorMessages.CANNOT_INJECT_INNER_CLASS, implementation); |
kevinb9n | 9da90b3 | 2007-04-20 15:28:04 +0000 | [diff] [blame] | 1012 | } |
crazyboblee | 235d068 | 2007-01-31 02:25:21 +0000 | [diff] [blame] | 1013 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 1014 | return new ConstructorInjector(InjectorImpl.this, implementation); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1015 | } |
| 1016 | }; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1017 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 1018 | static class SingleParameterInjector<T> { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1019 | |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1020 | final InjectionPoint<T> injectionPoint; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1021 | final InternalFactory<? extends T> factory; |
| 1022 | |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1023 | public SingleParameterInjector(InjectionPoint<T> injectionPoint, |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1024 | InternalFactory<? extends T> factory) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1025 | this.injectionPoint = injectionPoint; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1026 | this.factory = factory; |
| 1027 | } |
| 1028 | |
crazyboblee | 664a82e | 2007-01-25 23:09:51 +0000 | [diff] [blame] | 1029 | T inject(InternalContext context) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1030 | context.setInjectionPoint(injectionPoint); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1031 | try { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1032 | return factory.get(context, injectionPoint); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1033 | } |
kevinb9n | 9119a63 | 2007-02-22 01:28:24 +0000 | [diff] [blame] | 1034 | catch (ConfigurationException e) { |
| 1035 | throw e; |
| 1036 | } |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1037 | catch (ProvisionException provisionException) { |
| 1038 | provisionException.addContext(injectionPoint); |
| 1039 | throw provisionException; |
limpbizkit | 1dabcfd | 2007-08-25 08:06:30 +0000 | [diff] [blame] | 1040 | } |
| 1041 | catch (RuntimeException runtimeException) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1042 | throw new ProvisionException(runtimeException, |
| 1043 | ErrorMessages.ERROR_INJECTING_METHOD); |
kevinb9n | 9119a63 | 2007-02-22 01:28:24 +0000 | [diff] [blame] | 1044 | } |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1045 | finally { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1046 | context.setInjectionPoint(injectionPoint); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1047 | } |
| 1048 | } |
| 1049 | } |
| 1050 | |
crazyboblee | e3adfd6 | 2007-02-02 21:30:08 +0000 | [diff] [blame] | 1051 | /** |
| 1052 | * Iterates over parameter injectors and creates an array of parameter |
| 1053 | * values. |
| 1054 | */ |
| 1055 | static Object[] getParameters(InternalContext context, |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 1056 | SingleParameterInjector[] parameterInjectors) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1057 | if (parameterInjectors == null) { |
| 1058 | return null; |
| 1059 | } |
| 1060 | |
| 1061 | Object[] parameters = new Object[parameterInjectors.length]; |
| 1062 | for (int i = 0; i < parameters.length; i++) { |
crazyboblee | 664a82e | 2007-01-25 23:09:51 +0000 | [diff] [blame] | 1063 | parameters[i] = parameterInjectors[i].inject(context); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1064 | } |
| 1065 | return parameters; |
| 1066 | } |
| 1067 | |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1068 | void injectMembers(Object o, InternalContext context) { |
limpbizkit | c808df0 | 2007-08-25 03:25:13 +0000 | [diff] [blame] | 1069 | if (o == null) { |
| 1070 | return; |
| 1071 | } |
| 1072 | |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 1073 | List<SingleMemberInjector> injectorsForClass = injectors.get(o.getClass()); |
| 1074 | for (SingleMemberInjector injector : injectorsForClass) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1075 | injector.inject(context, o); |
| 1076 | } |
| 1077 | } |
| 1078 | |
kevinb9n | 225310e | 2007-02-20 04:12:01 +0000 | [diff] [blame] | 1079 | // Not test-covered |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1080 | public void injectMembers(final Object o) { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1081 | callInContext(new ContextualCallable<Void>() { |
| 1082 | public Void call(InternalContext context) { |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1083 | injectMembers(o, context); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1084 | return null; |
| 1085 | } |
| 1086 | }); |
| 1087 | } |
| 1088 | |
crazyboblee | bd9544e | 2007-02-25 20:32:11 +0000 | [diff] [blame] | 1089 | public <T> Provider<T> getProvider(Class<T> type) { |
| 1090 | return getProvider(Key.get(type)); |
crazyboblee | e5fbbb0 | 2007-02-05 07:00:27 +0000 | [diff] [blame] | 1091 | } |
| 1092 | |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1093 | <T> Provider<T> getProviderOrThrow(final Key<T> key) throws ResolveFailedException { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 1094 | final InternalFactory<? extends T> factory = getInternalFactory(key); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1095 | |
crazyboblee | bd9544e | 2007-02-25 20:32:11 +0000 | [diff] [blame] | 1096 | return new Provider<T>() { |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1097 | public T get() { |
| 1098 | return callInContext(new ContextualCallable<T>() { |
| 1099 | public T call(InternalContext context) { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1100 | InjectionPoint<T> injectionPoint |
| 1101 | = InjectionPoint.newInstance(key, InjectorImpl.this); |
| 1102 | context.setInjectionPoint(injectionPoint); |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1103 | try { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1104 | return factory.get(context, injectionPoint); |
| 1105 | } |
| 1106 | catch(ProvisionException provisionException) { |
| 1107 | provisionException.addContext(injectionPoint); |
| 1108 | throw provisionException; |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1109 | } |
| 1110 | finally { |
limpbizkit | fcf2b8c | 2007-10-21 18:23:43 +0000 | [diff] [blame] | 1111 | context.setInjectionPoint(null); |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1112 | } |
| 1113 | } |
| 1114 | }); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1115 | } |
crazyboblee | d7908e8 | 2007-02-16 01:04:53 +0000 | [diff] [blame] | 1116 | |
| 1117 | public String toString() { |
| 1118 | return factory.toString(); |
| 1119 | } |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1120 | }; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1121 | } |
| 1122 | |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 1123 | public <T> Provider<T> getProvider(final Key<T> key) { |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1124 | try { |
| 1125 | return getProviderOrThrow(key); |
| 1126 | } catch (ResolveFailedException e) { |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 1127 | throw new ConfigurationException( |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1128 | "Missing binding to " + ErrorMessages.convert(key) + ": " + e.getMessage()); |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 1129 | } |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 1130 | } |
| 1131 | |
kevinb9n | 27f8a58 | 2007-02-28 22:54:06 +0000 | [diff] [blame] | 1132 | public <T> T getInstance(Key<T> key) { |
| 1133 | return getProvider(key).get(); |
| 1134 | } |
| 1135 | |
| 1136 | public <T> T getInstance(Class<T> type) { |
| 1137 | return getProvider(type).get(); |
| 1138 | } |
| 1139 | |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1140 | final ThreadLocal<InternalContext[]> localContext |
| 1141 | = new ThreadLocal<InternalContext[]>() { |
| 1142 | protected InternalContext[] initialValue() { |
| 1143 | return new InternalContext[1]; |
| 1144 | } |
| 1145 | }; |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1146 | |
| 1147 | /** |
| 1148 | * Looks up thread local context. Creates (and removes) a new context if |
| 1149 | * necessary. |
| 1150 | */ |
| 1151 | <T> T callInContext(ContextualCallable<T> callable) { |
| 1152 | InternalContext[] reference = localContext.get(); |
| 1153 | if (reference[0] == null) { |
crazyboblee | 63b592b | 2007-01-25 02:45:24 +0000 | [diff] [blame] | 1154 | reference[0] = new InternalContext(this); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1155 | try { |
| 1156 | return callable.call(reference[0]); |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1157 | } |
| 1158 | finally { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1159 | // Only remove the context if this call created it. |
| 1160 | reference[0] = null; |
| 1161 | } |
kevinb9n | a99dca7 | 2007-02-11 04:48:57 +0000 | [diff] [blame] | 1162 | } |
| 1163 | else { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1164 | // Someone else will clean up this context. |
| 1165 | return callable.call(reference[0]); |
| 1166 | } |
| 1167 | } |
| 1168 | |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1169 | /** |
| 1170 | * Gets a constructor function for a given implementation class. |
| 1171 | */ |
| 1172 | @SuppressWarnings("unchecked") |
limpbizkit | 3b1cd58 | 2008-04-28 00:06:01 +0000 | [diff] [blame] | 1173 | <T> ConstructorInjector<T> getConstructor(Class<T> implementation) |
| 1174 | throws ResolveFailedException { |
| 1175 | Object o = constructors.get(implementation); |
| 1176 | if (o instanceof ResolveFailedException) { |
| 1177 | throw (ResolveFailedException) o; |
| 1178 | } else if (o instanceof ConstructorInjector<?>) { |
| 1179 | return (ConstructorInjector<T>) o; |
| 1180 | } else { |
| 1181 | throw new AssertionError(); |
| 1182 | } |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1183 | } |
| 1184 | |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1185 | /** |
| 1186 | * Injects a field or method in a given object. |
| 1187 | */ |
limpbizkit | 8b23745 | 2008-04-22 06:47:36 +0000 | [diff] [blame] | 1188 | public interface SingleMemberInjector { |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1189 | void inject(InternalContext context, Object o); |
crazyboblee | d71c19e | 2007-09-08 01:55:10 +0000 | [diff] [blame] | 1190 | Collection<Dependency<?>> getDependencies(); |
| 1191 | } |
| 1192 | |
| 1193 | List<Dependency<?>> getModifiableFieldAndMethodDependenciesFor( |
| 1194 | Class<?> clazz) { |
| 1195 | List<SingleMemberInjector> injectors = this.injectors.get(clazz); |
| 1196 | List<Dependency<?>> dependencies = new ArrayList<Dependency<?>>(); |
| 1197 | for (SingleMemberInjector singleMemberInjector : injectors) { |
| 1198 | dependencies.addAll(singleMemberInjector.getDependencies()); |
| 1199 | } |
| 1200 | return dependencies; |
| 1201 | } |
| 1202 | |
| 1203 | Collection<Dependency<?>> getFieldAndMethodDependenciesFor(Class<?> clazz) { |
| 1204 | return Collections.unmodifiableList( |
| 1205 | getModifiableFieldAndMethodDependenciesFor(clazz)); |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1206 | } |
| 1207 | |
crazyboblee | e3adfd6 | 2007-02-02 21:30:08 +0000 | [diff] [blame] | 1208 | public String toString() { |
kevinb9n | a2915a9 | 2007-02-28 06:20:30 +0000 | [diff] [blame] | 1209 | return new ToStringBuilder(Injector.class) |
crazyboblee | 712705c | 2007-09-07 03:20:30 +0000 | [diff] [blame] | 1210 | .add("bindings", explicitBindings) |
crazyboblee | e3adfd6 | 2007-02-02 21:30:08 +0000 | [diff] [blame] | 1211 | .toString(); |
| 1212 | } |
crazyboblee | 66b415a | 2006-08-25 02:01:19 +0000 | [diff] [blame] | 1213 | } |