You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by zo...@apache.org on 2011/02/27 22:05:20 UTC
svn commit: r1075147 [10/23] - in /aries/tags/blueprint-0.3: ./
blueprint-annotation-api/ blueprint-annotation-api/src/
blueprint-annotation-api/src/main/ blueprint-annotation-api/src/main/java/
blueprint-annotation-api/src/main/java/org/ blueprint-ann...
Added: aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java?rev=1075147&view=auto
==============================================================================
--- aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java (added)
+++ aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java Sun Feb 27 21:05:07 2011
@@ -0,0 +1,446 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.blueprint.container;
+
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Hashtable;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
+import java.math.BigInteger;
+import java.math.BigDecimal;
+
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+import org.apache.aries.blueprint.di.CollectionRecipe;
+import org.apache.aries.blueprint.di.MapRecipe;
+import org.apache.aries.blueprint.utils.ReflectionUtils;
+
+import static org.apache.aries.blueprint.utils.ReflectionUtils.getRealCause;
+import org.osgi.service.blueprint.container.ReifiedType;
+import org.osgi.service.blueprint.container.Converter;
+
+/**
+ * Implementation of the Converter.
+ *
+ * This object contains all the registered Converters which can be registered
+ * by using {@link #registerConverter(Converter)}
+ * and unregistered using {@link #unregisterConverter(Converter)}.
+ *
+ * Each {@link org.osgi.service.blueprint.container.BlueprintContainer} has its own AggregateConverter
+ * used to register converters defined by the related blueprint bundle.
+ *
+ * @version $Rev: 990789 $, $Date: 2010-08-30 13:42:03 +0100 (Mon, 30 Aug 2010) $
+ */
+public class AggregateConverter implements Converter {
+
+ /**
+ * Objects implementing this interface will bypass the default conversion rules
+ * and be called directly to transform into the expected type.
+ */
+ public static interface Convertible {
+
+ Object convert(ReifiedType type) throws Exception;
+ }
+
+ private static class ConversionResult {
+
+ public final Converter converter;
+ public final Object value;
+
+ public ConversionResult(Converter converter, Object value) {
+ this.converter = converter;
+ this.value = value;
+ }
+ }
+
+ private ExtendedBlueprintContainer blueprintContainer;
+ private List<Converter> converters = new ArrayList<Converter>();
+
+ public AggregateConverter(ExtendedBlueprintContainer blueprintContainer) {
+ this.blueprintContainer = blueprintContainer;
+ }
+
+ public void registerConverter(Converter converter) {
+ converters.add(converter);
+ }
+
+ public void unregisterConverter(Converter converter) {
+ converters.remove(converter);
+ }
+
+ public boolean canConvert(final Object fromValue, final ReifiedType toType) {
+ if (fromValue == null) {
+ return true;
+ }
+ if (isAssignable(fromValue, toType)) {
+ return true;
+ }
+
+ boolean canConvert = false;
+ AccessControlContext acc = blueprintContainer.getAccessControlContext();
+ if (acc == null) {
+ canConvert = canConvertWithConverters(fromValue, toType);
+ } else {
+ canConvert = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return canConvertWithConverters(fromValue, toType);
+ }
+ }, acc);
+ }
+ if (canConvert) {
+ return true;
+ }
+
+ // TODO implement better logic ?!
+ try {
+ convert(fromValue, toType);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public Object convert(final Object fromValue, final ReifiedType type) throws Exception {
+ // Discard null values
+ if (fromValue == null) {
+ return null;
+ }
+ // First convert service proxies
+ if (fromValue instanceof Convertible) {
+ return ((Convertible) fromValue).convert(type);
+ }
+ // If the object is an instance of the type, just return it
+ if (isAssignable(fromValue, type)) {
+ return fromValue;
+ }
+ ConversionResult result = null;
+ AccessControlContext acc = blueprintContainer.getAccessControlContext();
+ if (acc == null) {
+ result = convertWithConverters(fromValue, type);
+ } else {
+ result = AccessController.doPrivileged(new PrivilegedExceptionAction<ConversionResult>() {
+ public ConversionResult run() throws Exception {
+ return convertWithConverters(fromValue, type);
+ }
+ }, acc);
+ }
+ if (result == null) {
+ if (fromValue instanceof Number && Number.class.isAssignableFrom(unwrap(toClass(type)))) {
+ return convertToNumber((Number) fromValue, toClass(type));
+ } else if (fromValue instanceof String) {
+ return convertFromString((String) fromValue, toClass(type), blueprintContainer);
+ } else if (toClass(type).isArray() && (fromValue instanceof Collection || fromValue.getClass().isArray())) {
+ return convertToArray(fromValue, type);
+ } else if (Map.class.isAssignableFrom(toClass(type)) && (fromValue instanceof Map || fromValue instanceof Dictionary)) {
+ return convertToMap(fromValue, type);
+ } else if (Dictionary.class.isAssignableFrom(toClass(type)) && (fromValue instanceof Map || fromValue instanceof Dictionary)) {
+ return convertToDictionary(fromValue, type);
+ } else if (Collection.class.isAssignableFrom(toClass(type)) && (fromValue instanceof Collection || fromValue.getClass().isArray())) {
+ return convertToCollection(fromValue, type);
+ } else {
+ throw new Exception("Unable to convert value " + fromValue + " to type " + type);
+ }
+ }
+ return result.value;
+ }
+
+ private Converter selectMatchingConverter(Object source, ReifiedType type) {
+ for (Converter converter : converters) {
+ if (converter.canConvert(source, type)) {
+ return converter;
+ }
+ }
+ return null;
+ }
+
+ private boolean canConvertWithConverters(Object source, ReifiedType type) {
+ return selectMatchingConverter(source,type) != null;
+ }
+
+ private ConversionResult convertWithConverters(Object source, ReifiedType type) throws Exception {
+
+ Converter converter = selectMatchingConverter(source,type);
+
+ if (converter == null) return null;
+
+ Object value = converter.convert(source, type);
+ return new ConversionResult(converter,value);
+ }
+
+ public Object convertToNumber(Number value, Class toType) throws Exception {
+ toType = unwrap(toType);
+ if (AtomicInteger.class == toType) {
+ return new AtomicInteger((Integer) convertToNumber(value, Integer.class));
+ } else if (AtomicLong.class == toType) {
+ return new AtomicLong((Long) convertToNumber(value, Long.class));
+ } else if (Integer.class == toType) {
+ return value.intValue();
+ } else if (Short.class == toType) {
+ return value.shortValue();
+ } else if (Long.class == toType) {
+ return value.longValue();
+ } else if (Float.class == toType) {
+ return value.floatValue();
+ } else if (Double.class == toType) {
+ return value.doubleValue();
+ } else if (Byte.class == toType) {
+ return value.byteValue();
+ } else if (BigInteger.class == toType) {
+ return new BigInteger(value.toString());
+ } else if (BigDecimal.class == toType) {
+ return new BigDecimal(value.toString());
+ } else {
+ throw new Exception("Unable to convert number " + value + " to " + toType);
+ }
+ }
+
+ public Object convertFromString(String value, Class toType, Object loader) throws Exception {
+ toType = unwrap(toType);
+ if (ReifiedType.class == toType) {
+ try {
+ return GenericType.parse(value, loader);
+ } catch (ClassNotFoundException e) {
+ throw new Exception("Unable to convert", e);
+ }
+ } else if (Class.class == toType) {
+ try {
+ return GenericType.parse(value, loader).getRawClass();
+ } catch (ClassNotFoundException e) {
+ throw new Exception("Unable to convert", e);
+ }
+ } else if (Locale.class == toType) {
+ String[] tokens = value.split("_");
+ if (tokens.length == 1) {
+ return new Locale(tokens[0]);
+ } else if (tokens.length == 2) {
+ return new Locale(tokens[0], tokens[1]);
+ } else if (tokens.length == 3) {
+ return new Locale(tokens[0], tokens[1], tokens[2]);
+ } else {
+ throw new Exception("Invalid locale string:" + value);
+ }
+ } else if (Pattern.class == toType) {
+ return Pattern.compile(value);
+ } else if (Properties.class == toType) {
+ Properties props = new Properties();
+ ByteArrayInputStream in = new ByteArrayInputStream(value.getBytes("UTF8"));
+ props.load(in);
+ return props;
+ } else if (Boolean.class == toType) {
+ if ("yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value)) {
+ return Boolean.TRUE;
+ } else if ("no".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value) || "off".equalsIgnoreCase(value)) {
+ return Boolean.FALSE;
+ } else {
+ throw new RuntimeException("Invalid boolean value: " + value);
+ }
+ } else if (Integer.class == toType) {
+ return Integer.valueOf(value);
+ } else if (Short.class == toType) {
+ return Short.valueOf(value);
+ } else if (Long.class == toType) {
+ return Long.valueOf(value);
+ } else if (Float.class == toType) {
+ return Float.valueOf(value);
+ } else if (Double.class == toType) {
+ return Double.valueOf(value);
+ } else if (Character.class == toType) {
+ if (value.length() == 6 && value.startsWith("\\u")) {
+ int code = Integer.parseInt(value.substring(2), 16);
+ return (char)code;
+ } else if (value.length() == 1) {
+ return value.charAt(0);
+ } else {
+ throw new Exception("Invalid value for character type: " + value);
+ }
+ } else if (Byte.class == toType) {
+ return Byte.valueOf(value);
+ } else if (Enum.class.isAssignableFrom(toType)) {
+ return Enum.valueOf((Class<Enum>) toType, value);
+ } else {
+ return createObject(value, toType);
+ }
+ }
+
+ private Object createObject(String value, Class type) throws Exception {
+ if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) {
+ throw new Exception("Unable to convert value " + value + " to type " + type + ". Type " + type + " is an interface or an abstract class");
+ }
+ Constructor constructor = null;
+ try {
+ constructor = type.getConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Unable to convert to " + type);
+ }
+ try {
+ return ReflectionUtils.newInstance(blueprintContainer.getAccessControlContext(), constructor, value);
+ } catch (Exception e) {
+ throw new Exception("Unable to convert ", getRealCause(e));
+ }
+ }
+
+ private Object convertToCollection(Object obj, ReifiedType type) throws Exception {
+ ReifiedType valueType = type.getActualTypeArgument(0);
+ Collection newCol = (Collection) ReflectionUtils.newInstance(blueprintContainer.getAccessControlContext(),
+ CollectionRecipe.getCollection(toClass(type)));
+ if (obj.getClass().isArray()) {
+ for (int i = 0; i < Array.getLength(obj); i++) {
+ try {
+ newCol.add(convert(Array.get(obj, i), valueType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting array element)", t);
+ }
+ }
+ } else {
+ for (Object item : (Collection) obj) {
+ try {
+ newCol.add(convert(item, valueType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting collection entry)", t);
+ }
+ }
+ }
+ return newCol;
+ }
+
+ private Object convertToDictionary(Object obj, ReifiedType type) throws Exception {
+ ReifiedType keyType = type.getActualTypeArgument(0);
+ ReifiedType valueType = type.getActualTypeArgument(1);
+ Dictionary newDic = new Hashtable();
+ if (obj instanceof Dictionary) {
+ Dictionary dic = (Dictionary) obj;
+ for (Enumeration keyEnum = dic.keys(); keyEnum.hasMoreElements();) {
+ Object key = keyEnum.nextElement();
+ try {
+ newDic.put(convert(key, keyType), convert(dic.get(key), valueType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+ }
+ }
+ } else {
+ for (Map.Entry e : ((Map<Object,Object>) obj).entrySet()) {
+ try {
+ newDic.put(convert(e.getKey(), keyType), convert(e.getValue(), valueType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+ }
+ }
+ }
+ return newDic;
+ }
+
+ private Object convertToMap(Object obj, ReifiedType type) throws Exception {
+ ReifiedType keyType = type.getActualTypeArgument(0);
+ ReifiedType valueType = type.getActualTypeArgument(1);
+ Map newMap = (Map) ReflectionUtils.newInstance(blueprintContainer.getAccessControlContext(),
+ MapRecipe.getMap(toClass(type)));
+ if (obj instanceof Dictionary) {
+ Dictionary dic = (Dictionary) obj;
+ for (Enumeration keyEnum = dic.keys(); keyEnum.hasMoreElements();) {
+ Object key = keyEnum.nextElement();
+ try {
+ newMap.put(convert(key, keyType), convert(dic.get(key), valueType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+ }
+ }
+ } else {
+ for (Map.Entry e : ((Map<Object,Object>) obj).entrySet()) {
+ try {
+ newMap.put(convert(e.getKey(), keyType), convert(e.getValue(), valueType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+ }
+ }
+ }
+ return newMap;
+ }
+
+ private Object convertToArray(Object obj, ReifiedType type) throws Exception {
+ if (obj instanceof Collection) {
+ obj = ((Collection) obj).toArray();
+ }
+ if (!obj.getClass().isArray()) {
+ throw new Exception("Unable to convert from " + obj + " to " + type);
+ }
+ ReifiedType componentType;
+ if (type.size() > 0) {
+ componentType = type.getActualTypeArgument(0);
+ } else {
+ componentType = new GenericType(type.getRawClass().getComponentType());
+ }
+ Object array = Array.newInstance(toClass(componentType), Array.getLength(obj));
+ for (int i = 0; i < Array.getLength(obj); i++) {
+ try {
+ Array.set(array, i, convert(Array.get(obj, i), componentType));
+ } catch (Exception t) {
+ throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting array element)", t);
+ }
+ }
+ return array;
+ }
+
+ public static boolean isAssignable(Object source, ReifiedType target) {
+ return source == null
+ || (target.size() == 0
+ && unwrap(target.getRawClass()).isAssignableFrom(unwrap(source.getClass())));
+ }
+
+ private static Class unwrap(Class c) {
+ Class u = primitives.get(c);
+ return u != null ? u : c;
+ }
+
+ private static final Map<Class, Class> primitives;
+ static {
+ primitives = new HashMap<Class, Class>();
+ primitives.put(byte.class, Byte.class);
+ primitives.put(short.class, Short.class);
+ primitives.put(char.class, Character.class);
+ primitives.put(int.class, Integer.class);
+ primitives.put(long.class, Long.class);
+ primitives.put(float.class, Float.class);
+ primitives.put(double.class, Double.class);
+ primitives.put(boolean.class, Boolean.class);
+ }
+
+ public Object convert(Object source, Type target) throws Exception {
+ return convert( source, new GenericType(target));
+ }
+
+ private Class toClass(ReifiedType type) {
+ return type.getRawClass();
+ }
+
+}
\ No newline at end of file
Added: aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java?rev=1075147&view=auto
==============================================================================
--- aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java (added)
+++ aries/tags/blueprint-0.3/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java Sun Feb 27 21:05:07 2011
@@ -0,0 +1,920 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.container;
+
+import static org.apache.aries.blueprint.utils.ReflectionUtils.getRealCause;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.aries.blueprint.BeanProcessor;
+import org.apache.aries.blueprint.ComponentDefinitionRegistry;
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+import org.apache.aries.blueprint.Interceptor;
+import org.apache.aries.blueprint.di.AbstractRecipe;
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.proxy.Collaborator;
+import org.apache.aries.blueprint.proxy.ProxyUtils;
+import org.apache.aries.blueprint.utils.ReflectionUtils;
+import org.apache.aries.blueprint.utils.ReflectionUtils.PropertyDescriptor;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.container.ReifiedType;
+import org.osgi.service.blueprint.reflect.BeanMetadata;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A <code>Recipe</code> to create POJOs.
+ *
+ * @version $Rev: 1055827 $, $Date: 2011-01-06 11:03:36 +0000 (Thu, 06 Jan 2011) $
+ */
+public class BeanRecipe extends AbstractRecipe {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BeanRecipe.class);
+
+ private final ExtendedBlueprintContainer blueprintContainer;
+ private final LinkedHashMap<String,Object> properties = new LinkedHashMap<String,Object>();
+ private final Object type;
+
+ private String initMethod;
+ private String destroyMethod;
+ private List<Recipe> explicitDependencies;
+
+ private Recipe factory;
+ private String factoryMethod;
+ private List<Object> arguments;
+ private List<String> argTypes;
+ private boolean reorderArguments;
+ private final boolean allowsFieldInjection;
+ private BeanMetadata interceptorLookupKey;
+
+
+ public BeanRecipe(String name, ExtendedBlueprintContainer blueprintContainer, Object type, boolean allowsFieldInjection) {
+ super(name);
+ this.blueprintContainer = blueprintContainer;
+ this.type = type;
+ this.allowsFieldInjection = allowsFieldInjection;
+ }
+
+ public Object getProperty(String name) {
+ return properties.get(name);
+ }
+
+ public Map<String, Object> getProperties() {
+ return new LinkedHashMap<String, Object>(properties);
+ }
+
+ public void setProperty(String name, Object value) {
+ properties.put(name, value);
+ }
+
+ public void setFactoryMethod(String method) {
+ this.factoryMethod = method;
+ }
+
+ public void setFactoryComponent(Recipe factory) {
+ this.factory = factory;
+ }
+
+ public void setArgTypes(List<String> argTypes) {
+ this.argTypes = argTypes;
+ }
+
+ public void setArguments(List<Object> arguments) {
+ this.arguments = arguments;
+ }
+
+ public void setReorderArguments(boolean reorder) {
+ this.reorderArguments = reorder;
+ }
+
+ public void setInitMethod(String initMethod) {
+ this.initMethod = initMethod;
+ }
+
+ public String getInitMethod() {
+ return initMethod;
+ }
+
+ public void setDestroyMethod(String destroyMethod) {
+ this.destroyMethod = destroyMethod;
+ }
+
+ public String getDestroyMethod() {
+ return destroyMethod;
+ }
+
+ public List<Recipe> getExplicitDependencies() {
+ return explicitDependencies;
+ }
+
+ public void setExplicitDependencies(List<Recipe> explicitDependencies) {
+ this.explicitDependencies = explicitDependencies;
+ }
+
+ public void setInterceptorLookupKey(BeanMetadata metadata) {
+ interceptorLookupKey = metadata;
+ }
+
+ @Override
+ public List<Recipe> getConstructorDependencies() {
+ List<Recipe> recipes = new ArrayList<Recipe>();
+ if (explicitDependencies != null) {
+ recipes.addAll(explicitDependencies);
+ }
+ if (arguments != null) {
+ for (Object argument : arguments) {
+ if (argument instanceof Recipe) {
+ recipes.add((Recipe)argument);
+ }
+ }
+ }
+ return recipes;
+ }
+
+ public List<Recipe> getDependencies() {
+ List<Recipe> recipes = new ArrayList<Recipe>();
+ for (Object o : properties.values()) {
+ if (o instanceof Recipe) {
+ Recipe recipe = (Recipe) o;
+ recipes.add(recipe);
+ }
+ }
+ recipes.addAll(getConstructorDependencies());
+ return recipes;
+ }
+
+ private void instantiateExplicitDependencies() {
+ if (explicitDependencies != null) {
+ for (Recipe recipe : explicitDependencies) {
+ recipe.create();
+ }
+ }
+ }
+
+ @Override
+ protected Class loadClass(String className) {
+ ClassLoader loader = type instanceof Class ? ((Class) type).getClassLoader() : null;
+ ReifiedType t = loadType(className, loader);
+ return t != null ? t.getRawClass() : null;
+ }
+
+ @Override
+ protected ReifiedType loadType(String className) {
+ return loadType(className, type instanceof Class ? ((Class) type).getClassLoader() : null);
+ }
+
+ private Object getInstance() throws ComponentDefinitionException {
+ Object instance;
+
+ // Instanciate arguments
+ List<Object> args = new ArrayList<Object>();
+ List<ReifiedType> argTypes = new ArrayList<ReifiedType>();
+ if (arguments != null) {
+ for (int i = 0; i < arguments.size(); i++) {
+ Object arg = arguments.get(i);
+ if (arg instanceof Recipe) {
+ args.add(((Recipe) arg).create());
+ } else {
+ args.add(arg);
+ }
+ if (this.argTypes != null) {
+ argTypes.add(this.argTypes.get(i) != null ? loadType(this.argTypes.get(i)) : null);
+ }
+ }
+ }
+
+ if (factory != null) {
+ // look for instance method on factory object
+ Object factoryObj = factory.create();
+
+ // If the factory is a service reference, we need to get hold of the actual proxy for the service
+ if (factoryObj instanceof ReferenceRecipe.ServiceProxyWrapper) {
+ try {
+ factoryObj = ((ReferenceRecipe.ServiceProxyWrapper) factoryObj).convert(new ReifiedType(Object.class));
+ } catch (Exception e) {
+ throw new ComponentDefinitionException("Error when instantiating bean " + getName() + " of class " + getType(), getRealCause(e));
+ }
+ }
+
+ // Map of matching methods
+ Map<Method, List<Object>> matches = findMatchingMethods(factoryObj.getClass(), factoryMethod, true, args, argTypes);
+ if (matches.size() == 1) {
+ try {
+ Map.Entry<Method, List<Object>> match = matches.entrySet().iterator().next();
+ instance = invoke(match.getKey(), factoryObj, match.getValue().toArray());
+ } catch (Throwable e) {
+ throw new ComponentDefinitionException("Error when instantiating bean " + getName() + " of class " + getType(), getRealCause(e));
+ }
+ } else if (matches.size() == 0) {
+ throw new ComponentDefinitionException("Unable to find a matching factory method " + factoryMethod + " on class " + factoryObj.getClass().getName() + " for arguments " + args + " when instanciating bean " + getName());
+ } else {
+ throw new ComponentDefinitionException("Multiple matching factory methods " + factoryMethod + " found on class " + factoryObj.getClass().getName() + " for arguments " + args + " when instanciating bean " + getName() + ": " + matches.keySet());
+ }
+ } else if (factoryMethod != null) {
+ // Map of matching methods
+ Map<Method, List<Object>> matches = findMatchingMethods(getType(), factoryMethod, false, args, argTypes);
+ if (matches.size() == 1) {
+ try {
+ Map.Entry<Method, List<Object>> match = matches.entrySet().iterator().next();
+ instance = invoke(match.getKey(), null, match.getValue().toArray());
+ } catch (Throwable e) {
+ throw new ComponentDefinitionException("Error when instanciating bean " + getName() + " of class " + getType(), getRealCause(e));
+ }
+ } else if (matches.size() == 0) {
+ throw new ComponentDefinitionException("Unable to find a matching factory method " + factoryMethod + " on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName());
+ } else {
+ throw new ComponentDefinitionException("Multiple matching factory methods " + factoryMethod + " found on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName() + ": " + matches.keySet());
+ }
+ } else {
+ if (getType() == null) {
+ throw new ComponentDefinitionException("No factoryMethod nor class is defined for this bean");
+ }
+ // Map of matching constructors
+ Map<Constructor, List<Object>> matches = findMatchingConstructors(getType(), args, argTypes);
+ if (matches.size() == 1) {
+ try {
+ Map.Entry<Constructor, List<Object>> match = matches.entrySet().iterator().next();
+ instance = newInstance(match.getKey(), match.getValue().toArray());
+ } catch (Throwable e) {
+ throw new ComponentDefinitionException("Error when instanciating bean " + getName() + " of class " + getType(), getRealCause(e));
+ }
+ } else if (matches.size() == 0) {
+ throw new ComponentDefinitionException("Unable to find a matching constructor on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName());
+ } else {
+ throw new ComponentDefinitionException("Multiple matching constructors found on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName() + ": " + matches.keySet());
+ }
+ }
+
+ return instance;
+ }
+
+ private Map<Method, List<Object>> findMatchingMethods(Class type, String name, boolean instance, List<Object> args, List<ReifiedType> types) {
+ Map<Method, List<Object>> matches = new HashMap<Method, List<Object>>();
+ // Get constructors
+ List<Method> methods = new ArrayList<Method>(Arrays.asList(type.getMethods()));
+ // Discard any signature with wrong cardinality
+ for (Iterator<Method> it = methods.iterator(); it.hasNext();) {
+ Method mth = it.next();
+ if (!mth.getName().equals(name)) {
+ it.remove();
+ } else if (mth.getParameterTypes().length != args.size()) {
+ it.remove();
+ } else if (instance ^ !Modifier.isStatic(mth.getModifiers())) {
+ it.remove();
+ } else if (mth.isBridge()) {
+ it.remove();
+ }
+ }
+
+ // on some JVMs (J9) hidden static methods are returned by Class.getMethods so we need to weed them out
+ // to reduce ambiguity
+ if (!instance) {
+ methods = applyStaticHidingRules(methods);
+ }
+
+ // Find a direct match with assignment
+ if (matches.size() != 1) {
+ Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
+ for (Method mth : methods) {
+ boolean found = true;
+ List<Object> match = new ArrayList<Object>();
+ for (int i = 0; i < args.size(); i++) {
+ ReifiedType argType = new GenericType(mth.getGenericParameterTypes()[i]);
+ if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
+ found = false;
+ break;
+ }
+ if (!AggregateConverter.isAssignable(args.get(i), argType)) {
+ found = false;
+ break;
+ }
+ try {
+ match.add(convert(args.get(i), mth.getGenericParameterTypes()[i]));
+ } catch (Throwable t) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ nmatches.put(mth, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ // Find a direct match with conversion
+ if (matches.size() != 1) {
+ Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
+ for (Method mth : methods) {
+ boolean found = true;
+ List<Object> match = new ArrayList<Object>();
+ for (int i = 0; i < args.size(); i++) {
+ ReifiedType argType = new GenericType(mth.getGenericParameterTypes()[i]);
+ if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
+ found = false;
+ break;
+ }
+ try {
+ Object val = convert(args.get(i), argType);
+ match.add(val);
+ } catch (Throwable t) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ nmatches.put(mth, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ // Start reordering with assignment
+ if (matches.size() != 1 && reorderArguments && args.size() > 1) {
+ Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
+ for (Method mth : methods) {
+ ArgumentMatcher matcher = new ArgumentMatcher(mth.getGenericParameterTypes(), false);
+ List<Object> match = matcher.match(args, types);
+ if (match != null) {
+ nmatches.put(mth, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ // Start reordering with conversion
+ if (matches.size() != 1 && reorderArguments && args.size() > 1) {
+ Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
+ for (Method mth : methods) {
+ ArgumentMatcher matcher = new ArgumentMatcher(mth.getGenericParameterTypes(), true);
+ List<Object> match = matcher.match(args, types);
+ if (match != null) {
+ nmatches.put(mth, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+
+ return matches;
+ }
+
+ private static List<Method> applyStaticHidingRules(Collection<Method> methods) {
+ List<Method> result = new ArrayList<Method>(methods.size());
+ for (Method m : methods) {
+ boolean toBeAdded = true;
+
+ Iterator<Method> it = result.iterator();
+ inner: while (it.hasNext()) {
+ Method other = it.next();
+ if (hasIdenticalParameters(m, other)) {
+ Class<?> mClass = m.getDeclaringClass();
+ Class<?> otherClass = other.getDeclaringClass();
+
+ if (mClass.isAssignableFrom(otherClass)) {
+ toBeAdded = false;
+ break inner;
+ } else if (otherClass.isAssignableFrom(mClass)) {
+ it.remove();
+ }
+ }
+ }
+
+ if (toBeAdded) result.add(m);
+ }
+
+ return result;
+ }
+
+ private static boolean hasIdenticalParameters(Method one, Method two) {
+ Class<?>[] oneTypes = one.getParameterTypes();
+ Class<?>[] twoTypes = two.getParameterTypes();
+
+ if (oneTypes.length != twoTypes.length) return false;
+
+ for (int i=0; i<oneTypes.length; i++) {
+ if (!oneTypes[i].equals(twoTypes[i])) return false;
+ }
+
+ return true;
+ }
+
+ private Map<Constructor, List<Object>> findMatchingConstructors(Class type, List<Object> args, List<ReifiedType> types) {
+ Map<Constructor, List<Object>> matches = new HashMap<Constructor, List<Object>>();
+ // Get constructors
+ List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(type.getConstructors()));
+ // Discard any signature with wrong cardinality
+ for (Iterator<Constructor> it = constructors.iterator(); it.hasNext();) {
+ if (it.next().getParameterTypes().length != args.size()) {
+ it.remove();
+ }
+ }
+ // Find a direct match with assignment
+ if (matches.size() != 1) {
+ Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
+ for (Constructor cns : constructors) {
+ boolean found = true;
+ List<Object> match = new ArrayList<Object>();
+ for (int i = 0; i < args.size(); i++) {
+ ReifiedType argType = new GenericType(cns.getGenericParameterTypes()[i]);
+ if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
+ found = false;
+ break;
+ }
+ if (!AggregateConverter.isAssignable(args.get(i), argType)) {
+ found = false;
+ break;
+ }
+ try {
+ match.add(convert(args.get(i), cns.getGenericParameterTypes()[i]));
+ } catch (Throwable t) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ nmatches.put(cns, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ // Find a direct match with conversion
+ if (matches.size() != 1) {
+ Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
+ for (Constructor cns : constructors) {
+ boolean found = true;
+ List<Object> match = new ArrayList<Object>();
+ for (int i = 0; i < args.size(); i++) {
+ ReifiedType argType = new GenericType(cns.getGenericParameterTypes()[i]);
+ if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
+ found = false;
+ break;
+ }
+ try {
+ Object val = convert(args.get(i), argType);
+ match.add(val);
+ } catch (Throwable t) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ nmatches.put(cns, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ // Start reordering with assignment
+ if (matches.size() != 1 && reorderArguments && arguments.size() > 1) {
+ Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
+ for (Constructor cns : constructors) {
+ ArgumentMatcher matcher = new ArgumentMatcher(cns.getGenericParameterTypes(), false);
+ List<Object> match = matcher.match(args, types);
+ if (match != null) {
+ nmatches.put(cns, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ // Start reordering with conversion
+ if (matches.size() != 1 && reorderArguments && arguments.size() > 1) {
+ Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
+ for (Constructor cns : constructors) {
+ ArgumentMatcher matcher = new ArgumentMatcher(cns.getGenericParameterTypes(), true);
+ List<Object> match = matcher.match(args, types);
+ if (match != null) {
+ nmatches.put(cns, match);
+ }
+ }
+ if (nmatches.size() > 0) {
+ matches = nmatches;
+ }
+ }
+ return matches;
+ }
+
+ /**
+ * Returns init method (if any). Throws exception if the init-method was set explicitly on the bean
+ * and the method is not found on the instance.
+ */
+ protected Method getInitMethod(Object instance) throws ComponentDefinitionException {
+ Method method = null;
+ if (initMethod != null && initMethod.length() > 0) {
+ method = ReflectionUtils.getLifecycleMethod(instance.getClass(), initMethod);
+ if (method == null) {
+ throw new ComponentDefinitionException("Component '" + getName() + "' does not have init-method: " + initMethod);
+ }
+ }
+ return method;
+ }
+
+ /**
+ * Returns destroy method (if any). Throws exception if the destroy-method was set explicitly on the bean
+ * and the method is not found on the instance.
+ */
+ public Method getDestroyMethod(Object instance) throws ComponentDefinitionException {
+ Method method = null;
+ if (destroyMethod != null && destroyMethod.length() > 0) {
+ method = ReflectionUtils.getLifecycleMethod(instance.getClass(), destroyMethod);
+ if (method == null) {
+ throw new ComponentDefinitionException("Component '" + getName() + "' does not have destroy-method: " + destroyMethod);
+ }
+ }
+ return method;
+ }
+
+ /**
+ * Small helper class, to construct a chain of BeanCreators.
+ * <br>
+ * Each bean creator in the chain will return a bean that has been
+ * processed by every BeanProcessor in the chain before it.
+ */
+ private static class BeanCreatorChain implements BeanProcessor.BeanCreator {
+ public enum ChainType{Before,After};
+ private final BeanProcessor.BeanCreator parentBeanCreator;
+ private final BeanProcessor parentBeanProcessor;
+ private final BeanMetadata beanData;
+ private final String beanName;
+ private final ChainType when;
+ public BeanCreatorChain(BeanProcessor.BeanCreator parentBeanCreator,
+ BeanProcessor parentBeanProcessor,
+ BeanMetadata beanData,
+ String beanName,
+ ChainType when){
+ this.parentBeanCreator = parentBeanCreator;
+ this.parentBeanProcessor = parentBeanProcessor;
+ this.beanData = beanData;
+ this.beanName = beanName;
+ this.when = when;
+ }
+
+ public Object getBean() {
+ Object previousBean = parentBeanCreator.getBean();
+ Object processed = null;
+ switch(when){
+ case Before :
+ processed = parentBeanProcessor.beforeInit(previousBean, beanName, parentBeanCreator, beanData);
+ break;
+ case After:
+ processed = parentBeanProcessor.afterInit(previousBean, beanName, parentBeanCreator, beanData);
+ break;
+ }
+ return processed;
+ }
+ }
+
+ private Object runBeanProcPreInit(Object obj){
+ String beanName = getName();
+ BeanMetadata beanData = (BeanMetadata) blueprintContainer
+ .getComponentDefinitionRegistry().getComponentDefinition(beanName);
+ List<BeanProcessor> processors = blueprintContainer.getProcessors(BeanProcessor.class);
+
+ //The start link of the chain, that provides the
+ //original, unprocessed bean to the head of the chain.
+ BeanProcessor.BeanCreator initialBeanCreator = new BeanProcessor.BeanCreator() {
+ public Object getBean() {
+ Object obj = getInstance();
+ //getinit, getdestroy, addpartial object don't need calling again.
+ //however, property injection does.
+ setProperties(obj);
+ return obj;
+ }
+ };
+
+ BeanProcessor.BeanCreator currentCreator = initialBeanCreator;
+ for(BeanProcessor processor : processors){
+ obj = processor.beforeInit(obj, getName(), currentCreator, beanData);
+ currentCreator = new BeanCreatorChain(currentCreator, processor, beanData, beanName, BeanCreatorChain.ChainType.Before);
+ }
+ return obj;
+ }
+
+ private void runBeanProcInit(Method initMethod, Object obj){
+ // call init method
+ if (initMethod != null) {
+ try {
+ invoke(initMethod, obj, (Object[]) null);
+ } catch (Throwable t) {
+ throw new ComponentDefinitionException("Unable to intialize bean " + getName(), getRealCause(t));
+ }
+ }
+ }
+
+ private Object runBeanProcPostInit(Object obj){
+ String beanName = getName();
+ BeanMetadata beanData = (BeanMetadata) blueprintContainer
+ .getComponentDefinitionRegistry().getComponentDefinition(beanName);
+ List<BeanProcessor> processors = blueprintContainer.getProcessors(BeanProcessor.class);
+
+ //The start link of the chain, that provides the
+ //original, unprocessed bean to the head of the chain.
+ BeanProcessor.BeanCreator initialBeanCreator = new BeanProcessor.BeanCreator() {
+ public Object getBean() {
+ Object obj = getInstance();
+ //getinit, getdestroy, addpartial object don't need calling again.
+ //however, property injection does.
+ setProperties(obj);
+ //as this is the post init chain, new beans need to go thru
+ //the pre-init chain, and then have init called, before
+ //being passed along the post-init chain.
+ obj = runBeanProcPreInit(obj);
+ runBeanProcInit(getInitMethod(obj), obj);
+ return obj;
+ }
+ };
+
+ BeanProcessor.BeanCreator currentCreator = initialBeanCreator;
+ for(BeanProcessor processor : processors){
+ obj = processor.afterInit(obj, getName(), currentCreator, beanData);
+ currentCreator = new BeanCreatorChain(currentCreator, processor, beanData, beanName, BeanCreatorChain.ChainType.After);
+ }
+ return obj;
+ }
+
+ private Object addInterceptors(final Object original)
+ throws ComponentDefinitionException {
+
+ Object intercepted = null;
+ ComponentDefinitionRegistry reg = blueprintContainer
+ .getComponentDefinitionRegistry();
+ List<Interceptor> interceptors = reg.getInterceptors(interceptorLookupKey);
+ if (interceptors != null && interceptors.size() > 0) {
+ try {
+ Bundle b = FrameworkUtil.getBundle(original.getClass());
+ if (b == null) {
+ // we have a class from the framework parent, so use our bundle for proxying.
+ b = blueprintContainer.getBundleContext().getBundle();
+ }
+ intercepted = BlueprintExtender.getProxyManager().createProxy(b,
+ ProxyUtils.asList(original.getClass()), ProxyUtils.passThrough(original),
+ new Collaborator(interceptorLookupKey, interceptors));
+ } catch (org.apache.aries.proxy.UnableToProxyException e) {
+ Bundle b = blueprintContainer.getBundleContext().getBundle();
+ throw new ComponentDefinitionException("Unable to create proxy for bean " + name + " in bundle " + b.getSymbolicName() + " version " + b.getVersion(), e);
+ }
+ } else {
+ intercepted = original;
+ }
+ return intercepted;
+ }
+
+ @Override
+ protected Object internalCreate() throws ComponentDefinitionException {
+
+ instantiateExplicitDependencies();
+
+ Object obj = getInstance();
+
+ // check for init lifecycle method (if any)
+ Method initMethod = getInitMethod(obj);
+
+ // check for destroy lifecycle method (if any)
+ getDestroyMethod(obj);
+
+ // Add partially created object to the container
+// if (initMethod == null) {
+ addPartialObject(obj);
+// }
+
+ // inject properties
+ setProperties(obj);
+
+ obj = runBeanProcPreInit(obj);
+
+ runBeanProcInit(initMethod, obj);
+
+ obj = runBeanProcPostInit(obj);
+
+ obj = addInterceptors(obj);
+
+ return obj;
+ }
+
+ @Override
+ public void destroy(Object obj) {
+ for (BeanProcessor processor : blueprintContainer.getProcessors(BeanProcessor.class)) {
+ processor.beforeDestroy(obj, getName());
+ }
+ try {
+ Method method = getDestroyMethod(obj);
+ if (method != null) {
+ invoke(method, obj, (Object[]) null);
+ }
+ } catch (Exception e) {
+ LOGGER.info("Error invoking destroy method", getRealCause(e));
+ }
+ for (BeanProcessor processor : blueprintContainer.getProcessors(BeanProcessor.class)) {
+ processor.afterDestroy(obj, getName());
+ }
+ }
+
+ public void setProperties(Object instance) throws ComponentDefinitionException {
+ // clone the properties so they can be used again
+ Map<String,Object> propertyValues = new LinkedHashMap<String,Object>(properties);
+ setProperties(propertyValues, instance, instance.getClass());
+ }
+
+ public Class getType() {
+ if (type instanceof Class) {
+ return (Class) type;
+ } else if (type instanceof String) {
+ return loadClass((String) type);
+ } else {
+ return null;
+ }
+ }
+
+ private void setProperties(Map<String, Object> propertyValues, Object instance, Class clazz) {
+ // set remaining properties
+ for (Map.Entry<String, Object> entry : propertyValues.entrySet()) {
+ String propertyName = entry.getKey();
+ Object propertyValue = entry.getValue();
+
+ setProperty(instance, clazz, propertyName, propertyValue);
+ }
+
+ }
+
+ private void setProperty(Object instance, Class clazz, String propertyName, Object propertyValue) {
+ String[] names = propertyName.split("\\.");
+ for (int i = 0; i < names.length - 1; i++) {
+ PropertyDescriptor pd = getPropertyDescriptor(clazz, names[i]);
+ if (pd.allowsGet()) {
+ try {
+ instance = pd.get(instance, blueprintContainer);
+ } catch (Exception e) {
+ throw new ComponentDefinitionException("Error getting property: " + names[i] + " on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName(), getRealCause(e));
+ }
+ if (instance == null) {
+ throw new ComponentDefinitionException("Error setting compound property " + propertyName + " on bean " + getName() + ". Property " + names[i] + " is null");
+ }
+ clazz = instance.getClass();
+ } else {
+ throw new ComponentDefinitionException("No getter for " + names[i] + " property on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName());
+ }
+ }
+
+ // Instantiate value
+ if (propertyValue instanceof Recipe) {
+ propertyValue = ((Recipe) propertyValue).create();
+ }
+
+ final PropertyDescriptor pd = getPropertyDescriptor(clazz, names[names.length - 1]);
+ if (pd.allowsSet()) {
+ try {
+ pd.set(instance, propertyValue, blueprintContainer);
+ } catch (Exception e) {
+ throw new ComponentDefinitionException("Error setting property: " + pd, getRealCause(e));
+ }
+ } else {
+ throw new ComponentDefinitionException("No setter for " + names[names.length - 1] + " property");
+ }
+ }
+
+ private ReflectionUtils.PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String name) {
+ for (ReflectionUtils.PropertyDescriptor pd : ReflectionUtils.getPropertyDescriptors(clazz, allowsFieldInjection)) {
+ if (pd.getName().equals(name)) {
+ return pd;
+ }
+ }
+ throw new ComponentDefinitionException("Unable to find property descriptor " + name + " on class " + clazz.getName());
+ }
+
+ private Object invoke(Method method, Object instance, Object... args) throws Exception {
+ return ReflectionUtils.invoke(blueprintContainer.getAccessControlContext(), method, instance, args);
+ }
+
+ private Object newInstance(Constructor constructor, Object... args) throws Exception {
+ return ReflectionUtils.newInstance(blueprintContainer.getAccessControlContext(), constructor, args);
+ }
+
+ private static Object UNMATCHED = new Object();
+
+ private class ArgumentMatcher {
+
+ private final List<TypeEntry> entries;
+ private final boolean convert;
+
+ public ArgumentMatcher(Type[] types, boolean convert) {
+ entries = new ArrayList<TypeEntry>();
+ for (Type type : types) {
+ entries.add(new TypeEntry(new GenericType(type)));
+ }
+ this.convert = convert;
+ }
+
+ public List<Object> match(List<Object> arguments, List<ReifiedType> forcedTypes) {
+ if (find(arguments, forcedTypes)) {
+ return getArguments();
+ }
+ return null;
+ }
+
+ private List<Object> getArguments() {
+ List<Object> list = new ArrayList<Object>();
+ for (TypeEntry entry : entries) {
+ if (entry.argument == UNMATCHED) {
+ throw new RuntimeException("There are unmatched types");
+ } else {
+ list.add(entry.argument);
+ }
+ }
+ return list;
+ }
+
+ private boolean find(List<Object> arguments, List<ReifiedType> forcedTypes) {
+ if (entries.size() == arguments.size()) {
+ boolean matched = true;
+ for (int i = 0; i < arguments.size() && matched; i++) {
+ matched = find(arguments.get(i), forcedTypes.get(i));
+ }
+ return matched;
+ }
+ return false;
+ }
+
+ private boolean find(Object arg, ReifiedType forcedType) {
+ for (TypeEntry entry : entries) {
+ Object val = arg;
+ if (entry.argument != UNMATCHED) {
+ continue;
+ }
+ if (forcedType != null) {
+ if (!forcedType.equals(entry.type)) {
+ continue;
+ }
+ } else if (arg != null) {
+ if (convert) {
+ try {
+ // TODO: call canConvert instead of convert()
+ val = convert(arg, entry.type);
+ } catch (Throwable t) {
+ continue;
+ }
+ } else {
+ if (!AggregateConverter.isAssignable(arg, entry.type)) {
+ continue;
+ }
+ }
+ }
+ entry.argument = val;
+ return true;
+ }
+ return false;
+ }
+
+ }
+
+ private static class TypeEntry {
+
+ private final ReifiedType type;
+ private Object argument;
+
+ public TypeEntry(ReifiedType type) {
+ this.type = type;
+ this.argument = UNMATCHED;
+ }
+
+ }
+
+}