001: /*
002: * Copyright (c) 2007 XStream Committers.
003: * All rights reserved.
004: *
005: * The software in this package is published under the terms of the BSD
006: * style license a copy of which has been included with this distribution in
007: * the LICENSE.txt file.
008: *
009: * Created on 30. March 2007 by Joerg Schaible
010: */
011: package com.thoughtworks.xstream.core.util;
012:
013: import java.lang.reflect.Constructor;
014: import java.lang.reflect.InvocationTargetException;
015: import java.util.ArrayList;
016: import java.util.Arrays;
017: import java.util.Comparator;
018: import java.util.List;
019:
020: import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
021:
022: /**
023: * A dependency injection factory.
024: *
025: * @author Jörg Schaible
026: * @since 1.2.2
027: */
028: public class DependencyInjectionFactory {
029:
030: /**
031: * Create an instance with dependency injection. The given dependencies are used to match the parameters of the
032: * constructors of the type. Constructors with most parameters are examined first. A parameter type sequence
033: * matching the sequence of the dependencies' types match first. Otherwise all the types of the dependencies must
034: * match one of the the parameters although no dependency is used twice. Use a {@link TypedNull} instance to inject
035: * <code>null</code> as parameter.
036: *
037: * @param type the type to create an instance of
038: * @param dependencies the possible dependencies
039: * @return the instantiated object
040: * @throws ObjectAccessException if no instance can be generated
041: */
042: public static Object newInstance(final Class type,
043: final Object[] dependencies) {
044: // sort available ctors according their arity
045: final Constructor[] ctors = type.getConstructors();
046: if (ctors.length > 1) {
047: Arrays.sort(ctors, new Comparator() {
048: public int compare(final Object o1, final Object o2) {
049: return ((Constructor) o2).getParameterTypes().length
050: - ((Constructor) o1).getParameterTypes().length;
051: }
052: });
053: }
054:
055: final TypedValue[] typedDependencies = new TypedValue[dependencies.length];
056: for (int i = 0; i < dependencies.length; i++) {
057: Object dependency = dependencies[i];
058: Class depType = dependency.getClass();
059: if (depType.isPrimitive()) {
060: depType = Primitives.box(depType);
061: } else if (depType == TypedNull.class) {
062: depType = ((TypedNull) dependency).getType();
063: dependency = null;
064: }
065:
066: typedDependencies[i] = new TypedValue(depType, dependency);
067: }
068:
069: Constructor bestMatchingCtor = null;
070: Constructor possibleCtor = null;
071: int arity = Integer.MAX_VALUE;
072: final List matchingDependencies = new ArrayList();
073: for (int i = 0; bestMatchingCtor == null && i < ctors.length; i++) {
074: final Constructor constructor = ctors[i];
075: final Class[] parameterTypes = constructor
076: .getParameterTypes();
077: if (parameterTypes.length > dependencies.length) {
078: continue;
079: } else if (parameterTypes.length == 0) {
080: bestMatchingCtor = constructor;
081: break;
082: }
083: if (arity > parameterTypes.length) {
084: if (possibleCtor != null) {
085: bestMatchingCtor = possibleCtor;
086: continue;
087: }
088: arity = parameterTypes.length;
089: }
090:
091: for (int j = 0; j < parameterTypes.length; j++) {
092: if (parameterTypes[j].isPrimitive()) {
093: parameterTypes[j] = Primitives
094: .box(parameterTypes[j]);
095: }
096: }
097:
098: // first approach: test the ctor params against the dependencies in the sequence of the parameter
099: // declaration
100: matchingDependencies.clear();
101: for (int j = 0, k = 0; j < parameterTypes.length
102: && parameterTypes.length + k - j <= typedDependencies.length; k++) {
103: if (parameterTypes[j]
104: .isAssignableFrom(typedDependencies[k].type)) {
105: matchingDependencies
106: .add(typedDependencies[k].value);
107: if (++j == parameterTypes.length) {
108: bestMatchingCtor = constructor;
109: break;
110: }
111: }
112: }
113:
114: if (bestMatchingCtor == null && possibleCtor == null) {
115: possibleCtor = constructor; // assumption
116:
117: // try to match all dependencies in the sequence of the parameter declaration
118: final TypedValue[] deps = new TypedValue[typedDependencies.length];
119: System.arraycopy(typedDependencies, 0, deps, 0,
120: deps.length);
121: matchingDependencies.clear();
122: for (int j = 0; j < parameterTypes.length; j++) {
123: int assignable = -1;
124: for (int k = 0; k < deps.length; k++) {
125: if (deps[k] == null) {
126: continue;
127: }
128: if (deps[k].type == parameterTypes[j]) {
129: assignable = k;
130: // optimal match
131: break;
132: } else if (parameterTypes[j]
133: .isAssignableFrom(deps[k].type)) {
134: // use most specific type
135: if (assignable < 0
136: || deps[assignable].type
137: .isAssignableFrom(deps[k].type)) {
138: assignable = k;
139: }
140: }
141: }
142:
143: if (assignable >= 0) {
144: matchingDependencies
145: .add(deps[assignable].value);
146: deps[assignable] = null; // do not match same dep twice
147: } else {
148: possibleCtor = null;
149: break;
150: }
151: }
152: }
153: }
154:
155: if (bestMatchingCtor == null) {
156: if (possibleCtor == null) {
157: throw new ObjectAccessException(
158: "Cannot construct "
159: + type.getName()
160: + ", none of the dependencies match any constructor's parameters");
161: } else {
162: bestMatchingCtor = possibleCtor;
163: }
164: }
165:
166: try {
167: return bestMatchingCtor.newInstance(matchingDependencies
168: .toArray());
169: } catch (final InstantiationException e) {
170: throw new ObjectAccessException("Cannot construct "
171: + type.getName(), e);
172: } catch (final IllegalAccessException e) {
173: throw new ObjectAccessException("Cannot construct "
174: + type.getName(), e);
175: } catch (final InvocationTargetException e) {
176: throw new ObjectAccessException("Cannot construct "
177: + type.getName(), e);
178: }
179: }
180:
181: private static class TypedValue {
182: final Class type;
183: final Object value;
184:
185: public TypedValue(final Class type, final Object value) {
186: super();
187: this.type = type;
188: this.value = value;
189: }
190: }
191:
192: }
|