001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.google.inject;
016:
017: import com.google.inject.util.StackTraceElements;
018: import java.lang.reflect.Constructor;
019: import java.lang.reflect.InvocationTargetException;
020:
021: /**
022: * Injects constructors.
023: *
024: * @author crazybob@google.com (Bob Lee)
025: */
026: class ConstructorInjector<T> {
027:
028: final Class<T> implementation;
029: final InjectorImpl.SingleMemberInjector[] memberInjectors;
030: final InjectorImpl.SingleParameterInjector<?>[] parameterInjectors;
031: final ConstructionProxy<T> constructionProxy;
032:
033: ConstructorInjector(InjectorImpl injector, Class<T> implementation) {
034: this .implementation = implementation;
035: Constructor<T> constructor = findConstructorIn(injector,
036: implementation);
037: parameterInjectors = createParameterInjector(injector,
038: constructor);
039: memberInjectors = injector.injectors.get(implementation)
040: .toArray(new InjectorImpl.SingleMemberInjector[0]);
041: constructionProxy = injector.constructionProxyFactory
042: .get(constructor);
043: }
044:
045: /**
046: * Used to create an invalid injector.
047: */
048: private ConstructorInjector() {
049: implementation = null;
050: memberInjectors = null;
051: parameterInjectors = null;
052: constructionProxy = null;
053: }
054:
055: InjectorImpl.SingleParameterInjector<?>[] createParameterInjector(
056: InjectorImpl injector, Constructor<T> constructor) {
057: try {
058: return constructor.getParameterTypes().length == 0 ? null // default constructor.
059: : injector.getParametersInjectors(constructor,
060: constructor.getParameterAnnotations(),
061: constructor.getGenericParameterTypes());
062: } catch (InjectorImpl.MissingDependencyException e) {
063: e.handle(injector.errorHandler);
064: return null;
065: }
066: }
067:
068: private Constructor<T> findConstructorIn(InjectorImpl injector,
069: Class<T> implementation) {
070: Constructor<T> found = null;
071: @SuppressWarnings("unchecked")
072: Constructor<T>[] constructors = (Constructor<T>[]) implementation
073: .getDeclaredConstructors();
074: for (Constructor<T> constructor : constructors) {
075: Inject inject = constructor.getAnnotation(Inject.class);
076: if (inject != null) {
077: if (inject.optional()) {
078: injector.errorHandler.handle(StackTraceElements
079: .forMember(constructor),
080: ErrorMessages.OPTIONAL_CONSTRUCTOR);
081: }
082:
083: if (found != null) {
084: injector.errorHandler.handle(StackTraceElements
085: .forMember(found),
086: ErrorMessages.TOO_MANY_CONSTRUCTORS);
087: return InjectorImpl.invalidConstructor();
088: }
089: found = constructor;
090: }
091: }
092: if (found != null) {
093: return found;
094: }
095:
096: // If no annotated constructor is found, look for a no-arg constructor
097: // instead.
098: try {
099: return implementation.getDeclaredConstructor();
100: } catch (NoSuchMethodException e) {
101: injector.errorHandler.handle(
102: StackTraceElements.forMember(implementation
103: .getDeclaredConstructors()[0]),
104: ErrorMessages.MISSING_CONSTRUCTOR, implementation);
105: return InjectorImpl.invalidConstructor();
106: }
107: }
108:
109: /**
110: * Construct an instance. Returns {@code Object} instead of {@code T} because
111: * it may return a proxy.
112: */
113: Object construct(InternalContext context, Class<?> expectedType) {
114: ConstructionContext<T> constructionContext = context
115: .getConstructionContext(this );
116:
117: // We have a circular reference between constructors. Return a proxy.
118: if (constructionContext.isConstructing()) {
119: // TODO (crazybob): if we can't proxy this object, can we proxy the
120: // other object?
121: return constructionContext.createProxy(expectedType);
122: }
123:
124: // If we're re-entering this factory while injecting fields or methods,
125: // return the same instance. This prevents infinite loops.
126: T t = constructionContext.getCurrentReference();
127: if (t != null) {
128: return t;
129: }
130:
131: try {
132: // First time through...
133: constructionContext.startConstruction();
134: try {
135: Object[] parameters = InjectorImpl.getParameters(
136: context, parameterInjectors);
137: t = constructionProxy.newInstance(parameters);
138: constructionContext.setProxyDelegates(t);
139: } finally {
140: constructionContext.finishConstruction();
141: }
142:
143: // Store reference. If an injector re-enters this factory, they'll
144: // get the same reference.
145: constructionContext.setCurrentReference(t);
146:
147: // Inject fields and methods.
148: for (InjectorImpl.SingleMemberInjector injector : memberInjectors) {
149: injector.inject(context, t);
150: }
151:
152: return t;
153: } catch (InvocationTargetException e) {
154: throw new RuntimeException(e);
155: } finally {
156: constructionContext.removeCurrentReference();
157: }
158: }
159:
160: /**
161: * Returns an invalid constructor. This enables us to keep running and
162: * reporting legitimate errors.
163: */
164: static <T> ConstructorInjector<T> invalidConstructor() {
165: return new ConstructorInjector<T>() {
166: Object construct(InternalContext context,
167: Class<?> expectedType) {
168: throw new UnsupportedOperationException();
169: }
170:
171: public T get() {
172: throw new UnsupportedOperationException();
173: }
174: };
175: }
176: }
|