org.glassfish.hk2.utilities.reflection.ReflectionHelper Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.hk2.utilities.reflection;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
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.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Scope;
import org.glassfish.hk2.utilities.reflection.internal.MethodWrapperImpl;
/**
* @author jwells
*
*/
public final class ReflectionHelper {
private final static HashSet ESCAPE_CHARACTERS = new HashSet();
private final static char ILLEGAL_CHARACTERS[] = {
'{' , '}', '[', ']', ':', ';', '=', ',', '\\'
};
private final static HashMap REPLACE_CHARACTERS = new HashMap();
static {
for (char illegal : ILLEGAL_CHARACTERS) {
ESCAPE_CHARACTERS.add(illegal);
}
REPLACE_CHARACTERS.put('\n', 'n');
REPLACE_CHARACTERS.put('\r', 'r');
}
private final static String EQUALS_STRING = "=";
private final static String COMMA_STRING = ",";
private final static String QUOTE_STRING = "\"";
/**
* Given the type parameter gets the raw type represented
* by the type, or null if this has no associated raw class
* @param type The type to find the raw class on
* @return The raw class associated with this type
*/
public static Class> getRawClass(Type type) {
if (type == null) return null;
if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
if (!(componentType instanceof ParameterizedType)) {
// type variable is not supported
return null;
}
Class> rawComponentClass = getRawClass(componentType);
String forNameName = "[L" + rawComponentClass.getName() + ";";
try {
return Class.forName(forNameName);
}
catch (Throwable th) {
// ignore, but return null
return null;
}
}
if (type instanceof Class) {
return (Class>) type;
}
if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class) {
return (Class>) rawType;
}
}
return null;
}
private static String getNamedName(Named named, Class> implClass) {
String name = named.value();
if (name != null && !name.equals("")) return name;
String cn = implClass.getName();
int index = cn.lastIndexOf(".");
if (index < 0) return cn;
return cn.substring(index + 1);
}
/**
* Returns the name that should be associated with this class
*
* @param implClass The class to evaluate
* @return The name this class should have
*/
public static String getName(Class> implClass) {
Named named = implClass.getAnnotation(Named.class);
String namedName = (named != null) ? getNamedName(named, implClass) : null ;
if (namedName != null) return namedName;
return null;
}
/**
* Gets all the interfaces on this particular class (but not any
* superclasses of this class).
*/
private static void addAllGenericInterfaces(Class> rawClass ,
Type type,
Set closures) {
Map typeArgumentsMap = null;
for (Type currentType : rawClass.getGenericInterfaces()) {
if (type instanceof ParameterizedType &&
currentType instanceof ParameterizedType) {
if (typeArgumentsMap == null ) {
typeArgumentsMap = getTypeArguments(rawClass, (ParameterizedType) type);
}
currentType = fixTypeVariables((ParameterizedType) currentType, typeArgumentsMap);
}
closures.add(currentType);
rawClass = ReflectionHelper.getRawClass(currentType);
if (rawClass != null) {
addAllGenericInterfaces(rawClass, currentType, closures);
}
}
}
/**
* Replace any TypeVariables in the given type's arguments with
* the actual argument types. Return the given type if no replacing
* is required.
*/
private static Type fixTypeVariables(ParameterizedType type,
Map typeArgumentsMap) {
Type[] newTypeArguments = getNewTypeArguments(type, typeArgumentsMap);
if (newTypeArguments != null) {
type = new ParameterizedTypeImpl(type.getRawType(), newTypeArguments);
}
return type;
}
/**
* Get a new array of type arguments for the given ParameterizedType, replacing any TypeVariables with
* actual types. The types should be found in the given arguments map, keyed by variable name. Return
* null if no arguments needed to be replaced.
*/
private static Type[] getNewTypeArguments(final ParameterizedType type,
final Map typeArgumentsMap) {
Type[] typeArguments = type.getActualTypeArguments();
Type[] newTypeArguments = new Type[typeArguments.length];
boolean newArgsNeeded = false;
int i = 0;
for (Type argType : typeArguments) {
if (argType instanceof TypeVariable) {
newTypeArguments[i++] = typeArgumentsMap.get(((TypeVariable>) argType).getName());
newArgsNeeded = true;
} else {
newTypeArguments[i++] = argType;
}
}
return newArgsNeeded ? newTypeArguments : null;
}
/**
* Gets a mapping of type variable names of the raw class to type arguments of the
* parameterized type.
*/
private static Map getTypeArguments(Class> rawClass,
ParameterizedType type) {
Map typeMap = new HashMap();
Type[] typeArguments = type.getActualTypeArguments();
int i = 0;
for (TypeVariable> typeVariable : rawClass.getTypeParameters() ) {
typeMap.put(typeVariable.getName(), typeArguments[i++]);
}
return typeMap;
}
/**
* Returns the type closure of the given class
*
* @param ofType The full type closure of the given class
* with nothing omitted (normal case). May not be null
* @return The non-null (and never empty) set of classes
* that this class can be assigned to
*/
private static Set getTypeClosure(Type ofType) {
Set retVal = new HashSet();
Class> rawClass = ReflectionHelper.getRawClass(ofType);
if (rawClass != null) {
Map typeArgumentsMap = null;
Type currentType = ofType;
while (currentType != null && rawClass != null) {
retVal.add(currentType);
addAllGenericInterfaces(rawClass, currentType, retVal);
if (typeArgumentsMap == null && currentType instanceof ParameterizedType){
typeArgumentsMap = getTypeArguments(rawClass, (ParameterizedType) currentType);
}
currentType = rawClass.getGenericSuperclass();
if (currentType != null) {
rawClass = ReflectionHelper.getRawClass(currentType);
if (typeArgumentsMap != null && currentType instanceof ParameterizedType){
currentType = fixTypeVariables((ParameterizedType) currentType, typeArgumentsMap);
}
}
}
}
return retVal;
}
/**
* Returns the type closure, as restricted by the classes listed in the
* set of contracts implemented
*
* @param ofType The type to check
* @param contracts The contracts this type is allowed to handle
* @return The type closure restricted to the contracts
*/
public static Set getTypeClosure(Type ofType, Set contracts) {
Set closure = getTypeClosure(ofType);
HashSet retVal = new HashSet();
for (Type t : closure) {
Class> rawClass = ReflectionHelper.getRawClass(t);
if (rawClass == null) continue;
if (contracts.contains(rawClass.getName())) {
retVal.add(t);
}
}
return retVal;
}
/**
* Returns the set of types this class advertises
* @param type The outer type to analyze
* @param markerAnnotation The annotation to use to discover the advertised types
* @return The type itself and the contracts it implements
*/
public static Set getAdvertisedTypesFromClass(Type type, Class extends Annotation> markerAnnotation) {
Set retVal = new LinkedHashSet();
if (type == null) return retVal;
retVal.add(type);
Class> originalRawClass = getRawClass(type);
if (originalRawClass == null) return retVal;
Type genericSuperclass = originalRawClass.getGenericSuperclass();
while (genericSuperclass != null) {
Class> rawClass = getRawClass(genericSuperclass);
if (rawClass == null) break;
if (rawClass.isAnnotationPresent(markerAnnotation)) {
retVal.add(genericSuperclass);
}
genericSuperclass = rawClass.getGenericSuperclass();
}
Set> alreadyHandled = new HashSet>();
while (originalRawClass != null) {
getAllContractsFromInterfaces(originalRawClass,
markerAnnotation,
retVal,
alreadyHandled);
originalRawClass = originalRawClass.getSuperclass();
}
return retVal;
}
private static void getAllContractsFromInterfaces(Class> clazzOrInterface,
Class extends Annotation> markerAnnotation,
Set addToMe,
Set> alreadyHandled) {
Type interfacesAsType[] = clazzOrInterface.getGenericInterfaces();
for (Type interfaceAsType : interfacesAsType) {
Class> interfaceAsClass = getRawClass(interfaceAsType);
if (interfaceAsClass == null) continue;
if (alreadyHandled.contains(interfaceAsClass)) continue;
alreadyHandled.add(interfaceAsClass);
if (interfaceAsClass.isAnnotationPresent(markerAnnotation)) {
addToMe.add(interfaceAsType);
}
getAllContractsFromInterfaces(interfaceAsClass, markerAnnotation, addToMe, alreadyHandled);
}
}
/**
* Returns the set of types this class advertises
* @param t the object we are analyzing
* @param markerAnnotation The annotation to use to discover the advertised types
* @return The type itself and the contracts it implements
*/
public static Set getAdvertisedTypesFromObject(Object t, Class extends Annotation> markerAnnotation) {
if (t == null) return Collections.emptySet();
return getAdvertisedTypesFromClass(t.getClass(), markerAnnotation);
}
/**
* Returns the set of types this class advertises
* @param clazz the class we are analyzing
* @param markerAnnotation The annotation to use to discover annotated types
* @return The type itself and the contracts it implements
*/
public static Set getContractsFromClass(Class> clazz, Class extends Annotation> markerAnnotation) {
Set retVal = new LinkedHashSet();
if (clazz == null) return retVal;
retVal.add(clazz.getName());
Class> extendsClasses = clazz.getSuperclass();
while (extendsClasses != null) {
if (extendsClasses.isAnnotationPresent(markerAnnotation)) {
retVal.add(extendsClasses.getName());
}
extendsClasses = extendsClasses.getSuperclass();
}
while (clazz != null) {
Class> interfaces[] = clazz.getInterfaces();
for (Class> iFace : interfaces) {
if (iFace.isAnnotationPresent(markerAnnotation)) {
retVal.add(iFace.getName());
}
}
clazz = clazz.getSuperclass();
}
return retVal;
}
/**
* Gets the scope annotation from the object
* @param t The object to analyze
* @return The class of the scope annotation
*/
public static Annotation getScopeAnnotationFromObject(Object t) {
if (t == null) throw new IllegalArgumentException();
return getScopeAnnotationFromClass(t.getClass());
}
/**
* Gets the scope annotation from the object
* @param clazz The class to analyze
* @return The class of the scope annotation
*/
public static Annotation getScopeAnnotationFromClass(Class> clazz) {
if (clazz == null) throw new IllegalArgumentException();
for (Annotation annotation : clazz.getAnnotations()) {
Class extends Annotation> annoClass = annotation.annotationType();
if (annoClass.isAnnotationPresent(Scope.class)) {
return annotation;
}
}
return null;
}
/**
* Gets the scope annotation from the object
* @param t The object to analyze
* @param annoDefault The default that this should have if no scope could be found
* @return The class of the scope annotation
*/
public static Class extends Annotation> getScopeFromObject(Object t, Class extends Annotation> annoDefault) {
if (t == null) return annoDefault;
return getScopeFromClass(t.getClass(), annoDefault);
}
/**
* Gets the scope annotation from the object
* @param clazz The class to analyze
* @param annoDefault The scope that should be returned if no scope could be found
* @return The class of the scope annotation
*/
public static Class extends Annotation> getScopeFromClass(Class> clazz, Class extends Annotation> annoDefault) {
if (clazz == null) return annoDefault;
for (Annotation annotation : clazz.getAnnotations()) {
Class extends Annotation> annoClass = annotation.annotationType();
if (annoClass.isAnnotationPresent(Scope.class)) {
return annoClass;
}
}
return annoDefault;
}
/**
* Returns true if the given annotation is a qualifier
* @param anno The annotation to check
* @return true if this is an annotation
*/
public static boolean isAnnotationAQualifier(Annotation anno) {
Class extends Annotation> annoType = anno.annotationType();
return annoType.isAnnotationPresent(Qualifier.class);
}
/**
* Gets all the qualifiers from the object
*
* @param t The object to analyze
* @return The set of qualifiers. Will not return null but may return an empty set
*/
public static Set getQualifiersFromObject(Object t) {
if (t == null) return Collections.emptySet();
return getQualifierAnnotations(t.getClass());
}
/**
* Gets all the qualifiers from the object
*
* @param clazz The class to analyze
* @return The set of qualifiers. Will not return null but may return an empty set
*/
public static Set getQualifiersFromClass(Class> clazz) {
Set retVal = new LinkedHashSet();
if (clazz == null) return retVal;
for (Annotation annotation : clazz.getAnnotations()) {
if (isAnnotationAQualifier(annotation)) {
retVal.add(annotation.annotationType().getName());
}
}
while (clazz != null) {
for (Class> iFace : clazz.getInterfaces()) {
for (Annotation annotation : iFace.getAnnotations()) {
if (isAnnotationAQualifier(annotation)) {
retVal.add(annotation.annotationType().getName());
}
}
}
clazz = clazz.getSuperclass();
}
return retVal;
}
private static Set internalGetQualifierAnnotations(AnnotatedElement annotatedGuy) {
Set retVal = new LinkedHashSet();
if (annotatedGuy == null) return retVal;
for (Annotation annotation : annotatedGuy.getAnnotations()) {
if (isAnnotationAQualifier(annotation)) {
if ((annotatedGuy instanceof Field) &&
Named.class.equals(annotation.annotationType())) {
Named n = (Named) annotation;
if (n.value() == null || "".equals(n.value())) {
// Because we do not have access to AnnotationLiteral
// we cannot "fix" a Named annotation that has no explicit
// value here, and we must rely on the caller of this
// method to "fix" that case for us
continue;
}
}
retVal.add(annotation);
}
}
if (!(annotatedGuy instanceof Class)) return retVal;
Class> clazz = (Class>) annotatedGuy;
while (clazz != null) {
for (Class> iFace : clazz.getInterfaces()) {
for (Annotation annotation : iFace.getAnnotations()) {
if (isAnnotationAQualifier(annotation)) {
retVal.add(annotation);
}
}
}
clazz = clazz.getSuperclass();
}
return retVal;
}
/**
* Gets all the qualifier annotations from the object
*
* A strange behavior of this method is that if the annotatedGuy is
* a field and that field has the Named annotation on it with no
* value, then that Named annotation will NOT be added to the return
* list. This is because we have no access at this level to
* AnnotationLiteral, and hence cannot create a NamedImpl with which
* to fix the annotation. It is the responsibility of the caller
* of this method to add in the NamedImpl in that circumstance
*
* @param annotatedGuy The thing to analyze
* @return The set of qualifiers. Will not return null but may return an empty set
*/
public static Set getQualifierAnnotations(final AnnotatedElement annotatedGuy) {
Set retVal = AccessController.doPrivileged(new PrivilegedAction>() {
@Override
public Set run() {
return internalGetQualifierAnnotations(annotatedGuy);
}
});
return retVal;
}
/**
* Writes a set in a way that can be read from an input stream as well
*
* @param set The set to write
* @return a representation of a list
*/
public static String writeSet(Set> set) {
return writeSet(set, null);
}
/**
* Writes a set in a way that can be read from an input stream as well
*
* @param set The set to write
* @param excludeMe An object to exclude from the list of things written
* @return a representation of a list
*/
public static String writeSet(Set> set, Object excludeMe) {
if (set == null) return "{}";
StringBuffer sb = new StringBuffer("{");
boolean first = true;
for (Object writeMe : set) {
if (excludeMe != null && excludeMe.equals(writeMe)) {
// Excluded
continue;
}
if (first) {
first = false;
sb.append(escapeString(writeMe.toString()));
}
else {
sb.append("," + escapeString(writeMe.toString()));
}
}
sb.append("}");
return sb.toString();
}
/**
* Writes a set in a way that can be read from an input stream as well. The values in
* the set may not contain the characters "{},"
*
* @param line The line to read
* @param addToMe The set to add the strings to
* @throws IOException On a failure
*/
public static void readSet(String line, Collection addToMe) throws IOException {
char asChars[] = new char[line.length()];
line.getChars(0, line.length(), asChars, 0);
internalReadSet(asChars, 0, addToMe);
}
/**
* Writes a set in a way that can be read from an input stream as well. The values in
* the set may not contain the characters "{},"
*
* @param asChars The line to read
* @param addToMe The set to add the strings to
* @return The number of characters read until the end of the set
* @throws IOException On a failure
*/
private static int internalReadSet(char asChars[], int startIndex, Collection addToMe) throws IOException {
int dot = startIndex;
int startOfSet = -1;
while (dot < asChars.length) {
if (asChars[dot] == '{') {
startOfSet = dot;
dot++;
break;
}
dot++;
}
if (startOfSet == -1) {
throw new IOException("Unknown set format, no initial { character : " + new String(asChars));
}
StringBuffer elementBuffer = new StringBuffer();
int endOfSet = -1;
while (dot < asChars.length) {
char dotChar = asChars[dot];
if (dotChar == '}') {
addToMe.add(elementBuffer.toString());
endOfSet = dot;
break; // Done!
}
if (dotChar == ',') {
// Terminating a single element
addToMe.add(elementBuffer.toString());
elementBuffer = new StringBuffer();
}
else {
// This character is either an escape character or a real character
if (dotChar != '\\') {
elementBuffer.append(dotChar);
}
else {
// This is an escape character
if (dot + 1 >= asChars.length) {
// This is an error, escape at end of buffer
break;
}
dot++; // Moves it forward
dotChar = asChars[dot];
if (dotChar == 'n') {
elementBuffer.append('\n');
}
else if (dotChar == 'r') {
elementBuffer.append('\r');
}
else {
elementBuffer.append(dotChar);
}
}
}
dot++;
}
if (endOfSet == -1) {
throw new IOException("Unknown set format, no ending } character : " + new String(asChars));
}
return dot - startIndex;
}
private static int readKeyStringListLine(char asChars[], int startIndex, Map> addToMe) throws IOException {
int dot = startIndex;
int equalsIndex = -1;
while (dot < asChars.length) {
char dotChar = asChars[dot];
if (dotChar == '=') {
equalsIndex = dot;
break;
}
dot++;
}
if (equalsIndex < 0) {
throw new IOException("Unknown key-string list format, no equals: " + new String(asChars));
}
String key = new String(asChars, startIndex, (equalsIndex - startIndex)); // Does not include the =
dot++; // Move it past the equals
if (dot >= asChars.length) {
// Key with no values, this is illegal
throw new IOException("Found a key with no value, " + key + " in line " + new String(asChars));
}
LinkedList listValues = new LinkedList();
int addOn = internalReadSet(asChars, dot, listValues);
if (!listValues.isEmpty()) {
addToMe.put(key, listValues);
}
dot += addOn + 1;
if (dot < asChars.length) {
char skipComma = asChars[dot];
if (skipComma == ',') {
dot++;
}
}
return dot - startIndex; // The +1 gets us to the next character in the stream
}
/**
* Writes a set in a way that can be read from an input stream as well
* @param line The line to read
* @param addToMe The set to add the strings to
* @throws IOException On a failure
*/
public static void readMetadataMap(String line, Map> addToMe) throws IOException {
char asChars[] = new char[line.length()];
line.getChars(0, line.length(), asChars, 0);
int dot = 0;
while (dot < asChars.length) {
int addMe = readKeyStringListLine(asChars, dot, addToMe);
dot += addMe;
}
}
private static String escapeString(String escapeMe) {
char asChars[] = new char[escapeMe.length()];
escapeMe.getChars(0, escapeMe.length(), asChars, 0);
StringBuffer sb = new StringBuffer();
for (int lcv = 0; lcv < asChars.length; lcv++) {
char candidateChar = asChars[lcv];
if (ESCAPE_CHARACTERS.contains(candidateChar)) {
sb.append('\\');
sb.append(candidateChar);
}
else if (REPLACE_CHARACTERS.containsKey(candidateChar)) {
char replaceWithMe = REPLACE_CHARACTERS.get(candidateChar);
sb.append('\\');
sb.append(replaceWithMe);
}
else {
sb.append(candidateChar);
}
}
return sb.toString();
}
private static String writeList(List list) {
StringBuffer sb = new StringBuffer("{");
boolean first = true;
for (String writeMe : list) {
if (first) {
first = false;
sb.append(escapeString(writeMe));
}
else {
sb.append("," + escapeString(writeMe));
}
}
sb.append("}");
return sb.toString();
}
/**
* Used to write the metadata out
*
* @param metadata The metadata to externalize
* @return The metadata in an externalizable format
*/
public static String writeMetadata(Map> metadata) {
StringBuffer sb = new StringBuffer();
boolean first = true;
for (Map.Entry> entry : metadata.entrySet()) {
if (first) {
first = false;
sb.append(entry.getKey() + '=');
}
else {
sb.append("," + entry.getKey() + '=');
}
sb.append(writeList(entry.getValue()));
}
return sb.toString();
}
/**
* Adds a value to the list of values associated with this key
*
* @param metadatas The base metadata object
* @param key The key to which to add the value. May not be null
* @param value The value to add. May not be null
*/
public static void addMetadata(Map> metadatas, String key, String value) {
if (key == null || value == null) return;
if (key.indexOf('=') >= 0) {
throw new IllegalArgumentException("The key field may not have an = in it:" + key);
}
List inner = metadatas.get(key);
if (inner == null) {
inner = new LinkedList();
metadatas.put(key, inner);
}
inner.add(value);
}
/**
* Removes the given value from the given key
*
* @param metadatas The base metadata object
* @param key The key of the value to remove. May not be null
* @param value The value to remove. May not be null
* @return true if the value was removed
*/
public static boolean removeMetadata(Map> metadatas, String key, String value) {
if (key == null || value == null) return false;
List inner = metadatas.get(key);
if (inner == null) return false;
boolean retVal = inner.remove(value);
if (inner.size() <= 0) metadatas.remove(key);
return retVal;
}
/**
* Removes all the metadata values associated with key
*
* @param metadatas The base metadata object
* @param key The key of the metadata values to remove
* @return true if any value was removed
*/
public static boolean removeAllMetadata(Map> metadatas, String key) {
List values = metadatas.remove(key);
return (values != null && values.size() > 0);
}
/**
* This method does a deep copy of the incoming meta-data, (which basically means we will
* also make copies of the value list)
*
* @param copyMe The guy to copy (if null, null will be returned)
* @return A deep copy of the metadata
*/
public static Map> deepCopyMetadata(Map> copyMe) {
if (copyMe == null) return null;
Map> retVal = new LinkedHashMap>();
for (Map.Entry> entry : copyMe.entrySet()) {
String key = entry.getKey();
if (key.indexOf('=') >= 0) {
throw new IllegalArgumentException("The key field may not have an = in it:" + key);
}
List values = entry.getValue();
LinkedList valuesCopy = new LinkedList();
for (String value : values) {
valuesCopy.add(value);
}
retVal.put(key, valuesCopy);
}
return retVal;
}
/**
* Sets the given field to the given value
*
* @param field The non-null field to set
* @param instance The non-null instance to set into
* @param value The value to which the field should be set
* @throws Throwable If there was some exception while setting the field
*/
public static void setField(Field field, Object instance, Object value) throws Throwable {
setAccessible(field);
try {
field.set(instance, value);
}
catch (IllegalArgumentException e) {
Logger.getLogger().debug(field.getDeclaringClass().getName(), field.getName(), e);
throw e;
}
catch (IllegalAccessException e) {
Logger.getLogger().debug(field.getDeclaringClass().getName(), field.getName(), e);
throw e;
}
}
/**
* This version of invoke is CCL neutral (it will return with the
* same CCL as what it went in with)
*
* @param m the method to invoke
* @param o the object on which to invoke it
* @param args The arguments to invoke (may not be null)
* @param neutralCCL true if the ContextClassLoader shoult remain null with this call
* @return The return from the invocation
* @throws Throwable The unwrapped throwable thrown by the method
*/
public static Object invoke(Object o, Method m, Object args[], boolean neutralCCL)
throws Throwable {
if (isStatic(m)) {
o = null;
}
setAccessible(m);
ClassLoader currentCCL = null;
if (neutralCCL) {
currentCCL = getCurrentContextClassLoader();
}
try {
return m.invoke(o, args);
}
catch (InvocationTargetException ite) {
Throwable targetException = ite.getTargetException();
Logger.getLogger().debug(m.getDeclaringClass().getName(), m.getName(), targetException);
throw targetException;
}
catch (Throwable th) {
Logger.getLogger().debug(m.getDeclaringClass().getName(), m.getName(), th);
throw th;
}
finally {
if (neutralCCL) {
setContextClassLoader(Thread.currentThread(), currentCCL);
}
}
}
/**
* Returns true if the underlying member is static
*
* @param member The non-null member to test
* @return true if the member is static
*/
public static boolean isStatic(Member member) {
int modifiers = member.getModifiers();
return ((modifiers & Modifier.STATIC) != 0);
}
/**
* Sets the context classloader under the privileged of this class
* @param t The thread on which to set the classloader
* @param l The classloader to set
*/
private static void setContextClassLoader(final Thread t, final ClassLoader l) {
AccessController.doPrivileged(new PrivilegedAction