crazyboblee | e3adfd6 | 2007-02-02 21:30:08 +0000 | [diff] [blame^] | 1 | // Copyright 2006 Google Inc. All Rights Reserved. |
| 2 | |
| 3 | package com.google.inject; |
| 4 | |
| 5 | import com.google.inject.spi.ConstructionProxy; |
| 6 | |
| 7 | import java.lang.reflect.Constructor; |
| 8 | import java.lang.reflect.InvocationTargetException; |
| 9 | |
| 10 | /** |
| 11 | * Injects constructors. |
| 12 | * |
| 13 | * @author crazybob@google.com (Bob Lee) |
| 14 | */ |
| 15 | class ConstructorInjector<T> implements Factory<T> { |
| 16 | |
| 17 | final Class<T> implementation; |
| 18 | final ContainerImpl.Injector[] injectors; |
| 19 | final ContainerImpl.ParameterInjector<?>[] parameterInjectors; |
| 20 | final ConstructionProxy<T> constructionProxy; |
| 21 | |
| 22 | private ContainerImpl container; |
| 23 | |
| 24 | ConstructorInjector(ContainerImpl container, Class<T> implementation) { |
| 25 | this.container = container; |
| 26 | this.implementation = implementation; |
| 27 | Constructor<T> constructor = findConstructorIn(implementation); |
| 28 | parameterInjectors = createParameterInjector(constructor); |
| 29 | injectors = container.injectors.get(implementation) |
| 30 | .toArray(new ContainerImpl.Injector[0]); |
| 31 | constructionProxy = container.constructionProxyFactory.get(constructor); |
| 32 | } |
| 33 | |
| 34 | ContainerImpl.ParameterInjector<?>[] createParameterInjector( |
| 35 | Constructor<T> constructor) { |
| 36 | try { |
| 37 | Inject inject = constructor.getAnnotation(Inject.class); |
| 38 | return inject == null |
| 39 | ? null // default constructor. |
| 40 | : container.getParametersInjectors( |
| 41 | constructor, |
| 42 | constructor.getParameterAnnotations(), |
| 43 | constructor.getGenericParameterTypes(), |
| 44 | inject.value() |
| 45 | ); |
| 46 | } catch (ContainerImpl.MissingDependencyException e) { |
| 47 | e.handle(container.errorHandler); |
| 48 | return null; |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | @SuppressWarnings({"unchecked"}) |
| 53 | private Constructor<T> findConstructorIn(Class<T> implementation) { |
| 54 | Constructor<T> found = null; |
| 55 | for (Constructor<T> constructor |
| 56 | : implementation.getDeclaredConstructors()) { |
| 57 | if (constructor.getAnnotation(Inject.class) != null) { |
| 58 | if (found != null) { |
| 59 | container.errorHandler.handle( |
| 60 | ErrorMessage.TOO_MANY_CONSTRUCTORS, implementation); |
| 61 | return ContainerImpl.invalidConstructor(); |
| 62 | } |
| 63 | found = constructor; |
| 64 | } |
| 65 | } |
| 66 | if (found != null) { |
| 67 | return found; |
| 68 | } |
| 69 | |
| 70 | // If no annotated constructor is found, look for a no-arg constructor |
| 71 | // instead. |
| 72 | try { |
| 73 | return implementation.getDeclaredConstructor(); |
| 74 | } catch (NoSuchMethodException e) { |
| 75 | container.errorHandler.handle(ErrorMessage.MISSING_CONSTRUCTOR, implementation); |
| 76 | return ContainerImpl.invalidConstructor(); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * Construct an instance. Returns {@code Object} instead of {@code T} |
| 82 | * because it may return a proxy. |
| 83 | */ |
| 84 | Object construct(InternalContext context, Class<? super T> expectedType) { |
| 85 | ConstructionContext<T> constructionContext = |
| 86 | context.getConstructionContext(this); |
| 87 | |
| 88 | // We have a circular reference between constructors. Return a proxy. |
| 89 | if (constructionContext.isConstructing()) { |
| 90 | // TODO (crazybob): if we can't proxy this object, can we proxy the |
| 91 | // other object? |
| 92 | return constructionContext.createProxy(expectedType); |
| 93 | } |
| 94 | |
| 95 | // If we're re-entering this factory while injecting fields or methods, |
| 96 | // return the same instance. This prevents infinite loops. |
| 97 | T t = constructionContext.getCurrentReference(); |
| 98 | if (t != null) { |
| 99 | return t; |
| 100 | } |
| 101 | |
| 102 | try { |
| 103 | // First time through... |
| 104 | constructionContext.startConstruction(); |
| 105 | try { |
| 106 | Object[] parameters = |
| 107 | ContainerImpl.getParameters(context, parameterInjectors); |
| 108 | t = newInstance(parameters); |
| 109 | constructionContext.setProxyDelegates(t); |
| 110 | } finally { |
| 111 | constructionContext.finishConstruction(); |
| 112 | } |
| 113 | |
| 114 | // Store reference. If an injector re-enters this factory, they'll |
| 115 | // get the same reference. |
| 116 | constructionContext.setCurrentReference(t); |
| 117 | |
| 118 | // Inject fields and methods. |
| 119 | for (int i = 0; i < injectors.length; i++) { |
| 120 | injectors[i].inject(context, t); |
| 121 | } |
| 122 | |
| 123 | return t; |
| 124 | } catch (InvocationTargetException e) { |
| 125 | throw new RuntimeException(e); |
| 126 | } finally { |
| 127 | constructionContext.removeCurrentReference(); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | @SuppressWarnings({"unchecked"}) |
| 132 | private T newInstance(Object[] parameters) |
| 133 | throws InvocationTargetException { |
| 134 | return (T) constructionProxy.newInstance(parameters); |
| 135 | } |
| 136 | |
| 137 | public T get() { |
| 138 | try { |
| 139 | return container.callInContext(new ContextualCallable<T>() { |
| 140 | @SuppressWarnings({"unchecked"}) |
| 141 | public T call(InternalContext context) { |
| 142 | return (T) construct(context, implementation); |
| 143 | } |
| 144 | }); |
| 145 | } catch (RuntimeException e) { |
| 146 | throw e; |
| 147 | } catch (Exception e) { |
| 148 | throw new RuntimeException(e); |
| 149 | } |
| 150 | } |
| 151 | } |