blob: dc4c7816ec134463ff79da943ff72950ece16d1e [file] [log] [blame]
crazybobleee3adfd62007-02-02 21:30:08 +00001// Copyright 2006 Google Inc. All Rights Reserved.
2
3package com.google.inject;
4
5import com.google.inject.spi.ConstructionProxy;
6
7import java.lang.reflect.Constructor;
8import java.lang.reflect.InvocationTargetException;
9
10/**
11 * Injects constructors.
12 *
13 * @author crazybob@google.com (Bob Lee)
14*/
15class 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}