com.feilong.lib.xstream.core.util.DependencyInjectionFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
/*
* Copyright (c) 2007, 2009, 2010, 2011, 2012, 2013, 2016 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 30. March 2007 by Joerg Schaible
*/
package com.feilong.lib.xstream.core.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import com.feilong.lib.xstream.converters.reflection.ObjectAccessException;
/**
* A dependency injection factory.
*
* @author Jörg Schaible
* @since 1.2.2
*/
public class DependencyInjectionFactory{
/**
* Create an instance with dependency injection. The given dependencies are used to match the parameters of the
* constructors of the type. Constructors with most parameters are examined first. A parameter type sequence
* matching the sequence of the dependencies' types match first. Otherwise all the types of the dependencies must
* match one of the the parameters although no dependency is used twice. Use a {@link TypedNull} instance to inject
* null
as parameter.
*
* @param type
* the type to create an instance of
* @param dependencies
* the possible dependencies
* @return the instantiated object
* @throws ObjectAccessException
* if no instance can be generated
* @throws IllegalArgumentException
* if more than 63 dependencies have been provided
* @since 1.2.2
*/
public static Object newInstance(final Class type,final Object[] dependencies){
return newInstance(type, dependencies, null);
}
/**
* Create an instance with dependency injection. The given dependencies are used to match the parameters of the
* constructors of the type. Constructors with most parameters are examined first. A parameter type sequence
* matching the sequence of the dependencies' types match first. Otherwise all the types of the dependencies must
* match one of the the parameters although no dependency is used twice. Use a {@link TypedNull} instance to inject
* null
as parameter.
*
* @param type
* the type to create an instance of
* @param dependencies
* the possible dependencies
* @param usedDependencies
* bit mask set by the method for all used dependencies (may be null
)
* @return the instantiated object
* @throws ObjectAccessException
* if no instance can be generated
* @throws IllegalArgumentException
* if more than 63 dependencies have been provided
* @since 1.4
*/
public static Object newInstance(final Class type,final Object[] dependencies,final BitSet usedDependencies){
if (dependencies != null && dependencies.length > 63){
throw new IllegalArgumentException("More than 63 arguments are not supported");
}
Constructor bestMatchingCtor = null;
final ArrayList matchingDependencies = new ArrayList();
List possibleMatchingDependencies = null;
long usedDeps = 0;
long possibleUsedDeps = 0;
if (dependencies != null && dependencies.length > 0){
// sort available ctors according their arity
final Constructor[] ctors = type.getConstructors();
if (ctors.length > 1){
Arrays.sort(
ctors,
(o1,o2) -> ((Constructor) o2).getParameterTypes().length - ((Constructor) o1).getParameterTypes().length);
}
final TypedValue[] typedDependencies = new TypedValue[dependencies.length];
for (int i = 0; i < dependencies.length; i++){
Object dependency = dependencies[i];
Class depType = dependency.getClass();
if (depType.isPrimitive()){
depType = Primitives.box(depType);
}else if (depType == TypedNull.class){
depType = ((TypedNull) dependency).getType();
dependency = null;
}
typedDependencies[i] = new TypedValue(depType, dependency);
}
Constructor possibleCtor = null;
int arity = Integer.MAX_VALUE;
for (int i = 0; bestMatchingCtor == null && i < ctors.length; i++){
final Constructor constructor = ctors[i];
final Class[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length > dependencies.length){
continue;
}else if (parameterTypes.length == 0){
if (possibleCtor == null){
bestMatchingCtor = constructor;
}
break;
}
if (arity > parameterTypes.length){
if (possibleCtor != null){
continue;
}
arity = parameterTypes.length;
}
for (int j = 0; j < parameterTypes.length; j++){
if (parameterTypes[j].isPrimitive()){
parameterTypes[j] = Primitives.box(parameterTypes[j]);
}
}
// first approach: test the ctor params against the dependencies in the sequence
// of the parameter declaration
matchingDependencies.clear();
usedDeps = 0;
for (int j = 0, k = 0; j < parameterTypes.length && parameterTypes.length + k - j <= typedDependencies.length; k++){
if (parameterTypes[j].isAssignableFrom(typedDependencies[k].type)){
matchingDependencies.add(typedDependencies[k].value);
usedDeps |= 1L << k;
if (++j == parameterTypes.length){
bestMatchingCtor = constructor;
break;
}
}
}
if (bestMatchingCtor == null){
boolean possible = true; // assumption
// try to match all dependencies in the sequence of the parameter
// declaration
final TypedValue[] deps = new TypedValue[typedDependencies.length];
System.arraycopy(typedDependencies, 0, deps, 0, deps.length);
matchingDependencies.clear();
usedDeps = 0;
for (Class parameterType : parameterTypes){
int assignable = -1;
for (int k = 0; k < deps.length; k++){
if (deps[k] == null){
continue;
}
if (deps[k].type == parameterType){
assignable = k;
// optimal match
break;
}else if (parameterType.isAssignableFrom(deps[k].type)){
// use most specific type
if (assignable < 0 || deps[assignable].type != deps[k].type
&& deps[assignable].type.isAssignableFrom(deps[k].type)){
assignable = k;
}
}
}
if (assignable >= 0){
matchingDependencies.add(deps[assignable].value);
usedDeps |= 1L << assignable;
deps[assignable] = null; // do not match same dep twice
}else{
possible = false;
break;
}
}
if (possible){
// the smaller the value, the smaller the indices in the deps array
if (possibleCtor != null && usedDeps >= possibleUsedDeps){
continue;
}
possibleCtor = constructor;
possibleMatchingDependencies = (List) matchingDependencies.clone();
possibleUsedDeps = usedDeps;
}
}
}
if (bestMatchingCtor == null){
if (possibleCtor == null){
usedDeps = 0;
final ObjectAccessException ex = new ObjectAccessException(
"Cannot construct type, none of the arguments match any constructor's parameters");
ex.add("construction-type", type.getName());
throw ex;
}else{
bestMatchingCtor = possibleCtor;
matchingDependencies.clear();
matchingDependencies.addAll(possibleMatchingDependencies);
usedDeps = possibleUsedDeps;
}
}
}
Throwable th = null;
try{
final Object instance;
if (bestMatchingCtor == null){
instance = type.newInstance();
}else{
instance = bestMatchingCtor.newInstance(matchingDependencies.toArray());
}
if (usedDependencies != null){
usedDependencies.clear();
int i = 0;
for (long l = 1; l < usedDeps; l <<= 1, ++i){
if ((usedDeps & l) > 0){
usedDependencies.set(i);
}
}
}
return instance;
}catch (final InstantiationException e){
th = e;
}catch (final IllegalAccessException e){
th = e;
}catch (final InvocationTargetException e){
th = e.getCause();
}catch (final SecurityException e){
th = e;
}catch (final ExceptionInInitializerError e){
th = e;
}
final ObjectAccessException ex = new ObjectAccessException("Cannot construct type", th);
ex.add("construction-type", type.getName());
throw ex;
}
private static class TypedValue{
final Class type;
final Object value;
public TypedValue(final Class type, final Object value){
super();
this.type = type;
this.value = value;
}
@Override
public String toString(){
return type.getName() + ":" + value;
}
}
}