| /* |
| * Copyright (c) 2017 CEA. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * CEA - initial API and implementation |
| */ |
| package org.eclipse.sensinact.gateway.util; |
| |
| import org.json.JSONArray; |
| import org.json.JSONObject; |
| import org.osgi.framework.Bundle; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * Reflection helpers |
| */ |
| public abstract class ReflectUtils { |
| private static final Logger LOGGER = Logger.getLogger(ReflectUtils.class.getCanonicalName()); |
| |
| /** |
| * Returns {@link Method} contained by the {@link Collection} |
| * passed as parameter whose signature is the same than the one of |
| * the {@link Method} also passed as parameter. Returns null if |
| * the {@link Collection} does not contain such a {@link Method} |
| * |
| * @param method the {@link Method} whose signature has to be searched |
| * in the specified {@link Collection} |
| * @param methods the {@link Collection} of {@link Method} in which search the |
| * signature |
| * @return the {@link Method} contained by the {@link Collection} whose |
| * signature is the same than the one of the specified |
| * {@link Method} |
| */ |
| public static Method containsSignature(Method method, Collection<Method> methods) { |
| if (methods == null || methods.isEmpty()) { |
| return null; |
| } |
| Iterator<Method> iterator = methods.iterator(); |
| while (iterator.hasNext()) { |
| Method containedMethod = iterator.next(); |
| if (signatureEquals(method, containedMethod)) { |
| return containedMethod; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns true is the signatures of the two {@link Method}s |
| * passed as parameters are the same ; returns false |
| * otherwise |
| * |
| * @param first the first of the two {@link Method}s to compare |
| * the signatures of |
| * @param second the second of the two {@link Method}s to compare |
| * the signatures of |
| * @return true is the signatures of the two {@link Method}s |
| * are the same; <br/>false otherwise |
| */ |
| public static boolean signatureEquals(Method first, Method second) { |
| if (first == null || second == null) { |
| return false; |
| } |
| if (!first.getName().equals(second.getName())) { |
| return false; |
| } |
| Class<?>[] firstParameterTypes = first.getParameterTypes(); |
| Class<?>[] secondParameterTypes = second.getParameterTypes(); |
| |
| if (firstParameterTypes.length != secondParameterTypes.length) { |
| return false; |
| } |
| int index = 0; |
| for (; (index < firstParameterTypes.length) && (firstParameterTypes[index] == secondParameterTypes[index]); index++) |
| ; |
| return (index == firstParameterTypes.length); |
| } |
| |
| /** |
| * Returns as a list the hierarchy of interfaces implemented by the |
| * bottomClass class argument which will be included as the bottom |
| * of the hierarchy if it is an interface. |
| * |
| * @param bottomClass the class from which retrieve the list of implemented |
| * interfaces |
| * @return an ordered list of all interfaces the bottomClass implements |
| */ |
| public static LinkedList<Class<?>> getOrderedImplementedInterfaces(Class<?> bottomClass) { |
| LinkedList<Class<?>> list = new LinkedList<Class<?>>(); |
| |
| if (bottomClass == null) { |
| return list; |
| } |
| if (bottomClass.isInterface()) { |
| list.offer(bottomClass); |
| |
| } else { |
| list = getOrderedImplementedInterfaces(bottomClass.getSuperclass()); |
| } |
| Class<?>[] interfaces = bottomClass.getInterfaces(); |
| if (interfaces != null) { |
| int index = 0; |
| for (; index < interfaces.length; index++) { |
| LinkedList<Class<?>> inheritedList = getOrderedImplementedInterfaces(interfaces[index]); |
| while (!inheritedList.isEmpty()) { |
| Class<?> inheritedClass = inheritedList.removeFirst(); |
| if (!list.contains(inheritedClass)) { |
| list.offer(inheritedClass); |
| } |
| } |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Returns the hierarchy of interfaces extending the referenceInterface class |
| * argument (which will be included as the top of the hierarchy)as an ordered |
| * list, beginning from the specified bottomClass class argument which will be |
| * included as the bottom of the hierarchy if it is an interface. |
| * |
| * @param referenceInterface the interface for which to retrieve all extending interface |
| * implemented by the bottomClass class argument |
| * @param bottomClass the class from which retrieve the list of implemented |
| * extended referenceInterface interfaces |
| * @return an ordered list of all extended referenceInterface interfaces |
| * the bottomClass implements |
| */ |
| public static <C> LinkedList<Class<C>> getOrderedImplementedInterfaces(Class<C> referenceInterface, Class<?> bottomClass) { |
| LinkedList<Class<C>> list = new LinkedList<Class<C>>(); |
| |
| if (referenceInterface == null || !referenceInterface.isInterface() || bottomClass == null || !referenceInterface.isAssignableFrom(bottomClass)) { |
| return list; |
| } |
| if (bottomClass.isInterface()) { |
| list.offer((Class<C>) bottomClass); |
| |
| } else { |
| list = getOrderedImplementedInterfaces(referenceInterface, bottomClass.getSuperclass()); |
| } |
| Class<?>[] interfaces = bottomClass.getInterfaces(); |
| if (interfaces != null) { |
| int index = 0; |
| for (; index < interfaces.length; index++) { |
| if (referenceInterface.isAssignableFrom(interfaces[index])) { |
| LinkedList<Class<C>> inheritedList = getOrderedImplementedInterfaces(referenceInterface, interfaces[index]); |
| while (!inheritedList.isEmpty()) { |
| Class<?> inheritedClass = inheritedList.removeFirst(); |
| if (!list.contains(inheritedClass)) { |
| list.offer((Class<C>) inheritedClass); |
| } |
| } |
| } |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Searches and returns the value of the constant whose name |
| * is passed as parameter in an interface hierarchy specified |
| * as an ordered list of classes. If the fromTop argument is |
| * set to true the constant value is searched from the top |
| * of the hierarchy to the bottom; otherwise it is searched |
| * from the bottom to the top of the hierarchy |
| * |
| * @param list an interface hierarchy specified as an ordered |
| * list of classes in which to search for the constant |
| * value |
| * @param constantName the name of the constant to retrieve the value of |
| * @param fromTop <ul> |
| * <li>true if the constant value must be searched |
| * from the top to the bottom of the hierarchy</li> |
| * <li>false if the constant value must be searched |
| * from the bottom to the top of the hierarchy</li> |
| * </ul> |
| * @return the value of the constant or null if the value cannot |
| * be found |
| */ |
| public static <C, T> T getConstantValue(Deque<Class<C>> list, String constantName, boolean fromTop) { |
| T constantValue = null; |
| Iterator<Class<C>> iterator = fromTop ? list.descendingIterator() : list.iterator(); |
| while (iterator.hasNext()) { |
| Class<C> resourceInterface = iterator.next(); |
| constantValue = ReflectUtils.<C, T>getConstantValue(resourceInterface, constantName); |
| if (constantValue != null) { |
| break; |
| } |
| } |
| return constantValue; |
| } |
| |
| /** |
| * Searches and returns the value of the constant whose name |
| * is passed as parameter in the Class also passed as parameter |
| * |
| * @param clazz Class where to search for the constant value |
| * @param constantName the name of the constant to retrieve the value of |
| * @return the value of the constant or null if the value cannot |
| * be found |
| */ |
| @SuppressWarnings({"unchecked"}) |
| public static <C, T> T getConstantValue(Class<C> clazz, String constantName) { |
| T constantValue = null; |
| try { |
| constantValue = (T) clazz.getField(constantName).get(null); |
| } catch (Exception e) { |
| //do nothing; |
| } |
| return constantValue; |
| } |
| |
| /** |
| * Returns the map of {@link Field}s of the {@link Class} passed |
| * as parameter mapped to {@link Annotation} instances whose type is |
| * the same as the annotationClass argument. |
| * |
| * @param annotated the {@link Class} in which to search properly annotated |
| * fields |
| * @param annotationClass the expected {@link Annotation} type |
| * @return the map of fields of the specified class mapped to |
| * associated annotations |
| */ |
| public static <A extends Annotation> Map<Field, A> getAnnotatedFields(Class<?> annotated, Class<A> annotationClass) { |
| if (annotated == null || annotationClass == null) { |
| return Collections.<Field, A>emptyMap(); |
| } |
| Map<Field, A> annotatedFields = new HashMap<Field, A>(); |
| |
| Field[] fields = annotated.getDeclaredFields(); |
| A annotation = null; |
| |
| int index = 0; |
| int length = fields == null ? 0 : fields.length; |
| for (; index < length; index++) { |
| if ((annotation = fields[index].getAnnotation(annotationClass)) != null) { |
| annotatedFields.put(fields[index], annotation); |
| annotation = null; |
| } |
| } |
| return annotatedFields; |
| } |
| |
| /** |
| * Returns the map of {@link Method}s of the {@link Class} passed |
| * as parameter mapped to {@link Annotation} instances whose type is |
| * the same as the annotationClass argument. If the method is not |
| * annotated but overwrites an annotated one, it is mapped to the |
| * annotation instance of its overwritten counterpart. Synthetic |
| * and Bridge methods are excluded from the research. |
| * |
| * @param annotated the {@link Class} in which to search properly annotated |
| * methods |
| * @param annotationClass the expected {@link Annotation} type |
| * @return the map of methods of the specified class mapped to |
| * associated annotations |
| */ |
| public static <A extends Annotation> Map<Method, A> getAnnotatedMethods(Class<?> annotated, Class<A> annotationClass) { |
| if (annotated == null || annotationClass == null) { |
| return Collections.<Method, A>emptyMap(); |
| } |
| Map<Method, A> annotatedMethods = new HashMap<Method, A>(); |
| ReflectUtils.getAnnotatedMethods(annotated, annotationClass, annotatedMethods); |
| return annotatedMethods; |
| } |
| |
| /** |
| * Feeds the map passed as parameter with {@link Method}s of the |
| * annotated {@link Class} argument mapped to annotationClass |
| * {@link Annotation} instances annotating them. If a method is not |
| * annotated but overwrites/implements an annotated one, it is mapped |
| * to the annotation instance of its overwritten/implemented |
| * super-class or interface method counterpart. Synthetic and Bridge |
| * methods are excluded from the research. |
| * |
| * @param annotated the {@link Class} in which to search properly annotated |
| * methods |
| * @param annotationClass the expected {@link Annotation} type |
| * @param map the map of methods of the specified class mapped to |
| * annotations of the specified type annotating them |
| */ |
| private static final <A extends Annotation> void getAnnotatedMethods(Class<?> annotated, Class<A> annotationClass, Map<Method, A> map) { |
| if (annotated == null) { |
| return; |
| } |
| Method[] methods = annotated.getDeclaredMethods(); |
| Set<Method> methodsSet = map.keySet(); |
| |
| int index = 0; |
| A annotation = null; |
| for (; index < methods.length; index++) { |
| if (methods[index].isSynthetic() || methods[index].isBridge() || containsSignature(methods[index], methodsSet) != null) { |
| continue; |
| } |
| if ((annotation = ReflectUtils.getAnnotation(methods[index], annotationClass)) != null) { |
| map.put(methods[index], annotation); |
| } |
| } |
| getAnnotatedMethods(annotated.getSuperclass(), annotationClass, map); |
| } |
| |
| |
| /** |
| * Returns the {@link Annotation} instance of the type passed as |
| * parameter, annotating the {@link Method} also passed as |
| * parameter or its overwritten/implemented counterpart from |
| * a super-class or an interface |
| * |
| * @param method the method where to search for the {@link Annotation} |
| * @param annotationClass the type of the {@link Annotation} to search for |
| * @return the {@link Annotation} instance annotating the {@link |
| * Method} or its overwritten/implemented counterpart from |
| * a super-class or an interface |
| */ |
| public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationClass) { |
| A annotation = retrieveInheritedAnnotation(method, annotationClass); |
| |
| if (annotation == null) { |
| LinkedList<Class<?>> implementedInterfaces = ReflectUtils.getOrderedImplementedInterfaces(method.getDeclaringClass()); |
| |
| Iterator<Class<?>> iterator = implementedInterfaces.iterator(); |
| while (iterator.hasNext()) { |
| Method implementedMethod = ReflectUtils.containsSignature(method, Arrays.<Method>asList(iterator.next().getDeclaredMethods())); |
| |
| if (implementedMethod != null && (annotation = implementedMethod.getAnnotation(annotationClass)) != null) { |
| break; |
| } |
| } |
| } |
| return annotation; |
| } |
| |
| /** |
| * Returns the {@link Annotation} instance of the type passed as |
| * parameter, annotating the {@link Method} also passed as |
| * parameter or its overwritten counterpart from a super-class |
| * |
| * @param method the method where to search for the {@link Annotation} |
| * @param annotationClass the type of the {@link Annotation} to search the instance |
| * of |
| * @return the {@link Annotation} instance annotating the {@link |
| * Method} or its overwritten counterpart from a super-class |
| */ |
| private static final <A extends Annotation> A retrieveInheritedAnnotation(Method method, Class<A> annotationClass) { |
| if (method == null || annotationClass == null) { |
| return null; |
| } |
| A annotation = method.getAnnotation(annotationClass); |
| if (annotation == null) { |
| annotation = retrieveInheritedAnnotation(overwritten(method), annotationClass); |
| } |
| return annotation; |
| } |
| |
| /** |
| * Returns true if the {@link Method} passed as |
| * parameter is valid according to the other |
| * specified parameter : |
| * <ul> |
| * <li>the same returned type or void if the |
| * acceptVoid argument is set to true</li> |
| * <li>the same name if the strict argument |
| * is set to true</li> |
| * <li>the same parameter types</li> |
| * <lu> |
| * |
| * @param method the method to validate |
| * @param returnedType the returned type |
| * @param methodName the method name |
| * @param parameterTypes the parameter classes array |
| * @param acceptVoid defines if the returned type as to |
| * be the same as the specified one or if |
| * a Void returned type is allowed |
| * @param strict defines if the method's name as to be |
| * the same as the specified one |
| * @return true if the method is valid; returns |
| * false otherwise |
| */ |
| public static boolean validMethod(Method method, Class<?> returnedType, String methodName, Class<?>[] parameterTypes, boolean acceptVoid, boolean strict) { |
| if (method == null) { |
| return false; |
| } |
| Class<?>[] methodParameterTypes = method.getParameterTypes(); |
| int parametersLength = parameterTypes == null ? 0 : parameterTypes.length; |
| |
| if ((parametersLength != methodParameterTypes.length) || (strict && (methodName == null || !methodName.equals(method.getName()))) || ((returnedType == null || !returnedType.isAssignableFrom(method.getReturnType())) && (!acceptVoid || !method.getReturnType().equals(Void.TYPE)))) { |
| return false; |
| } |
| int parameterIndex = 0; |
| for (; parameterIndex < parametersLength && (methodParameterTypes[parameterIndex].isAssignableFrom(parameterTypes[parameterIndex])); parameterIndex++) |
| ; |
| |
| if (parameterIndex != parametersLength) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns the first {@link Method} validating the |
| * specified parameters in the array of the ones |
| * passed as parameter |
| * |
| * @param methods the array of {@link Method} |
| * @param returnedType the returned type |
| * @param parameterTypes the parameter classes array |
| * @param acceptVoid defines if the returned type as to |
| * be the same as the specified one or if |
| * a Void returned type is allowed |
| * @return the first method validing the parameters |
| */ |
| public static Method getDeclaredMethod(Method[] methods, Class<?> returnedType, Class<?>[] parameterTypes, boolean acceptVoid) { |
| int index = 0; |
| int length = methods == null ? 0 : methods.length; |
| |
| Method method = null; |
| |
| for (; index < length; index++) { |
| if (validMethod(methods[index], returnedType, null, parameterTypes, acceptVoid, false)) { |
| method = methods[index]; |
| break; |
| } |
| } |
| return method; |
| } |
| |
| /** |
| * Returns the first {@link Method} validating the |
| * specified parameters in the array of the ones |
| * passed as parameter |
| * |
| * @param methods the array of {@link Method} |
| * @param returnedType the returned type |
| * @param methodName the method's name |
| * @param parameterTypes the parameter classes array |
| * @param acceptVoid defines if the returned type as to |
| * be the same as the specified one or if |
| * a Void returned type is allowed |
| * @return the first method validing the parameters |
| */ |
| public static Method getDeclaredMethod(Method[] methods, Class<?> returnedType, String methodName, Class<?>[] parameterTypes, boolean acceptVoid) { |
| int index = 0; |
| int length = methods == null ? 0 : methods.length; |
| |
| Method method = null; |
| |
| for (; index < length; index++) { |
| if (validMethod(methods[index], returnedType, methodName, parameterTypes, acceptVoid, methodName != null)) { |
| method = methods[index]; |
| break; |
| } |
| } |
| return method; |
| } |
| |
| /** |
| * Returns the first {@link Method} validating the |
| * specified parameters in the set of the ones declared |
| * for the type passed as parameter |
| * |
| * @param declaringClass the type in which to search the |
| * valid {@link Method} |
| * @param returnedType the returned type |
| * @param methodName the method's name |
| * @param parameterTypes the parameter classes array |
| * @param acceptVoid defines if the returned type as to |
| * be the same as the specified one or if |
| * a Void returned type is allowed |
| * @return the first method validing the parameters |
| */ |
| public static Method getDeclaredMethod(Class<?> declaringClass, Class<?> returnedType, String methodName, Class<?>[] parameterTypes, boolean acceptVoid) { |
| if (declaringClass == null || methodName == null || (returnedType == null && acceptVoid == false)) { |
| return null; |
| } |
| if (returnedType == null) { |
| returnedType = Void.class; |
| } |
| Method method = getDeclaredMethod(declaringClass.getMethods(), returnedType, methodName, parameterTypes, acceptVoid); |
| |
| if (method == null) { |
| method = getDeclaredMethod(declaringClass.getDeclaredMethods(), returnedType, methodName, parameterTypes, acceptVoid); |
| } |
| return method; |
| } |
| |
| |
| /** |
| * Returns the {@link Method} instance the one passed |
| * as parameter overwrites if it exists; otherwise |
| * returns null |
| * |
| * @param method the method or which to retrieve the overwritten |
| * one |
| * @return the overwritten method or null if the specified |
| * method does not overwrite a super class one |
| */ |
| public static Method overwritten(Method method) { |
| Method overwritten = null; |
| if (method != null) { |
| String methodName = method.getName(); |
| Class<?>[] parameterTypes = method.getParameterTypes(); |
| Class<?> declaringClass = method.getDeclaringClass(); |
| |
| while (overwritten == null && declaringClass != null) { |
| try { |
| declaringClass = declaringClass.getSuperclass(); |
| overwritten = declaringClass.getDeclaredMethod(methodName, parameterTypes); |
| |
| } catch (Exception e) { |
| continue; |
| } |
| } |
| } |
| return overwritten; |
| } |
| |
| /** |
| * Returns a new instance of the implementation class passed |
| * as parameter using the parameters array as argument |
| * |
| * @param baseClass the class which the implementation one |
| * as to extend |
| * @param implementationClass the class to instantiate |
| * @param parameters the objects array parameters to use |
| * @return a new instance of the implementation class |
| */ |
| public static <E, F> F getInstance(Class<E> baseClass, Class<F> implementationClass, Object... parameters) { |
| if (baseClass == implementationClass) { |
| return ReflectUtils.<F>getInstance(implementationClass, parameters); |
| } |
| F instance = null; |
| |
| try { |
| if (implementationClass != null && baseClass != null && baseClass.isAssignableFrom(implementationClass)) { |
| if (parameters == null || parameters.length == 0) { |
| instance = implementationClass.newInstance(); |
| |
| } else { |
| @SuppressWarnings("unchecked") Constructor<F>[] constructors = (Constructor<F>[]) implementationClass.getDeclaredConstructors(); |
| |
| for (Constructor<F> constructor : constructors) { |
| Class<?>[] parameterTypes = null; |
| |
| if ((parameterTypes = constructor.getParameterTypes()).length == parameters.length) { |
| int index = 0; |
| |
| for (; index < parameterTypes.length && (parameters[index] == null || parameterTypes[index].isAssignableFrom(parameters[index].getClass())); index++) |
| ; |
| |
| if (index == parameterTypes.length) { |
| constructor.setAccessible(true); |
| instance = constructor.newInstance(parameters); |
| break; |
| } |
| } |
| } |
| } |
| } else { |
| LOGGER.log(Level.CONFIG, baseClass.getName() + " is not assignable from " + implementationClass.getName()); |
| } |
| } catch (Exception e) { |
| LOGGER.log(Level.CONFIG, e.getMessage(), e); |
| } |
| return instance; |
| } |
| |
| /** |
| * Returns a new instance of the implementation class passed |
| * as parameter using the parameters array as argument |
| * |
| * @param implementationClass the class to instantiate |
| * @return a new instance of the implementation class |
| */ |
| public static <F> F getInstance(Class<F> implementationClass, Object[] parameters) { |
| F instance = null; |
| try { |
| @SuppressWarnings("unchecked") Constructor<F>[] constructors = (Constructor<F>[]) implementationClass.getDeclaredConstructors(); |
| |
| for (Constructor<F> constructor : constructors) { |
| Class<?>[] parameterTypes = null; |
| |
| if ((parameterTypes = constructor.getParameterTypes()).length == parameters.length) { |
| int index = 0; |
| |
| for (; index < parameterTypes.length && (parameters[index] == null || parameterTypes[index].isAssignableFrom(parameters[index].getClass())); index++) |
| ; |
| |
| if (index == parameterTypes.length) { |
| constructor.setAccessible(true); |
| instance = constructor.newInstance(parameters); |
| break; |
| } |
| } |
| } |
| if (instance == null) { |
| instance = implementationClass.newInstance(); |
| } |
| } catch (Exception e) { |
| LOGGER.log(Level.CONFIG, e.getMessage(), e); |
| } |
| return instance; |
| } |
| |
| /** |
| * Returns a new instance of the implementation class passed |
| * as parameter using the parameters array as argument |
| * |
| * @param implementationClass the class to instantiate |
| * @return a new instance of the implementation class |
| */ |
| public static <F> F getTheBestInstance(Class<F> clazz, Object[] parameters) { |
| F instance = null; |
| Constructor[] constructors = clazz.getConstructors(); |
| int index = constructors.length - 1; |
| |
| //order constructors by decreasing arguments number |
| //to use the maximum number of passed parameters |
| for (; index >= 0; index--) { |
| int pos = index; |
| Constructor current = constructors[index]; |
| while (pos > 0) { |
| Constructor previous = constructors[pos - 1]; |
| if (previous.getParameterTypes().length < current.getParameterTypes().length) { |
| constructors[pos] = previous; |
| constructors[pos - 1] = current; |
| pos--; |
| continue; |
| } |
| break; |
| } |
| } |
| index = 0; |
| int position = 0; |
| for (; index < constructors.length; index++) { |
| Class<?>[] parameterTypes = constructors[index].getParameterTypes(); |
| if (parameterTypes.length > parameters.length) { |
| continue; |
| } |
| Object[] params = new Object[parameterTypes.length]; |
| |
| int paramsIndex = 0; |
| int parametersIndex = 0; |
| int parametersLength = parameters == null ? 0 : parameters.length; |
| |
| for (; parametersIndex < parametersLength && paramsIndex < params.length; parametersIndex++) { |
| Object parameter = parameters[parametersIndex]; |
| if (parameter != null && parameterTypes[paramsIndex].isAssignableFrom(parameter.getClass())) { |
| params[paramsIndex++] = parameter; |
| } |
| } |
| if (paramsIndex == parameterTypes.length) { |
| try { |
| instance = (F) constructors[index].newInstance(params); |
| break; |
| } catch (Exception e) { |
| continue; |
| } |
| } |
| } |
| return instance; |
| } |
| |
| /** |
| * Instantiate the Java object described |
| * by the JSON array passed as parameter |
| * |
| * @param clazz the Java type to instantiate |
| * @param jsonObject the JSON array describing the java object |
| * to instantiate |
| * @return the Java object |
| */ |
| public static <E extends Object, T> T instantiate(ClassLoader classloader, Class<T> clazz, JSONArray jsonObject) { |
| T instance = null; |
| |
| if (jsonObject == null) { |
| return instance; |
| } |
| Constructor<T> constructor = null; |
| try { |
| constructor = clazz.getConstructor(JSONArray.class); |
| instance = constructor.newInstance(jsonObject); |
| |
| } catch (Exception e) { |
| try { |
| constructor = clazz.getConstructor(new Class[]{ClassLoader.class, JSONArray.class}); |
| instance = constructor.newInstance(new Object[]{classloader, jsonObject}); |
| |
| } catch (Exception ex) { |
| LOGGER.log(Level.CONFIG, e.getMessage(), e); |
| } |
| } |
| if (instance != null) { |
| return instance; |
| } |
| Object[][] parameters = null; |
| int length = jsonObject.length(); |
| parameters = new Object[length][2]; |
| for (int i = 0; i < length; i++) { |
| parameters[i][0] = null; |
| parameters[i][1] = jsonObject.get(i); |
| } |
| return newInstance(classloader, clazz, parameters); |
| } |
| |
| /** |
| * Instantiate the Java object described by the JSON object |
| * (JSONObject or JSONArray ) passed as parameter |
| * |
| * @param clazz the Java type to instantiate |
| * @param jsonObject the JSON object describing the java one to instantiate |
| * @return the Java object |
| */ |
| public static <E extends Object, T> T instantiate(ClassLoader classloader, Class<T> clazz, JSONObject jsonObject) { |
| T instance = null; |
| if (jsonObject == null) { |
| return instance; |
| } |
| Constructor<T> constructor = null; |
| try { |
| constructor = clazz.getConstructor(JSONObject.class); |
| instance = constructor.newInstance(jsonObject); |
| } catch (Exception e) { |
| try { |
| constructor = clazz.getConstructor(new Class[]{ClassLoader.class, JSONObject.class}); |
| instance = constructor.newInstance(new Object[]{classloader, jsonObject}); |
| |
| } catch (Exception ex) { |
| LOGGER.log(Level.CONFIG, e.getMessage(), e); |
| } |
| } |
| if (instance != null) { |
| return instance; |
| } |
| Object[][] parameters = null; |
| String[] names = JSONObject.getNames(jsonObject); |
| int length = names.length; |
| parameters = new Object[length][2]; |
| for (int i = 0; i < length; i++) { |
| parameters[i][0] = names[i]; |
| parameters[i][1] = (jsonObject).get(names[i]); |
| } |
| return newInstance(classloader, clazz, parameters); |
| } |
| |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| private static final <E extends Object, T> T newInstance(ClassLoader classloader, Class<T> clazz, Object[][] parameters) { |
| T instance = null; |
| |
| //orders public constructors according to their |
| //number of parameters |
| Constructor[] constructors = clazz.getConstructors(); |
| int index = constructors.length - 1; |
| for (; index >= 0; index--) { |
| int pos = index; |
| Constructor current = constructors[index]; |
| |
| while (pos > 0) { |
| Constructor previous = constructors[pos - 1]; |
| if (previous.getParameterTypes().length < current.getParameterTypes().length) { |
| constructors[pos] = previous; |
| constructors[pos - 1] = current; |
| pos--; |
| continue; |
| } |
| break; |
| } |
| } |
| index = constructors.length - 1; |
| int position = 0; |
| for (; index >= 0; index--) { |
| if (constructors[index].getParameterTypes().length > parameters.length) { |
| continue; |
| } |
| Class<?>[] parameterTypes = constructors[index].getParameterTypes(); |
| Object[] params = new Object[parameterTypes.length]; |
| int typeIndex = 0; |
| for (; typeIndex < parameterTypes.length; typeIndex++) { |
| Object parameter = CastUtils.getObjectFromJSON(classloader, parameterTypes[typeIndex], parameters[typeIndex][1]); |
| |
| if (parameter == null && !JSONObject.NULL.equals(parameters[typeIndex][1])) { |
| params = null; |
| break; |
| } |
| params[typeIndex] = parameter; |
| } |
| if (params != null) { |
| try { |
| instance = (T) constructors[index].newInstance(params); |
| position = (typeIndex + 1); |
| break; |
| |
| } catch (Exception e) { |
| continue; |
| } |
| } |
| } |
| if (instance != null && position < parameters.length && parameters[position][0] != null) { |
| for (; position < parameters.length; position++) { |
| try { |
| Field field = clazz.getDeclaredField((String) parameters[position][0]); |
| Object value = parameters[position][1]; |
| |
| if (value == null || (field.getModifiers() & Modifier.FINAL) == Modifier.FINAL || (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC) { |
| continue; |
| } |
| field.setAccessible(true); |
| if (List.class.isAssignableFrom(field.getType())) { |
| field.set(instance, CastUtils.toList(classloader, (Class<List<E>>) field.getType(), (Class<E>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0], (JSONArray) value)); |
| |
| } else if (Map.class.isAssignableFrom(field.getType())) { |
| field.set(instance, CastUtils.toMap(classloader, (Class<Map<String, E>>) field.getType(), (Class<E>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], (JSONObject) value)); |
| } else { |
| field.set(instance, CastUtils.getObjectFromJSON(classloader, field.getType(), value)); |
| } |
| } catch (Exception exception) { |
| LOGGER.log(Level.CONFIG, exception.getMessage(), exception); |
| continue; |
| } |
| } |
| } |
| return instance; |
| } |
| |
| /** |
| * Returns the array of types from the {@link Bundle} passed as |
| * parameter for which the one also passed as parameter is |
| * assignable to |
| * |
| * @param type the type to find the ones that it is assignable to |
| * @param bundle the {@link Bundle} to search for the types in |
| * @return the array of types from the specified {@link Bundle} |
| * assignable to specified one |
| */ |
| public static List<Class<?>> getAssignableTypes(Class<?> type, Bundle bundle) { |
| return ReflectUtils.getAssignableTypes(type, ReflectUtils.getAllTypes(bundle)); |
| } |
| |
| /** |
| * Returns the array of types from the List of Class passed as |
| * parameter for which the type also passed as parameter is |
| * assignable to |
| * |
| * @param type the type to find the ones that it is assignable to |
| * @param classes the List of Class to search in |
| * @return the array of types from the specified List of Class |
| * assignable to specified one |
| */ |
| public static List<Class<?>> getAssignableTypes(Class<?> type, List<Class<?>> classes) { |
| List<Class<?>> assignables = new ArrayList<Class<?>>(); |
| Iterator<Class<?>> iterator = classes.iterator(); |
| while (iterator.hasNext()) { |
| Class<?> clazz = iterator.next(); |
| if (type.isAssignableFrom(clazz)) { |
| assignables.add(clazz); |
| } |
| } |
| return assignables; |
| } |
| |
| /** |
| * Returns the array of existing types from the Bundle |
| * passed as parameter |
| * |
| * @param bundle the Bundle to search for the types in |
| * @return the array of types from the specified Bundle |
| */ |
| public static List<String> getAllStringTypes(Bundle bundle) { |
| List<String> types = new ArrayList<String>(); |
| |
| Enumeration<URL> entries = bundle.findEntries("/", "*.class", true); |
| |
| if (entries != null) { |
| while (entries.hasMoreElements()) { |
| String classname = entries.nextElement().getPath(); |
| int startIndex = 0; |
| int endIndex = classname.length() - ".class".length(); |
| startIndex += classname.startsWith("/") ? 1 : 0; |
| |
| classname = classname.substring(startIndex, endIndex); |
| classname = classname.replace('/', '.'); |
| types.add(classname); |
| } |
| } |
| return types; |
| } |
| |
| /** |
| * Returns the array of existing types from the Bundle |
| * passed as parameter |
| * |
| * @param bundle the Bundle to search for the types in |
| * @return the array of types from the specified Bundle |
| */ |
| public static List<Class<?>> getAllTypes(Bundle bundle) { |
| List<String> strTypes = ReflectUtils.getAllStringTypes(bundle); |
| |
| List<Class<?>> types = new ArrayList<Class<?>>(); |
| if (strTypes == null || strTypes.size() == 0) { |
| return types; |
| } |
| Iterator<String> iterator = strTypes.iterator(); |
| while (iterator.hasNext()) { |
| try { |
| types.add(bundle.loadClass(iterator.next())); |
| |
| } catch (ClassNotFoundException ex) { |
| continue; |
| } |
| } |
| return types; |
| } |
| |
| /** |
| * Get the underlying class for a type, or null if the type is |
| * a variable type. |
| * |
| * @param type the type |
| * @return the underlying class |
| */ |
| public static Class<?> getClass(Type type) { |
| if (type instanceof Class) { |
| return (Class<?>) type; |
| } else if (type instanceof ParameterizedType) { |
| return getClass(((ParameterizedType) type).getRawType()); |
| } else if (type instanceof GenericArrayType) { |
| Type componentType = ((GenericArrayType) type).getGenericComponentType(); |
| Class<?> componentClass = getClass(componentType); |
| if (componentClass != null) { |
| return Array.newInstance(componentClass, 0).getClass(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the actual type arguments a child class has used to |
| * extend a generic base class. |
| * |
| * @param baseClass the base class |
| * @param childClass the child class |
| * @return a list of the raw classes for the actual type arguments. |
| */ |
| public static <T> List<Class<?>> getTypeArguments(Class<T> baseClass, Class<? extends T> childClass) { |
| Map<Type, Type> resolvedTypes = new HashMap<Type, Type>(); |
| Type type = childClass; |
| // start walking up the inheritance hierarchy until we hit baseClass |
| while (!getClass(type).equals(baseClass)) { |
| if (type instanceof Class) { |
| // there is no useful information for us in raw types, so just keep going. |
| type = ((Class<?>) type).getGenericSuperclass(); |
| } else { |
| ParameterizedType parameterizedType = (ParameterizedType) type; |
| Class<?> rawType = (Class<?>) parameterizedType.getRawType(); |
| |
| Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); |
| TypeVariable<?>[] typeParameters = rawType.getTypeParameters(); |
| for (int i = 0; i < actualTypeArguments.length; i++) { |
| resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); |
| } |
| |
| if (!rawType.equals(baseClass)) { |
| type = rawType.getGenericSuperclass(); |
| } |
| } |
| } |
| |
| // finally, for each actual type argument provided |
| //to baseClass, determine (if possible) |
| // the raw class for that type argument. |
| Type[] actualTypeArguments; |
| if (type instanceof Class) { |
| actualTypeArguments = ((Class<?>) type).getTypeParameters(); |
| } else { |
| actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); |
| } |
| List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>(); |
| // resolve types by chasing down type variables. |
| for (Type baseType : actualTypeArguments) { |
| while (resolvedTypes.containsKey(baseType)) { |
| baseType = resolvedTypes.get(baseType); |
| } |
| typeArgumentsAsClasses.add(getClass(baseType)); |
| } |
| return typeArgumentsAsClasses; |
| } |
| |
| } |