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

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

The newest version!
/*
 * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

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.Array;
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 jakarta.inject.Named;
import jakarta.inject.Qualifier;
import jakarta.inject.Scope;

import org.glassfish.hk2.utilities.general.GeneralUtilities;
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) && !(componentType instanceof Class)) {
                // 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;
    }
    
    /**
     * Resolves the generic type of a field given the actual class being instantiated
     * 
     * @param topclass The instantiation class.  Must not be null
     * @param field The non-null field whose type to resolve
     * @return The resolved field type by way of its subclasses.  Will not return
     * null, but may return the original fields generic type
     */
    public static Type resolveField(Class topclass, Field field) {
        return resolveMember(topclass, field.getGenericType(), field.getDeclaringClass());
    }
    
    /**
     * Resolves the generic type of a type and declaring class given the actual class being instantiated
     * 
     * @param topclass The instantiation class.  Must not be null
     * @param lookingForType The type to resolve.  Must not be null
     * @param declaringClass The class of the entity declaring the lookingForType. Must not be null
     * @return The resolved type by way of its subclasses.  Will not return null but may
     * return lookingForType if it could not be further resolved
     */
    public static Type resolveMember(Class topclass, Type lookingForType, Class declaringClass) {
        Map typeArguments = typesFromSubClassToDeclaringClass(topclass, declaringClass);
        if (typeArguments == null) return lookingForType;
        
        if (lookingForType instanceof ParameterizedType) {
            return fixTypeVariables((ParameterizedType) lookingForType, typeArguments);
        }
        
        if (lookingForType instanceof GenericArrayType) {
            return fixGenericArrayTypeVariables((GenericArrayType) lookingForType, typeArguments);
        }
        
        if (!(lookingForType instanceof TypeVariable)) {
            return lookingForType;
        }
        
        TypeVariable tv = (TypeVariable) lookingForType;
        String typeVariableName = tv.getName();
        
        Type retVal = typeArguments.get(typeVariableName);
        if (retVal == null) return lookingForType;
        
        if (retVal instanceof Class) return retVal;
        
        if (retVal instanceof ParameterizedType) {
            return fixTypeVariables((ParameterizedType) retVal, typeArguments);
        }
        
        if (retVal instanceof GenericArrayType) {
            return fixGenericArrayTypeVariables((GenericArrayType) retVal, typeArguments);
        }
        
        return retVal;
    }
    
    /**
     * If you have a class that declares type variables (knownDeclaringClass) and the type the user has told
     * us it should be (knownType) then return the replaced type for the given userType.  Returns null if
     * there is no match for the userType
     * 
     * @param userType The user type to replace.  May not be null
     * @param knownType The user defined known final type of the knownDeclaringClass. May not be null
     * @param knownDeclaringClass The declaringClass for which knownType is the resolved ParameterizedType for it.
     * May not be null
     * @return null if userType is not related, or the given hardened parameterized type from the knownType list
     */
    public static Type resolveKnownType(TypeVariable userType, ParameterizedType knownType, Class knownDeclaringClass) {
        TypeVariable knownTypeVariables[] = knownDeclaringClass.getTypeParameters();
        for (int lcv = 0; lcv < knownTypeVariables.length; lcv++) {
            TypeVariable knownTypeVariable = knownTypeVariables[lcv];
            
            if (GeneralUtilities.safeEquals(knownTypeVariable.getName(), userType.getName())) {
                return knownType.getActualTypeArguments()[lcv];
            }
        }
        
        return null;
    }
    
    private static Map typesFromSubClassToDeclaringClass(Class topClass, Class declaringClass) {
        if (topClass.equals(declaringClass)) {
            return null;
        }
        
        Type superType = topClass.getGenericSuperclass();
        Class superClass = getRawClass(superType);
        
        while (superType != null && superClass != null) {
            if (!(superType instanceof ParameterizedType)) {
                // superType MUST be a Class in this case
                if (superClass.equals(declaringClass)) return null;
                
                superType = superClass.getGenericSuperclass();
                superClass = ReflectionHelper.getRawClass(superType);
                
                continue;
            }
            
            ParameterizedType superPT = (ParameterizedType) superType;
            
            Map typeArguments = getTypeArguments(superClass, superPT);
            
            if (superClass.equals(declaringClass)) {
                return typeArguments;
            }
            
            superType = superClass.getGenericSuperclass();
            superClass = ReflectionHelper.getRawClass(superType);

            if (superType instanceof ParameterizedType) {
                superType = fixTypeVariables((ParameterizedType) superType, typeArguments);
            }
        }
        
        throw new AssertionError(topClass.getName() + " is not the same as or a subclass of " + declaringClass.getName());
    }
    
    /**
     * Gets the first type argument if this is a parameterized
     * type, otherwise it returns Object.class
     *
     * @param type The type to find the first type argument on
     * @return If this is a class, Object.class. If this is a parameterized
     * type, the type of the first actual argument
     */
    public static Type getFirstTypeArgument(Type type) {
        if (type instanceof Class) {
            return Object.class;
        }

        if (!(type instanceof ParameterizedType)) return Object.class;

        ParameterizedType pt = (ParameterizedType) type;
        Type arguments[] = pt.getActualTypeArguments();
        if (arguments.length <= 0) return Object.class;

        return arguments[0];
    }

    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) {
            return new ParameterizedTypeImpl(type.getRawType(), newTypeArguments);
        }
        
        return type;
    }
    
    /**
     * 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 fixGenericArrayTypeVariables(GenericArrayType type,
                                         Map typeArgumentsMap) {

        
        Type newTypeArgument = getNewTypeArrayArguments(type, typeArgumentsMap);

        if (newTypeArgument != null) {
            if (newTypeArgument instanceof Class) {
                return getArrayOfType((Class) newTypeArgument);
            }
            
            if (newTypeArgument instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) newTypeArgument;
                if (pt.getRawType() instanceof Class) {
                    return getArrayOfType((Class) pt.getRawType());
                }
            }
            
            return new GenericArrayTypeImpl(newTypeArgument);
        }
        
        return type;
    }
    
    private static Class getArrayOfType(Class type) {
        return Array.newInstance(type, 0).getClass();
    }

    /**
     * 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 if (argType instanceof ParameterizedType) {
                ParameterizedType original = (ParameterizedType) argType;
                
                Type[] internalTypeArgs = getNewTypeArguments(original, typeArgumentsMap);
                if (internalTypeArgs != null) {
                    newTypeArguments[i] = new ParameterizedTypeImpl(original.getRawType(), internalTypeArgs);
                    newArgsNeeded = true;
                }
                else {
                    newTypeArguments[i] = argType;
                }
            }
            else if (argType instanceof GenericArrayType) {
                GenericArrayType gat = (GenericArrayType) argType;
                
                Type internalTypeArg = getNewTypeArrayArguments(gat, typeArgumentsMap);
                if (internalTypeArg != null) {
                    if (internalTypeArg instanceof Class) {
                        newTypeArguments[i] = getArrayOfType((Class) internalTypeArg);
                        newArgsNeeded = true;
                    }
                    else if ((internalTypeArg instanceof ParameterizedType) &&
                            (((ParameterizedType) internalTypeArg).getRawType() instanceof Class)) {
                        ParameterizedType pt = (ParameterizedType) internalTypeArg;
                        
                        newTypeArguments[i] = getArrayOfType((Class) pt.getRawType());
                        newArgsNeeded = true;
                    }
                    else {
                        newTypeArguments[i] = new GenericArrayTypeImpl(internalTypeArg);
                        newArgsNeeded = true;
                    }
                }
                else {
                    newTypeArguments[i] = argType;
                }
            }
            else {
                newTypeArguments[i] = argType;
            }
            
            i++;
        }
        
        return newArgsNeeded ? newTypeArguments : null;
    }
    
    /**
     * Get a new Type for a GenericArrayType, 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 getNewTypeArrayArguments(final GenericArrayType gat,
                                              final Map typeArgumentsMap) {

        Type  typeArgument    = gat.getGenericComponentType();

        if (typeArgument instanceof TypeVariable) {
            return typeArgumentsMap.get(((TypeVariable) typeArgument).getName());
        }
        
        if (typeArgument instanceof ParameterizedType) {
            ParameterizedType original = (ParameterizedType) typeArgument;
                
            Type[] internalTypeArgs = getNewTypeArguments(original, typeArgumentsMap);
            if (internalTypeArgs != null) {
                return new ParameterizedTypeImpl(original.getRawType(), internalTypeArgs);
            }
            
            return original;
        }
        
        if (typeArgument instanceof GenericArrayType) {
            GenericArrayType original = (GenericArrayType) typeArgument;
                
            Type internalTypeArg = getNewTypeArrayArguments(original, typeArgumentsMap);
            if (internalTypeArg != null) {
                if (internalTypeArg instanceof Class) {
                    return getArrayOfType((Class) internalTypeArg);
                }
                
                if (internalTypeArg instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) internalTypeArg;
                    
                    if (pt.getRawType() instanceof Class) {
                        return getArrayOfType((Class) pt.getRawType());
                    }
                }
                
                return new GenericArrayTypeImpl(internalTypeArg);
            }
            
            return null;
        }
        
        return 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(
            final Class clazz,
            final Class markerAnnotation) {
        final Set contractSet = new LinkedHashSet<>();
        if (clazz == null) {
            return contractSet;
        }
        contractSet.add(clazz.getName());

        for (Class extendsClasses = clazz; extendsClasses != null;
             extendsClasses = extendsClasses.getSuperclass()) {
            if (extendsClasses.isAnnotationPresent(markerAnnotation)) {
                contractSet.add(extendsClasses.getName());
            }
            recursiveAddContractsFromInterfacesToMap(
                    extendsClasses.getInterfaces(), contractSet, markerAnnotation);
        }
        return contractSet;
    }

    private static void recursiveAddContractsFromInterfacesToMap(
            final Class[] interfaces,
            final Set contractSet,
            final Class markerAnnotation) {
        for (final Class anInterface : interfaces) {
            if (anInterface.isAnnotationPresent(markerAnnotation)) {
                contractSet.add(anInterface.getName());
            }
            recursiveAddContractsFromInterfacesToMap
                    (anInterface.getInterfaces(), contractSet, markerAnnotation);
        }
    }

    /**
     * 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 Annotation getScopeFromObject(Object t, 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 Annotation getScopeFromClass(Class clazz, Annotation annoDefault) {
        if (clazz == null) return annoDefault;

        for (Annotation annotation : clazz.getAnnotations()) {
            Class annoClass = annotation.annotationType();

            if (annoClass.isAnnotationPresent(Scope.class)) {
                return annotation;
            }

        }

        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); } /** * Casts this thing to the given type * @param o The thing to cast * @return A casted version of o */ @SuppressWarnings("unchecked") public static T cast(Object o) { return (T) o; } }