All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.hk2.utilities.reflection.ReflectionHelper Maven / Gradle / Ivy

There is a newer version: 8.16.0
Show newest version
/*
 * 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 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 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 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 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 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 getScopeFromObject(Object t, Class 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 getScopeFromClass(Class clazz, Class annoDefault) {
        if (clazz == null) return annoDefault;

        for (Annotation annotation : clazz.getAnnotations()) {
            Class 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 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() { @Override public Object run() { t.setContextClassLoader(l); return null; } }); } /** * Sets this accessible object to be accessible using the permissions of * the hk2-locator bundle (which will need the required grant) * * @param ao The object to change */ private static void setAccessible(final AccessibleObject ao) { if (ao.isAccessible()) return; AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { ao.setAccessible(true); return null; } }); } /** * This version of invoke is CCL neutral (it will return with the * same CCL as what it went in with) * * @param c the constructor to call * @param args The arguments to invoke (may not be null) * @param neutralCCL true if the context class loader should remain null through this call * @return The return from the invocation * @throws Throwable The unwrapped throwable thrown by the method */ public static Object makeMe(Constructor c, Object args[], boolean neutralCCL) throws Throwable { ClassLoader currentCCL = null; if (neutralCCL) { currentCCL = getCurrentContextClassLoader(); } try { return c.newInstance(args); } catch (InvocationTargetException ite) { Throwable targetException = ite.getTargetException(); Logger.getLogger().debug(c.getDeclaringClass().getName(), c.getName(), targetException); throw targetException; } catch (Throwable th) { Logger.getLogger().debug(c.getDeclaringClass().getName(), c.getName(), th); throw th; } finally { if (neutralCCL) { setContextClassLoader(Thread.currentThread(), currentCCL); } } } /** * This method parses the string that is found in the VService metadata field. * * @param metadataField A non-null metadata field that normally comes from the Service * metadata field * @param metadata The metadata field to add to * @throws IllegalStateException if a string with an invalid format is found */ public static void parseServiceMetadataString(String metadataField, Map> metadata) { StringBuffer sb = new StringBuffer(metadataField); int dot = 0; int nextEquals = sb.indexOf(EQUALS_STRING, dot); while (nextEquals > 0) { String key = sb.substring(dot, nextEquals); dot = nextEquals + 1; int commaPlace; String value = null; if (sb.charAt(dot) == '\"') { dot++; // Get past the quote int nextQuote = sb.indexOf(QUOTE_STRING, dot); if (nextQuote < 0) { // What to do? throw new IllegalStateException("Badly formed metadata \"" + metadataField + "\" for key " + key + " has a leading quote but no trailing quote"); } value = sb.substring(dot, nextQuote); dot = nextQuote + 1; commaPlace = sb.indexOf(COMMA_STRING, dot); // Should be right at dot } else { commaPlace = sb.indexOf(COMMA_STRING, dot); if (commaPlace < 0) { value = sb.substring(dot); } else { value = sb.substring(dot, commaPlace); } } List addToMe = metadata.get(key); if (addToMe == null) { addToMe = new LinkedList(); metadata.put(key, addToMe); } addToMe.add(value); if (commaPlace >= 0) { dot = commaPlace + 1; nextEquals = sb.indexOf(EQUALS_STRING, dot); } else { nextEquals = -1; } } } /** * Gets the name from the &46;Named qualifier in this set of qualifiers * * @param qualifiers The set of qualifiers that may or may not have Named in it * @param parent The parent element for which we are searching * @return null if no Named was found, or the appropriate name otherwise * @throws IllegalStateException If the parent is annotated with a blank Named but is not * a Class or a Field */ public static String getNameFromAllQualifiers(Set qualifiers, AnnotatedElement parent) throws IllegalStateException { for (Annotation qualifier : qualifiers) { if (!Named.class.equals(qualifier.annotationType())) continue; Named named = (Named) qualifier; if ((named.value() == null) || named.value().equals("")) { if (parent != null) { if (parent instanceof Class) { return Pretty.clazz((Class) parent); } if (parent instanceof Field) { return ((Field) parent).getName(); } } throw new IllegalStateException("@Named must have a value for " + parent); } return named.value(); } return null; } /** * Gets the current context class loader with privs * * @return The current context class loader */ private static ClassLoader getCurrentContextClassLoader() { return AccessController.doPrivileged(new PrivilegedAction() { @Override public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); } /** * This is used to check on the annotation set. It must be done under protection because the annotations may * attempt to discover if they are equal using getDeclaredMembers permission * * @param candidateAnnotations The candidate annotations * @param requiredAnnotations The required annotations * @return true if the candidate set contains the entire required set */ public static boolean annotationContainsAll(final Set candidateAnnotations, final Set requiredAnnotations) { return AccessController.doPrivileged(new PrivilegedAction() { @Override public Boolean run() { return candidateAnnotations.containsAll(requiredAnnotations); } }); } /** * Converts the type to its java form, or returns the original * * @param type The type to convert * @return The translated type or the type itself */ public static Class translatePrimitiveType(Class type) { Class translation = Constants.PRIMITIVE_MAP.get(type); if (translation == null) return type; return translation; } /** * Returns true if the underlying member is private * * @param member The non-null member to test * @return true if the member is private */ public static boolean isPrivate(Member member) { int modifiers = member.getModifiers(); return ((modifiers & Modifier.PRIVATE) != 0); } public static Set getAllTypes(Type t) { LinkedHashSet retVal = new LinkedHashSet(); retVal.add(t); Class rawClass = ReflectionHelper.getRawClass(t); if (rawClass == null) return retVal; Type genericSuperclass = rawClass.getGenericSuperclass(); while (genericSuperclass != null) { Class rawSuperclass = ReflectionHelper.getRawClass(genericSuperclass); if (rawSuperclass == null) break; retVal.add(genericSuperclass); genericSuperclass = rawSuperclass.getGenericSuperclass(); } while (rawClass != null) { for (Type iface : rawClass.getGenericInterfaces()) { addAllInterfaceContracts(iface, retVal); } rawClass = rawClass.getSuperclass(); } LinkedHashSet altRetVal = new LinkedHashSet(); HashMap, ParameterizedType> class2TypeMap = new HashMap, ParameterizedType>(); for (Type foundType : retVal) { if (!(foundType instanceof ParameterizedType)) { altRetVal.add(foundType); continue; } ParameterizedType originalPt = (ParameterizedType) foundType; Class rawType = getRawClass(foundType); class2TypeMap.put(rawType, originalPt); if (isFilledIn(originalPt)) { altRetVal.add(foundType); continue; } ParameterizedType pti = fillInPT(originalPt, class2TypeMap); altRetVal.add(pti); class2TypeMap.put(rawType, pti); } return altRetVal; } @SuppressWarnings("unchecked") private static ParameterizedType fillInPT(ParameterizedType pt, HashMap, ParameterizedType> class2TypeMap) { if (isFilledIn(pt)) return pt; // At this point, this guy may need to get filled in Type newActualArguments[] = new Type[pt.getActualTypeArguments().length]; for (int outerIndex = 0 ; outerIndex < newActualArguments.length; outerIndex++) { Type fillMeIn = pt.getActualTypeArguments()[outerIndex]; // All else failing ensure it is filled in with the original value newActualArguments[outerIndex] = fillMeIn; if (fillMeIn instanceof ParameterizedType) { newActualArguments[outerIndex] = fillInPT((ParameterizedType) fillMeIn, class2TypeMap); continue; } if (!(fillMeIn instanceof TypeVariable)) { continue; } TypeVariable> tv = (TypeVariable>) fillMeIn; Class genericDeclaration = tv.getGenericDeclaration(); boolean found = false; int count = -1; for (Type parentVariable : genericDeclaration.getTypeParameters()) { count++; if (parentVariable.equals(tv)) { found = true; break; } } if (found == false) continue; ParameterizedType parentPType = class2TypeMap.get(genericDeclaration); if (parentPType == null) continue; newActualArguments[outerIndex] = parentPType.getActualTypeArguments()[count]; } ParameterizedTypeImpl pti = new ParameterizedTypeImpl(getRawClass(pt), newActualArguments); return pti; } private static boolean isFilledIn(ParameterizedType pt, HashSet recursionKiller) { if (recursionKiller.contains(pt)) return false; recursionKiller.add(pt); for (Type t : pt.getActualTypeArguments()) { if (t instanceof TypeVariable) return false; if (t instanceof WildcardType) return false; if (t instanceof ParameterizedType) { return (isFilledIn((ParameterizedType) t, recursionKiller)); } } return true; } private static boolean isFilledIn(ParameterizedType pt) { return isFilledIn(pt, new HashSet()); } private static void addAllInterfaceContracts(Type interfaceType, LinkedHashSet addToMe) { Class interfaceClass = ReflectionHelper.getRawClass(interfaceType); if (interfaceClass == null) return; if (addToMe.contains(interfaceType)) return; addToMe.add(interfaceType); for (Type extendedInterfaces : interfaceClass.getGenericInterfaces()) { addAllInterfaceContracts(extendedInterfaces, addToMe); } } /** * Creates a method wrapper for the given method * * @param wrapMe The non-null method to wrap * @return A method wrapper that has a proper equals/hashCode */ public static MethodWrapper createMethodWrapper(Method wrapMe) { return new MethodWrapperImpl(wrapMe); } }