blob: 91fa1d7ed7cc911f29f31442aa2db465f0ed2d5e [file] [log] [blame]
crazybobleeb8cf1e52007-02-02 21:48:16 +00001/**
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 */
crazybobleee3adfd62007-02-02 21:30:08 +000016
17package com.google.inject;
18
crazybobleee3adfd62007-02-02 21:30:08 +000019import java.lang.reflect.Constructor;
20import java.lang.reflect.InvocationTargetException;
21
22/**
23 * Injects constructors.
24 *
25 * @author crazybob@google.com (Bob Lee)
kevinb9na99dca72007-02-11 04:48:57 +000026 */
crazybobleee3adfd62007-02-02 21:30:08 +000027class ConstructorInjector<T> implements Factory<T> {
28
29 final Class<T> implementation;
30 final ContainerImpl.Injector[] injectors;
31 final ContainerImpl.ParameterInjector<?>[] parameterInjectors;
32 final ConstructionProxy<T> constructionProxy;
33
kevinb9n6a565c72007-02-11 01:58:33 +000034 private final ContainerImpl container;
crazybobleee3adfd62007-02-02 21:30:08 +000035
36 ConstructorInjector(ContainerImpl container, Class<T> implementation) {
37 this.container = container;
38 this.implementation = implementation;
39 Constructor<T> constructor = findConstructorIn(implementation);
40 parameterInjectors = createParameterInjector(constructor);
41 injectors = container.injectors.get(implementation)
42 .toArray(new ContainerImpl.Injector[0]);
43 constructionProxy = container.constructionProxyFactory.get(constructor);
44 }
45
46 ContainerImpl.ParameterInjector<?>[] createParameterInjector(
47 Constructor<T> constructor) {
48 try {
49 Inject inject = constructor.getAnnotation(Inject.class);
50 return inject == null
51 ? null // default constructor.
52 : container.getParametersInjectors(
53 constructor,
54 constructor.getParameterAnnotations(),
55 constructor.getGenericParameterTypes(),
56 inject.value()
57 );
kevinb9na99dca72007-02-11 04:48:57 +000058 }
59 catch (ContainerImpl.MissingDependencyException e) {
crazybobleee3adfd62007-02-02 21:30:08 +000060 e.handle(container.errorHandler);
61 return null;
62 }
63 }
64
crazybobleee3adfd62007-02-02 21:30:08 +000065 private Constructor<T> findConstructorIn(Class<T> implementation) {
66 Constructor<T> found = null;
kevinb9n48d13072007-02-12 18:21:26 +000067 @SuppressWarnings("unchecked") // why doesn't it return the right thing?
68 Constructor<T>[] constructors
69 = (Constructor<T>[]) implementation.getDeclaredConstructors();
70 for (Constructor<T> constructor : constructors) {
crazybobleee3adfd62007-02-02 21:30:08 +000071 if (constructor.getAnnotation(Inject.class) != null) {
72 if (found != null) {
73 container.errorHandler.handle(
crazybobleee039bac2007-02-02 21:42:09 +000074 ErrorMessages.TOO_MANY_CONSTRUCTORS, implementation);
crazybobleee3adfd62007-02-02 21:30:08 +000075 return ContainerImpl.invalidConstructor();
76 }
77 found = constructor;
78 }
79 }
80 if (found != null) {
81 return found;
82 }
83
84 // If no annotated constructor is found, look for a no-arg constructor
85 // instead.
86 try {
87 return implementation.getDeclaredConstructor();
kevinb9na99dca72007-02-11 04:48:57 +000088 }
89 catch (NoSuchMethodException e) {
90 container.errorHandler.handle(
91 ErrorMessages.MISSING_CONSTRUCTOR, implementation);
crazybobleee3adfd62007-02-02 21:30:08 +000092 return ContainerImpl.invalidConstructor();
93 }
94 }
95
96 /**
kevinb9na99dca72007-02-11 04:48:57 +000097 * Construct an instance. Returns {@code Object} instead of {@code T} because
98 * it may return a proxy.
crazybobleee3adfd62007-02-02 21:30:08 +000099 */
kevinb9n48d13072007-02-12 18:21:26 +0000100 T construct(InternalContext context, Class<T> expectedType) {
kevinb9na99dca72007-02-11 04:48:57 +0000101 ConstructionContext<T> constructionContext
102 = context.getConstructionContext(this);
crazybobleee3adfd62007-02-02 21:30:08 +0000103
104 // We have a circular reference between constructors. Return a proxy.
105 if (constructionContext.isConstructing()) {
106 // TODO (crazybob): if we can't proxy this object, can we proxy the
107 // other object?
108 return constructionContext.createProxy(expectedType);
109 }
110
111 // If we're re-entering this factory while injecting fields or methods,
112 // return the same instance. This prevents infinite loops.
113 T t = constructionContext.getCurrentReference();
114 if (t != null) {
115 return t;
116 }
117
118 try {
119 // First time through...
120 constructionContext.startConstruction();
121 try {
kevinb9na99dca72007-02-11 04:48:57 +0000122 Object[] parameters
123 = ContainerImpl.getParameters(context, parameterInjectors);
kevinb9n48d13072007-02-12 18:21:26 +0000124 t = constructionProxy.newInstance(parameters);
crazybobleee3adfd62007-02-02 21:30:08 +0000125 constructionContext.setProxyDelegates(t);
kevinb9na99dca72007-02-11 04:48:57 +0000126 }
127 finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000128 constructionContext.finishConstruction();
129 }
130
131 // Store reference. If an injector re-enters this factory, they'll
132 // get the same reference.
133 constructionContext.setCurrentReference(t);
134
135 // Inject fields and methods.
kevinb9na99dca72007-02-11 04:48:57 +0000136 for (ContainerImpl.Injector injector : injectors) {
137 injector.inject(context, t);
crazybobleee3adfd62007-02-02 21:30:08 +0000138 }
139
140 return t;
kevinb9na99dca72007-02-11 04:48:57 +0000141 }
142 catch (InvocationTargetException e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000143 throw new RuntimeException(e);
kevinb9na99dca72007-02-11 04:48:57 +0000144 }
145 finally {
crazybobleee3adfd62007-02-02 21:30:08 +0000146 constructionContext.removeCurrentReference();
147 }
148 }
149
crazybobleee3adfd62007-02-02 21:30:08 +0000150 public T get() {
151 try {
152 return container.callInContext(new ContextualCallable<T>() {
crazybobleee3adfd62007-02-02 21:30:08 +0000153 public T call(InternalContext context) {
kevinb9n48d13072007-02-12 18:21:26 +0000154 return construct(context, implementation);
crazybobleee3adfd62007-02-02 21:30:08 +0000155 }
156 });
kevinb9na99dca72007-02-11 04:48:57 +0000157 }
158 catch (RuntimeException e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000159 throw e;
kevinb9na99dca72007-02-11 04:48:57 +0000160 }
161 catch (Exception e) {
crazybobleee3adfd62007-02-02 21:30:08 +0000162 throw new RuntimeException(e);
163 }
164 }
165}