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

org.jvnet.hk2.internal.Utilities Maven / Gradle / Ivy

There is a newer version: 2.22.2
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012 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.jvnet.hk2.internal;

import org.aopalliance.intercept.ConstructorInterceptor;
import org.aopalliance.intercept.MethodInterceptor;

import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.ref.SoftReference;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
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 java.util.WeakHashMap;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Scope;
import javax.inject.Singleton;

import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.ClassAnalyzer;
import org.glassfish.hk2.api.Context;
import org.glassfish.hk2.api.Descriptor;
import org.glassfish.hk2.api.DescriptorType;
import org.glassfish.hk2.api.DescriptorVisibility;
import org.glassfish.hk2.api.DynamicConfigurationListener;
import org.glassfish.hk2.api.DynamicConfigurationService;
import org.glassfish.hk2.api.ErrorService;
import org.glassfish.hk2.api.ErrorType;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.Filter;
import org.glassfish.hk2.api.HK2Loader;
import org.glassfish.hk2.api.IndexedFilter;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.InstanceLifecycleListener;
import org.glassfish.hk2.api.InterceptionService;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.Proxiable;
import org.glassfish.hk2.api.ProxyCtl;
import org.glassfish.hk2.api.ProxyForSameScope;
import org.glassfish.hk2.api.Rank;
import org.glassfish.hk2.api.Self;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.Unproxiable;
import org.glassfish.hk2.api.Unqualified;
import org.glassfish.hk2.api.UseProxy;
import org.glassfish.hk2.api.ValidationService;
import org.glassfish.hk2.api.Visibility;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.hk2.utilities.NamedImpl;
import org.glassfish.hk2.utilities.reflection.ClassReflectionHelper;
import org.glassfish.hk2.utilities.reflection.Constants;
import org.glassfish.hk2.utilities.reflection.MethodWrapper;
import org.glassfish.hk2.utilities.reflection.Pretty;
import org.glassfish.hk2.utilities.reflection.ParameterizedTypeImpl;
import org.glassfish.hk2.utilities.reflection.ReflectionHelper;
import org.glassfish.hk2.utilities.reflection.ScopeInfo;
import org.glassfish.hk2.utilities.reflection.TypeChecker;
import org.jvnet.hk2.annotations.Contract;
import org.jvnet.hk2.annotations.ContractsProvided;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;

/**
 * This class contains a set of static utilities useful
 * for implementing HK2
 *
 * @author jwells
 *
 */
public class Utilities {
    private final static String USE_SOFT_REFERENCE_PROPERTY = "org.jvnet.hk2.properties.useSoftReference";
    private final static boolean USE_SOFT_REFERENCE;
    static {
        USE_SOFT_REFERENCE = AccessController.doPrivileged(new PrivilegedAction() {

            @Override
            public Boolean run() {
                return Boolean.parseBoolean(System.getProperty(USE_SOFT_REFERENCE_PROPERTY, "true"));
            }

        });
    }
    
    private final static Object lock = new Object();
    
    private final static WeakHashMap, String> autoAnalyzerNameCache = new WeakHashMap, String>();
    private final static WeakHashMap annotationCache =
            new WeakHashMap();
    
    private final static AnnotationInformation DEFAULT_ANNOTATION_INFORMATION = new AnnotationInformation(
            Collections.emptySet(),
            false,
            false,
            null);

    /**
     * Returns the class analyzer with the given name
     *
     * @param sli The ServiceLocator to search in.  May not be null
     * @param analyzerName The name of the analyzer (may be null for the default analyzer)
     * @param errorCollector A non-null collector of exceptions
     * @return The ClassAnalyzer corresponding to the name, or null if none was found
     */
    public static ClassAnalyzer getClassAnalyzer(
            ServiceLocatorImpl sli,
            String analyzerName,
            Collector errorCollector) {
        return sli.getAnalyzer(analyzerName, errorCollector);
    }

    /**
     * Gets the constructor given the implClass and analyzer.  Checks service output
     *
     * @param implClass The implementation class (not null)
     * @param analyzer The analyzer (not null)
     * @param collector A collector for errors (not null)
     * @return null on failure (collector will have failures), non-null on success
     */
    public static  Constructor getConstructor(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        Constructor element = null;
        try {
            element = analyzer.getConstructor(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return element;
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return element;
        }

        if (element == null) {
            collector.addThrowable(new AssertionError("null return from getConstructor method of analyzer " +
                analyzer + " for class " + implClass.getName()));
            return element;
        }
        final Constructor result = element;
        AccessController.doPrivileged(new PrivilegedAction(){

            @Override
            public Object run() {
                result.setAccessible(true);
                return null;
            }
        });

        return element;
    }

    /**
     * Gets the initializer methods from the given class and analyzer.  Checks service output
     *
     * @param implClass the non-null impl class
     * @param analyzer the non-null analyzer
     * @param collector for gathering errors
     * @return a non-null set (even in error cases, check the collector)
     */
    public static Set getInitMethods(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        Set retVal;
        try {
            retVal = analyzer.getInitializerMethods(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return Collections.emptySet();
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return Collections.emptySet();
        }

        if (retVal == null) {
            collector.addThrowable(new AssertionError("null return from getInitializerMethods method of analyzer " +
                    analyzer + " for class " + implClass.getName()));
            return Collections.emptySet();
        }

        return retVal;
    }

    /**
     * Gets the initializer fields from the given class and analyzer.  Checks service output
     *
     * @param implClass the non-null impl class
     * @param analyzer the non-null analyzer
     * @param collector for gathering errors
     * @return a non-null set (even in error cases, check the collector)
     */
    public static Set getInitFields(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        Set retVal;
        try {
            retVal = analyzer.getFields(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return Collections.emptySet();
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return Collections.emptySet();
        }

        if (retVal == null) {
            collector.addThrowable(new AssertionError("null return from getFields method of analyzer " +
                    analyzer + " for class " + implClass.getName()));
            return Collections.emptySet();
        }

        return retVal;
    }

    /**
     * Gets the post construct from the analyzer, checking output
     *
     * @param implClass The non-null implementation class
     * @param analyzer The non-null analyzer
     * @param collector The non-null error collector
     * @return The possibly null post-construct method (check the collector for errors)
     */
    public static Method getPostConstruct(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        try {
            return analyzer.getPostConstructMethod(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return null;
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return null;
        }
    }

    /**
     * Gets the preDestroy from the analyzer, checking output
     *
     * @param implClass The non-null implementation class
     * @param analyzer The non-null analyzer
     * @param collector The non-null error collector
     * @return The possibly null pre-destroy method (check the collector for errors)
     */
    public static Method getPreDestroy(Class implClass, ClassAnalyzer analyzer, Collector collector) {
        try {
            return analyzer.getPreDestroyMethod(implClass);
        }
        catch (MultiException me) {
            collector.addMultiException(me);
            return null;
        }
        catch (Throwable th) {
            collector.addThrowable(th);
            return null;
        }
    }

    /**
     * This utility will return the proper implementation class, taking into account that the
     * descriptor may be a factory
     *
     * @param descriptor The descriptor (reified and not null) that will be used to find the
     * implementation
     *
     * @return The real implementation class
     */
    public static Class getFactoryAwareImplementationClass(ActiveDescriptor descriptor) {
        if (descriptor.getDescriptorType().equals(DescriptorType.CLASS)) {
            return descriptor.getImplementationClass();
        }

        return getFactoryProductionClass(descriptor.getImplementationClass());
    }

    /**
     * Checks that the incoming lookup type is not improper in some way
     *
     * @param checkMe  class to check
     */
    public static void checkLookupType(Class checkMe) {
        if (!checkMe.isAnnotation()) return;

        // If it is in annotation we need to ensure it is either a
        // scope or a qualifier
        if (checkMe.isAnnotationPresent(Scope.class)) return;
        if (checkMe.isAnnotationPresent(Qualifier.class)) return;

        throw new IllegalArgumentException("Lookup type " + checkMe + " must be a scope or annotation");
    }

    /**
     * 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;
    }

    /**
     * Calls the list of error services for the list of errors
     *
     * @param results    the results
     * @param callThese  the services to call
     */
    public static void handleErrors(NarrowResults results, LinkedList callThese) {
        Collector collector = new Collector();
        for (ErrorResults errorResult : results.getErrors()) {
            for (ErrorService eService : callThese) {
                try {
                    eService.onFailure(new ErrorInformationImpl(
                            ErrorType.FAILURE_TO_REIFY,
                            errorResult.getDescriptor(),
                            errorResult.getInjectee(),
                            errorResult.getMe()));
                } catch (MultiException me) {
                    for (Throwable th : me.getErrors()) {
                        collector.addThrowable(th);
                    }
                } catch (Throwable th) {
                    collector.addThrowable(th);
                }
            }
        }

        collector.throwIfErrors();
    }

    /**
     * Loads the class using the loader from the given descriptor or the
     * classloader of the utilities class otherwise
     *
     * @param loadMe The fully qualified class name
     * @param fromMe The descriptor to use for the loader
     * @param collector The error collector to fill in if this returns null
     * @return null on failure to load (the failure will be added to the collector)
     */
    public static Class loadClass(String loadMe, Descriptor fromMe, Collector collector) {
        HK2Loader loader = fromMe.getLoader();
        if (loader == null) {
            ClassLoader cl = Utilities.class.getClassLoader();
            if (cl == null) {
                cl = ClassLoader.getSystemClassLoader();
            }

            try {
                return cl.loadClass(loadMe);
            } catch (Throwable th) {
                collector.addThrowable(th);
                return null;
            }
        }

        try {
            return loader.loadClass(loadMe);
        } catch (Throwable th) {
            if (th instanceof MultiException) {
                MultiException me = (MultiException) th;

                for (Throwable th2 : me.getErrors()) {
                    collector.addThrowable(th2);
                }
            } else {
                collector.addThrowable(th);
            }

            return null;
        }

    }

    /**
     * Load the given class for the given injectee.
     *
     * @param implementation  the impl class name string
     * @param injectee        the injectee
     *
     * @return The class represented by this implementation and injectee
     */
    public static Class loadClass(String implementation, Injectee injectee) {
        ClassLoader loader;
        if (injectee != null) {
            AnnotatedElement parent = injectee.getParent();

            if (parent instanceof Constructor) {
                loader = ((Constructor) parent).getDeclaringClass().getClassLoader();
            } else if (parent instanceof Method) {
                loader = ((Method) parent).getDeclaringClass().getClassLoader();
            } else if (parent instanceof Field) {
                loader = ((Field) parent).getDeclaringClass().getClassLoader();
            } else {
                loader = injectee.getClass().getClassLoader();
            }
        } else {
            loader = Utilities.class.getClassLoader();
        }

        try {
            return loader.loadClass(implementation);
        } catch (Throwable th) {
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            if (ccl != null) {
                try {
                    return ccl.loadClass(implementation);
                }
                catch (Throwable th2) {
                    MultiException me = new MultiException(th);
                    me.addError(th2);
                    
                    throw me;
                }
            }
            
            throw new MultiException(th);
        }
    }

    /**
     * Will return the class of the injection resolver annotation type, or null if
     * no injection resolver annotation can be found
     *
     * @param desc The reified descriptor to find the injection resolution on
     * @return The annotation type for this injection resolver
     */
    @SuppressWarnings("unchecked")
    public static Class getInjectionResolverType(ActiveDescriptor desc) {
        for (Type advertisedType : desc.getContractTypes()) {
            Class rawClass = ReflectionHelper.getRawClass(advertisedType);

            if (!InjectionResolver.class.equals(rawClass)) continue;

            // Found the InjectionResolver
            if (!(advertisedType instanceof ParameterizedType)) {
                return null;
            }

            Type firstType = getFirstTypeArgument(advertisedType);
            if (!(firstType instanceof Class)) {
                return null;
            }

            Class retVal = (Class) firstType;

            if (!Annotation.class.isAssignableFrom(retVal)) {
                return null;
            }

            return (Class) retVal;
        }

        return null;
    }

    /**
     * This method returns the class associated with the type of the
     * factory
     *
     * @param factoryClass The non-null factory class.  May not be null
     * @return the CLASS version of what the factory produces.  Will
     * not be null
     * @throws MultiException if there was an error analyzing the class
     */
    private static Class getFactoryProductionClass(Class factoryClass) {
        Type factoryProvidedType = getFactoryProductionType(factoryClass);

        Class retVal = ReflectionHelper.getRawClass(factoryProvidedType);
        if (retVal != null) return retVal;

        throw new MultiException(new AssertionError("Could not find true produced type of factory " + factoryClass.getName()));
    }

    /**
     * This method returns the type produced by a factory class
     *
     * @param factoryClass The non-null factory class.  May not be null
     * @return the type version of what the factory produces.  Will
     * not be null
     * @throws MultiException if there was an error analyzing the class
     */
    public static Type getFactoryProductionType(Class factoryClass) {
        Set factoryTypes = ReflectionHelper.getTypeClosure(factoryClass,
                Collections.singleton(Factory.class.getName()));

        ParameterizedType parameterizedType = (ParameterizedType) factoryTypes.iterator().next();

        Type factoryProvidedType = parameterizedType.getActualTypeArguments()[0];

        return factoryProvidedType;
    }

    /**
     * Checks to be sure the Factory class is ok
     *
     * @param factoryClass  the class to check
     * @param collector     the exception collector
     */
    public static void checkFactoryType(Class factoryClass, Collector collector) {
        for (Type type : factoryClass.getGenericInterfaces()) {
            Class rawClass = ReflectionHelper.getRawClass(type);
            if (rawClass == null) continue;

            if (!Factory.class.equals(rawClass)) continue;

            Type firstType = getFirstTypeArgument(type);

            if (firstType instanceof WildcardType) {
                // This should not be possible
                collector.addThrowable(new IllegalArgumentException("The class " +
                        Pretty.clazz(factoryClass) + " has a Wildcard as its type"));
            }
        }

    }
    
    private static void addAllInterfaceContracts(Type interfaceType, LinkedHashSet addToMe) {
        Class interfaceClass = ReflectionHelper.getRawClass(interfaceType);
        if (interfaceClass == null) return;
        if (addToMe.contains(interfaceType)) return;
        
        if (interfaceClass.isAnnotationPresent(Contract.class)) {
            addToMe.add(interfaceType);
        }
        
        for (Type extendedInterfaces : interfaceClass.getGenericInterfaces()) {
            addAllInterfaceContracts(extendedInterfaces, addToMe);
        }
    }

    private static Set getAutoAdvertisedTypes(Type t) {
        LinkedHashSet retVal = new LinkedHashSet();
        retVal.add(t);

        Class rawClass = ReflectionHelper.getRawClass(t);
        if (rawClass == null) return retVal;
        
        ContractsProvided provided = rawClass.getAnnotation(ContractsProvided.class);
        if (provided != null) {
            // Need to clear the retVal, since even the parent class may not be
            // in the provided set
            retVal.clear();
            
            for (Class providedContract : provided.value()) {
                retVal.add(providedContract);
            }
            
            return retVal;
        }

        Type genericSuperclass = rawClass.getGenericSuperclass();
        while (genericSuperclass != null) {
            Class rawSuperclass = ReflectionHelper.getRawClass(genericSuperclass);
            if (rawSuperclass == null) break;

            if (rawSuperclass.isAnnotationPresent(Contract.class)) {
                retVal.add(genericSuperclass);
            }

            genericSuperclass = rawSuperclass.getGenericSuperclass();
        }

        while (rawClass != null) {
            for (Type iface : rawClass.getGenericInterfaces()) {
                addAllInterfaceContracts(iface, retVal);
            }

            rawClass = rawClass.getSuperclass();
        }

        return retVal;
    }

    /**
     * Creates a reified automatically generated descriptor
     *
     * @param clazz The class to create the desciptor for
     * @param locator The service locator for whom we are creating this
     * @return A reified active descriptor
     *
     * @throws MultiException if there was an error in the class
     * @throws IllegalArgumentException If the class is null
     * @throws IllegalStateException If the name could not be determined from the Named annotation
     */
    public static  AutoActiveDescriptor createAutoDescriptor(Class clazz, ServiceLocatorImpl locator)
            throws MultiException, IllegalArgumentException, IllegalStateException {
        if (clazz == null) throw new IllegalArgumentException();

        Collector collector = new Collector();

        ClazzCreator creator;
        Set qualifiers;
        Set contracts;
        Class scope;
        String name;
        Boolean proxy = null;
        Boolean proxyForSameScope = null;
        String analyzerName;
        
        // Qualifiers naming dance
        String serviceName = null;
        Service serviceAnno = clazz.getAnnotation(Service.class);
        if (serviceAnno != null && !"".equals(serviceAnno.name())) {
            serviceName = serviceAnno.name();
        }
        
        qualifiers = ReflectionHelper.getQualifierAnnotations(clazz);
        name = ReflectionHelper.getNameFromAllQualifiers(qualifiers, clazz);
        
        if (serviceName != null && name != null) {
            // They must match
            if (!serviceName.equals(name)) {
                throw new IllegalArgumentException("The class " + clazz.getName() + " has an @Service name of " + serviceName +
                        " and has an @Named value of " + name +" which names do not match");
            }
        }
        else if (name == null && serviceName != null) {
            name = serviceName;
        }
        
        qualifiers = getAllQualifiers(clazz, name, collector);  // Fixes the @Named qualifier if it has no value

        contracts = getAutoAdvertisedTypes(clazz);
        ScopeInfo scopeInfo = getScopeInfo(clazz, null, collector);
        scope = scopeInfo.getAnnoType();
        analyzerName = getAutoAnalyzerName(clazz);

        creator = new ClazzCreator(locator, clazz);

        collector.throwIfErrors();

        Map> metadata = new HashMap>();
        if (scopeInfo.getScope() != null) {
            BuilderHelper.getMetadataValues(scopeInfo.getScope(), metadata);
        }

        for (Annotation qualifier : qualifiers) {
            BuilderHelper.getMetadataValues(qualifier, metadata);
        }

        UseProxy useProxy = clazz.getAnnotation(UseProxy.class);
        if (useProxy != null) {
            proxy = useProxy.value();
        }

        ProxyForSameScope pfss = clazz.getAnnotation(ProxyForSameScope.class);
        if (pfss != null) {
            proxyForSameScope = pfss.value();
        }

        DescriptorVisibility visibility = DescriptorVisibility.NORMAL;
        Visibility vi = clazz.getAnnotation(Visibility.class);
        if (vi != null) {
            visibility = vi.value();
        }
        
        int rank = 0;
        Rank ranking = clazz.getAnnotation(Rank.class);
        if (ranking != null) {
            rank = ranking.value();
        }

        AutoActiveDescriptor retVal = new AutoActiveDescriptor(
                clazz,
                creator,
                contracts,
                scope,
                name,
                qualifiers,
                visibility,
                rank,
                proxy,
                proxyForSameScope,
                analyzerName,
                metadata);

        creator.initialize(retVal, analyzerName, collector);

        collector.throwIfErrors();

        return retVal;
    }

    /**
     * Pre Destroys the given object
     *
     * @param preMe pre destroys the thing
     * @param locator The non-null service locator associated with the operation (for finding the strategy)
     * @param strategy The strategy to use for analyzing the class
     */
    public static void justPreDestroy(Object preMe, ServiceLocatorImpl locator, String strategy) {
        if (preMe == null) throw new IllegalArgumentException();

        Collector collector = new Collector();

        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();

        collector.throwIfErrors();

        Class baseClass = preMe.getClass();

        Method preDestroy = getPreDestroy(baseClass, analyzer, collector);

        collector.throwIfErrors();

        if (preDestroy == null) return;

        try {
            ReflectionHelper.invoke(preMe, preDestroy, new Object[0], locator.getNeutralContextClassLoader());
        } catch (Throwable e) {
            throw new MultiException(e);
        }
    }

    /**
     * Post constructs the given object
     *
     * @param postMe post constructs the thing
     * @param locator The non-null service locator associated with the operation (for finding the strategy)
     * @param strategy The strategy to use for analyzing the class
     */
    public static void justPostConstruct(Object postMe, ServiceLocatorImpl locator, String strategy) {
        if (postMe == null) throw new IllegalArgumentException();

        Collector collector = new Collector();

        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();

        Class baseClass = postMe.getClass();

        Method postConstruct = getPostConstruct(baseClass, analyzer, collector);

        collector.throwIfErrors();

        if (postConstruct == null) return;

        try {
            ReflectionHelper.invoke(postMe, postConstruct, new Object[0], locator.getNeutralContextClassLoader());
        } catch (Throwable e) {
            throw new MultiException(e);
        }
    }

    /**
     * Just creates the thing, doesn't try to do anything else
     * @param injectMe The object to inject into
     * @param locator The locator to find the injection points with
     * @param strategy The strategy to use for analyzing the class
     */
    public static void justInject(Object injectMe, ServiceLocatorImpl locator, String strategy) {
        if (injectMe == null) throw new IllegalArgumentException();

        Collector collector = new Collector();

        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();

        Class baseClass = injectMe.getClass();

        Set fields = Utilities.getInitFields(baseClass, analyzer, collector);
        Set methods = Utilities.getInitMethods(baseClass, analyzer, collector);

        collector.throwIfErrors();

        for (Field field : fields) {
            InjectionResolver resolver = getInjectionResolver(locator, field);

            List injecteeFields = Utilities.getFieldInjectees(field, null);

            validateSelfInjectees(null, injecteeFields, collector);
            collector.throwIfErrors();

            Injectee injectee = injecteeFields.get(0);

            Object fieldValue = resolver.resolve(injectee, null);

            try {
                ReflectionHelper.setField(field, injectMe, fieldValue);
            }
            catch (Throwable th) {
                throw new MultiException(th);
            }
        }

        for (Method method : methods) {
            List injectees = Utilities.getMethodInjectees(method, null);

            validateSelfInjectees(null, injectees, collector);
            collector.throwIfErrors();

            Object args[] = new Object[injectees.size()];

            for (Injectee injectee : injectees) {
                InjectionResolver resolver = getInjectionResolver(locator, injectee);
                args[injectee.getPosition()] = resolver.resolve(injectee, null);
            }

            try {
                ReflectionHelper.invoke(injectMe, method, args, locator.getNeutralContextClassLoader());
            } catch (Throwable e) {
                throw new MultiException(e);
            }
        }

    }

    /**
     * Just creates the thing, doesn't try to do anything else
     * @param createMe The thing to create
     * @param locator The locator to find the injection points with
     * @param strategy The strategy to use for analyzing the class
     * @return The constructed thing, no further injection is performed
     */
    @SuppressWarnings("unchecked")
    public static  T justCreate(Class createMe, ServiceLocatorImpl locator, String strategy) {
        if (createMe == null) throw new IllegalArgumentException();

        Collector collector = new Collector();
        ClassAnalyzer analyzer = getClassAnalyzer(locator, strategy, collector);
        collector.throwIfErrors();

        Constructor c = getConstructor(createMe, analyzer, collector);

        collector.throwIfErrors();

        List injectees = getConstructorInjectees(c, null);

        validateSelfInjectees(null, injectees, collector);
        collector.throwIfErrors();

        Object args[] = new Object[injectees.size()];

        for (Injectee injectee : injectees) {
            InjectionResolver resolver = getInjectionResolver(locator, injectee);
            args[injectee.getPosition()] = resolver.resolve(injectee, null);
        }

        try {
          return (T) ReflectionHelper.makeMe(c, args, locator.getNeutralContextClassLoader());
        } catch (Throwable th) {
            throw new MultiException(th);
        }
    }

    /**
     * Returns all the interfaces the proxy must implement
     * @param contracts All of the advertised contracts
     * @return The array of contracts to add to the proxy
     */
    public static Class[] getInterfacesForProxy(Set contracts) {
        LinkedList> retVal = new LinkedList>();
        retVal.add(ProxyCtl.class);    // Every proxy implements this interface

        for (Type type : contracts) {
            Class rawClass = ReflectionHelper.getRawClass(type);
            if (rawClass == null) continue;
            if (!rawClass.isInterface()) continue;

            retVal.add(rawClass);
        }

        return retVal.toArray(new Class[retVal.size()]);
    }

    /**
     * Returns true if this scope is proxiable
     *
     * @param scope The scope annotation to test
     * @return true if this must be proxied
     */
    public static boolean isProxiableScope(Class scope) {
        return scope.isAnnotationPresent(Proxiable.class);
    }

    /**
     * Returns true if this scope is unproxiable
     *
     * @param scope The scope annotation to test
     * @return true if this must be proxied
     */
    public static boolean isUnproxiableScope(Class scope) {
        if (scope.isAnnotationPresent(Unproxiable.class)) return true;
        return false;
    }

    /**
     * This method determines whether or not the descriptor should be proxied.
     * The given descriptor must be reified and valid.
     *
     * @param desc A non-null, reified ActiveDescriptor
     * @param injectee The injectee where this is being injected if known,
     * or null if not known
     * @return true if this descriptor must be proxied, false otherwise
     */
    private static boolean isProxiable(ActiveDescriptor desc, Injectee injectee) {
        Boolean directed = desc.isProxiable();

        if (directed != null) {
            if (injectee == null) {
                // No other scope to compare to
                return directed;
            }

            if (!directed) {
                // Doesn't matter what the other scope is, not proxied
                return false;
            }

            ActiveDescriptor injecteeDescriptor = injectee.getInjecteeDescriptor();
            if (injecteeDescriptor == null) {
                // No other scope to compare to
                return true;
            }

            Boolean sameScope = desc.isProxyForSameScope();
            if (sameScope == null || sameScope) {
                // The default case is to be lazy
                return true;
            }

            // OK, same scope is false, forced Proxy is true,
            // now we need to see if the scopes of the two
            // things are in fact the same
            if (desc.getScope().equals(injecteeDescriptor.getScope())) {
                // The scopes are the same, proxy-for-same-scope is false,
                // so the answer is no, do not proxy
                return false;
            }

            // The scopes are different, deal with it
            return true;
        }

        final Class scopeAnnotation = desc.getScopeAnnotation();
        if (!scopeAnnotation.isAnnotationPresent(Proxiable.class)) return false;


        if (injectee == null) {
            // No other scope to compare to
            return true;
        }

        ActiveDescriptor injecteeDescriptor = injectee.getInjecteeDescriptor();
        if (injecteeDescriptor == null) {
            // No other scope to compare to
            return true;
        }

        Proxiable proxiable = scopeAnnotation.getAnnotation(Proxiable.class);
        Boolean proxyForSameScope = desc.isProxyForSameScope();

        if (proxyForSameScope != null) {
            if (proxyForSameScope) {
              return true;
            }
        }
        else if (proxiable == null || proxiable.proxyForSameScope()) {
            // The default case is to be lazy
            return true;
        }

        // OK, same scope is false, and we are in Proxiable scope,
        // now we need to see if the scopes of the two
        // things are in fact the same
        if (desc.getScope().equals(injecteeDescriptor.getScope())) {
            // The scopes are the same, proxy-for-same-scope is false,
            // so the answer is no, do not proxy

            return false;
        }

        // The scopes are different, deal with it
        return true;
    }

    /**
     * Returns the first thing found in the set
     *
     * @param set The set from which to get the first element
     * @return the first thing found in the set
     */
    public static  T getFirstThingInList(List set) {
        for (T t : set) {
            return t;
        }

        return null;
    }

    



    

    /**
     * Returns a constant ActiveDescriptor for the basic ServiceLocator
     *
     * @param locator The service locator to get the ActiveDescriptor for
     * @return An active descriptor specifically for the ServiceLocator
     */
    public static ActiveDescriptor getLocatorDescriptor(ServiceLocator locator) {
        HashSet contracts = new HashSet();
        contracts.add(ServiceLocator.class);

        Set qualifiers = Collections.emptySet();

        ActiveDescriptor retVal =
                new ConstantActiveDescriptor(
                        locator,
                        contracts,
                        PerLookup.class,
                        null,
                        qualifiers,
                        DescriptorVisibility.LOCAL,
                        0,
                        null,
                        null,
                        null,
                        locator.getLocatorId(),
                        null);

        return retVal;
    }

    /**
     * Creates a Three Thirty constant active descriptor
     *
     * @param locator The service locator to get the ActiveDescriptor for
     * @return An active descriptor specifically for the ServiceLocator
     */
    public static ActiveDescriptor> getThreeThirtyDescriptor(
            ServiceLocatorImpl locator) {
        ThreeThirtyResolver threeThirtyResolver = new ThreeThirtyResolver(locator);

        HashSet contracts = new HashSet();

        Type actuals[] = new Type[1];
        actuals[0] = Inject.class;

        contracts.add(new ParameterizedTypeImpl(InjectionResolver.class, actuals));

        Set qualifiers = new HashSet();
        qualifiers.add(new NamedImpl(InjectionResolver.SYSTEM_RESOLVER_NAME));

        ActiveDescriptor> retVal =
                new ConstantActiveDescriptor>(
                        threeThirtyResolver,
                        contracts,
                        Singleton.class,
                        InjectionResolver.SYSTEM_RESOLVER_NAME,
                        qualifiers,
                        DescriptorVisibility.NORMAL,
                        0,
                        null,
                        null,
                        null,
                        locator.getLocatorId(),
                        null);

        return retVal;
    }

    /**
     * Validates the constructors of the annotated type and returns the
     * producer for the annotatedType (if there is no valid producer
     * constructor then this method returns null)
     *
     * @param annotatedType The type to find the producer constructor
     * @param locator The service locator to use when analyzing constructors
     * @param collector The error collector
     * @return The producer constructor or null if the type has no valid
     * producer constructor
     */
    public static Constructor findProducerConstructor(Class annotatedType, ServiceLocatorImpl locator, Collector collector) {
        Constructor zeroArgConstructor = null;
        Constructor aConstructorWithInjectAnnotation = null;

        Set> allConstructors = getAllConstructors(annotatedType);
        for (Constructor constructor : allConstructors) {

            Type rawParameters[] = constructor.getGenericParameterTypes();
            if (rawParameters.length <= 0) {
                zeroArgConstructor = constructor;
            }

            if (hasInjectAnnotation(locator, constructor, true)) {
                if (aConstructorWithInjectAnnotation != null) {
                    collector.addThrowable(new IllegalArgumentException("There is more than one constructor on class " +
                            Pretty.clazz(annotatedType)));
                    return null;
                }

                aConstructorWithInjectAnnotation = constructor;
            }

            if (!isProperConstructor(constructor)) {
                collector.addThrowable(new IllegalArgumentException("The constructor for " +
                        Pretty.clazz(annotatedType) + " may not have an annotation as a parameter"));
                return null;

            }

        }

        if (aConstructorWithInjectAnnotation != null) {
            return aConstructorWithInjectAnnotation;
        }

        if (zeroArgConstructor == null) {
            collector.addThrowable(new NoSuchMethodException("The class " + Pretty.clazz(annotatedType) +
                    " has no constructor marked @Inject and no zero argument constructor"));
            return null;
        }

        return zeroArgConstructor;
    }

    private static boolean isProperConstructor(Constructor c) {
        for (Class pClazz : c.getParameterTypes()) {
            if (pClazz.isAnnotation()) return false;
        }

        return true;
    }

    /**
     * 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];
    }

    /**
     * Gets all the constructors for a given class
     *
     * @param clazz The class to find the constructors of
     * @return A set of Constructors for the given class
     */
    private static Set> getAllConstructors(final Class clazz) {
        HashSet> retVal = new LinkedHashSet>();
        
        Constructor constructors[] = AccessController.doPrivileged(new PrivilegedAction[]>() {

            @Override
            public Constructor[] run() {
                return clazz.getDeclaredConstructors();
            }

        });

        for (Constructor constructor : constructors) {
            retVal.add(constructor);
        }

        return retVal;
    }

    

    

    /**
     * Get all the initializer methods of the annotatedType.  If there are definitional
     * errors they will be put into the errorCollector (so as to get all the errors
     * at one shot)
     *
     * @param annotatedType The type to find the errors in
     * @param locator The locator to use when analyzing methods
     * @param errorCollector The collector to add errors to
     * @return A possibly empty but never null set of initializer methods
     */
    public static Set findInitializerMethods(
            Class annotatedType,
            ServiceLocatorImpl locator,
            Collector errorCollector) {
        LinkedHashSet retVal = new LinkedHashSet();
        ClassReflectionHelper crh = locator.getClassReflectionHelper();

        for (MethodWrapper methodWrapper : crh.getAllMethods(annotatedType)) {
            Method method = methodWrapper.getMethod();
            
            if (!hasInjectAnnotation(locator, method, true)) {
                // Not an initializer method
                continue;
            }

            if (!isProperMethod(method)) {
                errorCollector.addThrowable(new IllegalArgumentException(
                        "An initializer method " + Pretty.method(method) +
                                " is static, abstract or has a parameter that is an annotation"));
                continue;
            }

            retVal.add(method);
        }

        return retVal;
    }
    
    /**
     * Finds the post construct method on this class
     * @param clazz The class to search for the post construct
     * @param collector An error collector
     * @return The post construct method or null
     */
    public static Method findPostConstruct(Class clazz, ServiceLocatorImpl locator, Collector collector) {
        try {
            return locator.getClassReflectionHelper().findPostConstruct(clazz, org.glassfish.hk2.api.PostConstruct.class);
        }
        catch (IllegalArgumentException iae) {
            collector.addThrowable(iae);
            return null;
        }
    }
    
    /**
     * Finds the pre destroy method on this class
     * @param clazz The class to search for the pre destroy method
     * @param collector An error collector
     * @return The pre destroy method or null
     */
    public static Method findPreDestroy(Class clazz, ServiceLocatorImpl locator, Collector collector)  {
        try {
            return locator.getClassReflectionHelper().findPreDestroy(clazz, org.glassfish.hk2.api.PreDestroy.class);
        }
        catch (IllegalArgumentException iae) {
            collector.addThrowable(iae);
            return null;
        }
    }

    /**
     * Will find all the initialize fields in the class
     *
     * @param annotatedType The class to search for fields
     * @param locator The locator to use when analyzing the class
     * @param errorCollector The error collector
     * @return A non-null but possibly empty set of initializer fields
     */
    public static Set findInitializerFields(Class annotatedType,
                                                   ServiceLocatorImpl locator,
                                                   Collector errorCollector) {
        LinkedHashSet retVal = new LinkedHashSet();
        ClassReflectionHelper crh = locator.getClassReflectionHelper();
        
        Set fields = crh.getAllFields(annotatedType);

        for (Field field : fields) {
            if (!hasInjectAnnotation(locator, field, false)) {
                // Not an initializer field
                continue;
            }

            if (!isProperField(field)) {
                errorCollector.addThrowable(new IllegalArgumentException("The field " +
                        Pretty.field(field) + " may not be static, final or have an Annotation type"));
                continue;
            }

            retVal.add(field);
        }

        return retVal;
    }

    /**
     * Checks whether an annotated element has any annotation that was used for the injection
     *
     * @param locator The service locator to use (as it will get all
     * the annotations that were added on as well as the normal Inject)
     * @param annotated  the annotated element
     * @param checkParams  check the params if true
     * @return True if element contains at least one inject annotation
     */
    private static boolean hasInjectAnnotation(ServiceLocatorImpl locator, AnnotatedElement annotated, boolean checkParams) {
        for (Annotation anno : annotated.getAnnotations()) {
            if (locator.isInjectAnnotation(anno)) {
                return true;
            }
        }

        if (!checkParams) return false;

        boolean isConstructor;
        Annotation allAnnotations[][];
        if (annotated instanceof Method) {
            Method m = (Method) annotated;

            isConstructor = false;
            allAnnotations = m.getParameterAnnotations();
        } else if (annotated instanceof Constructor) {
            Constructor c = (Constructor) annotated;

            isConstructor = true;
            allAnnotations = c.getParameterAnnotations();
        } else {
            return false;
        }

        for (Annotation allParamAnnotations[] : allAnnotations) {
            for (Annotation paramAnno : allParamAnnotations) {
                if (locator.isInjectAnnotation(paramAnno, isConstructor)) {
                    return true;
                }
            }
        }

        return false;
    }

    private static class AnnotatedElementAnnotationInfo {
        private final Annotation[] elementAnnotations;
        private final Annotation[][] paramAnnotations;
        private final boolean hasParams;
        private final boolean isConstructor;

        private AnnotatedElementAnnotationInfo(Annotation[] elementAnnotation, boolean hasParams, Annotation[][] paramAnnotation, boolean isConstructor) {
            this.elementAnnotations = elementAnnotation;
            this.hasParams = hasParams;
            this.paramAnnotations = paramAnnotation;
            this.isConstructor = isConstructor;
        }
        
        private SoftAnnotatedElementAnnotationInfo soften() {
            return new SoftAnnotatedElementAnnotationInfo(elementAnnotations, hasParams, paramAnnotations, isConstructor);
        }
    }
    
    private static class SoftAnnotatedElementAnnotationInfo {
        private final SoftReference elementAnnotationsReference;
        private final SoftReference paramAnnotationsReference;
        private final boolean hasParams;
        private final boolean isConstructor;

        private SoftAnnotatedElementAnnotationInfo(Annotation[] elementAnnotation, boolean hasParams, Annotation[][] paramAnnotation, boolean isConstructor) {
            this.elementAnnotationsReference = new SoftReference(elementAnnotation);
            this.hasParams = hasParams;
            this.paramAnnotationsReference = new SoftReference(paramAnnotation);
            this.isConstructor = isConstructor;
        }
        
        private AnnotatedElementAnnotationInfo harden(AnnotatedElement ae) {
            Annotation[] hardenedElementAnnotations = elementAnnotationsReference.get();
            Annotation[][] hardenedParamAnnotations = paramAnnotationsReference.get();
            
            if (!USE_SOFT_REFERENCE || (hardenedElementAnnotations == null) || (hardenedParamAnnotations == null)) {
                return computeAEAI(ae);
            }
            
            return new AnnotatedElementAnnotationInfo(hardenedElementAnnotations, hasParams, hardenedParamAnnotations, isConstructor);
        }
    }
    
    /**
     * Represents a cache miss, and will fetch all of the information needed about the
     * AnnotatedElement in order to quickly determine what its resolver would be
     * 
     * @param annotatedElement The raw annotated element that can be used to
     * calculate the information needed to determine the resolver
     * @return An annotated element constructed from the information in the annotatedElement
     */
    private static AnnotatedElementAnnotationInfo computeAEAI(AnnotatedElement annotatedElement) {

        if (annotatedElement instanceof Method) {

            final Method m = (Method) annotatedElement;
            return new AnnotatedElementAnnotationInfo(m.getAnnotations(), true, m.getParameterAnnotations(), false);

        } else if (annotatedElement instanceof Constructor) {

            final Constructor c = (Constructor) annotatedElement;
            return new AnnotatedElementAnnotationInfo(c.getAnnotations(), true, c.getParameterAnnotations(), true);

        } else {
            return new AnnotatedElementAnnotationInfo(annotatedElement.getAnnotations(), false, new Annotation[0][], false);
        }
    }
    
    private static AnnotatedElementAnnotationInfo computeElementAnnotationInfo(AnnotatedElement ae) {
        synchronized (lock) {
            AnnotatedElementAnnotationInfo hard;
            SoftAnnotatedElementAnnotationInfo soft = annotationCache.get(ae);
            if (soft != null) {
                hard = soft.harden(ae);
            }
            else {
                hard = computeAEAI(ae);
                
                soft = hard.soften();
                annotationCache.put(ae, soft);
            }
            
            return hard;
        }
    }

    /**
     * Gets the annotation that was used for the injection
     *
     * @param locator The service locator to use (as it will get all
     * the annotations that were added on as well as the normal Inject)
     * @param annotated the annotated annotated
     * @param checkParams  check the params if true
     * @param position index of constructor or method parameter which which will be checked
     *                 for inject annotations. The {@code position} parameter is only used when
     *                 {@code annotated} is method or constructor otherwise the value will be ignored.
     * @return The annotation that is the inject annotation, or null
     * if no inject annotation was found
     */
    private static Annotation getInjectAnnotation(final ServiceLocatorImpl locator, final AnnotatedElement annotated,
            final boolean checkParams, final int position) {
        
        final AnnotatedElementAnnotationInfo annotationInfo = computeElementAnnotationInfo(annotated);

        if (checkParams) {

            if (annotationInfo.hasParams) {
                for (Annotation paramAnno : annotationInfo.paramAnnotations[position]) {
                    if (locator.isInjectAnnotation(paramAnno, annotationInfo.isConstructor)) {
                        return paramAnno;
                    }
                }
            }
        }

        for (Annotation annotation : annotationInfo.elementAnnotations) {
            if (locator.isInjectAnnotation(annotation)) {
                return annotation;
            }
        }

        return null;
    }

  private static boolean isProperMethod(Method member) {
        if (ReflectionHelper.isStatic(member)) return false;
        if (isAbstract(member)) return false;
        for (Class paramClazz : member.getParameterTypes()) {
            if (paramClazz.isAnnotation()) {
                return false;
            }
        }

        return true;
    }

    private static boolean isProperField(Field field) {
        if (ReflectionHelper.isStatic(field)) return false;
        if (isFinal(field)) return false;
        Class type = field.getType();
        return !type.isAnnotation();
    }

    

    /**
     * Returns true if the underlying member is abstract
     *
     * @param member The non-null member to test
     * @return true if the member is abstract
     */
    public static boolean isAbstract(Member member) {
        int modifiers = member.getModifiers();

        return ((modifiers & Modifier.ABSTRACT) != 0);
    }

    /**
     * Returns true if the underlying member is abstract
     *
     * @param member The non-null member to test
     * @return true if the member is abstract
     */
    public static boolean isFinal(Member member) {
        int modifiers = member.getModifiers();

        return ((modifiers & Modifier.FINAL) != 0);
    }
    
    private static boolean isFinal(Class clazz) {
        int modifiers = clazz.getModifiers();

        return ((modifiers & Modifier.FINAL) != 0);
    }

    /**
     * Gets the analyzer name from the Service annotation
     *
     * @param c The class to get the analyzer name from
     * @return The name of the analyzer (null for default)
     */
    public static String getAutoAnalyzerName(Class c) {
        synchronized (lock) {
            String retVal = autoAnalyzerNameCache.get(c);
            if (retVal != null) return retVal;
            
            Service s = c.getAnnotation(Service.class);
            if (s == null) return null;
            
            retVal = s.analyzer();
            autoAnalyzerNameCache.put(c, retVal);

            return retVal;
        }
    }

    @SuppressWarnings("unchecked")
    private static ScopeInfo getScopeInfo(
            AnnotatedElement annotatedGuy,
            Descriptor defaultScope,
            Collector collector) {
        AnnotatedElement topLevelElement = annotatedGuy;

        Annotation winnerScope = null;
        while (annotatedGuy != null) {
            Annotation current = internalGetScopeAnnotationType(
                    annotatedGuy,
                    collector);
            if (current != null) {
                if (annotatedGuy.equals(topLevelElement)) {
                    // We found a winner, no matter the inherited state
                    winnerScope = current;
                    break;
                }
                
                if (current.annotationType().isAnnotationPresent(Inherited.class)) {
                    winnerScope = current;
                    break;
                }

                // This non-inherited annotation wipes out all scopes above it
                break;
            }

            if (annotatedGuy instanceof Class) {
                annotatedGuy = ((Class) annotatedGuy).getSuperclass();
            } else {
                Method theMethod = (Method) annotatedGuy;
                Class methodClass = theMethod.getDeclaringClass();

                annotatedGuy = null;
                Class methodSuperclass = methodClass.getSuperclass();
                while (methodSuperclass != null) {
                    if (Factory.class.isAssignableFrom(methodSuperclass)) {
                        annotatedGuy = getFactoryProvideMethod(methodSuperclass);
                        break;
                    }

                    methodSuperclass = methodSuperclass.getSuperclass();
                }
            }
        }

        if (winnerScope != null) {
            return new ScopeInfo(winnerScope, winnerScope.annotationType());
        }


        if (topLevelElement.isAnnotationPresent(Service.class)) {
            return new ScopeInfo(null, Singleton.class);
        }

        if (defaultScope != null && defaultScope.getScope() != null) {
            Class descScope = (Class)
                    loadClass(defaultScope.getScope(), defaultScope, collector);
            if (descScope != null) {
                return new ScopeInfo(null, descScope);
            }
        }

        return new ScopeInfo(null, PerLookup.class);

    }

    /**
     * Returns the scope of this thing
     *
     * @param fromThis The annotated class or producer method
     * @param defaultScope The default scope if none other can be found
     * @return The scope of this class or producer method.  If no scope is
     * found will return the dependent scope
     */
    public static Class getScopeAnnotationType(
            Class fromThis,
            Descriptor defaultScope) {
        Collector collector = new Collector();

        ScopeInfo si = getScopeInfo(fromThis, defaultScope, collector);

        collector.throwIfErrors();

        return si.getAnnoType();
    }

    /**
     * Returns the scope of this thing
     *
     * @param annotatedGuy The annotated class or producer method
     * @param defaultScope The default scope if none other can be found
     * @param collector The error collector
     * @return The scope of this class or producer method.  If no scope is
     * found will return the dependent scope
     */
    public static Class getScopeAnnotationType(
            AnnotatedElement annotatedGuy,
            Descriptor defaultScope,
            Collector collector) {
        ScopeInfo si = getScopeInfo(annotatedGuy, defaultScope, collector);
        return si.getAnnoType();
    }

    /**
     * This returns the scope annotation on this class *itself*, and no other
     * classes (like, not subclasses).
     */
    private static Annotation internalGetScopeAnnotationType(
            AnnotatedElement annotatedGuy,
            Collector collector) {
        boolean epicFail = false;
        Annotation retVal = null;
        for (Annotation annotation : annotatedGuy.getDeclaredAnnotations()) {
            if (annotation.annotationType().isAnnotationPresent(Scope.class)) {
                if (retVal != null) {
                    collector.addThrowable(new IllegalArgumentException("The type " + annotatedGuy +
                            " may not have more than one scope.  It has at least " +
                            Pretty.clazz(retVal.annotationType()) +
                            " and " + Pretty.clazz(annotation.annotationType())));
                    epicFail = true;
                    continue;
                }

                retVal = annotation;
            }
        }

        if (epicFail) return null;

        return retVal;
    }

    /**
     * Returns an injection resolver for the injectee
     *
     * @param locator The locator to use when finding the resolver
     * @param injectee Injectee from which the annotation should be extracted
     * @return Injection resolver used to resolve the injection for the injectee
     * @throws IllegalStateException If we could not find a valid resolver
     */
    public static InjectionResolver getInjectionResolver(
            ServiceLocatorImpl locator, Injectee injectee) throws IllegalStateException {
        return getInjectionResolver(locator, injectee.getParent(), injectee.getPosition());

    }

    private static InjectionResolver getInjectionResolver(
            ServiceLocatorImpl locator, AnnotatedElement annotatedGuy, int position) throws IllegalStateException {
        boolean methodOrConstructor = annotatedGuy instanceof Method || annotatedGuy instanceof Constructor;
        Annotation injectAnnotation = getInjectAnnotation(locator, annotatedGuy, methodOrConstructor, position);

        //Annotation injectAnnotation = getInjectAnnotation(locator, annotatedGuy, position);

        Class injectType = (injectAnnotation == null) ?
                Inject.class : injectAnnotation.annotationType();

        InjectionResolver retVal = locator.getInjectionResolver(injectType);
        if (retVal == null) {
            // Not possible to get here, we only are here if we already found a resolver
            throw new IllegalStateException("There is no installed injection resolver for " +
                    Pretty.clazz(injectType) + " for type " + annotatedGuy);
        }

        return retVal;
    }

    /**
     * Returns an injection resolver for this AnnotatedElement. The method cannot be used for constructors
     * or methods.
     *
     * @param locator The locator to use when finding the resolver
     * @param annotatedGuy The annotated class or producer method
     * @return The scope of this class or producer method.  If no scope is
     * found will return the dependent scope
     * @throws IllegalStateException If we could not find a valid resolver
     */
    private static InjectionResolver getInjectionResolver(
            ServiceLocatorImpl locator, AnnotatedElement annotatedGuy) throws IllegalStateException {
        if (annotatedGuy instanceof Method || annotatedGuy instanceof Constructor) {
            throw new IllegalArgumentException("Annotated element '" + annotatedGuy + "' can be neither a Method nor a Constructor.");
        }
        return getInjectionResolver(locator, annotatedGuy, -1);
    }

    private final static String PROVIDE_METHOD = "provide";

    /**
     * This method will retrieve the provide method from a Factory
     *
     * @param clazz This class must implement factory
     * @return The provide method from this class
     */
    public static Method getFactoryProvideMethod(Class clazz) {
        try {
            return clazz.getMethod(PROVIDE_METHOD);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    /**
     * Returns the default name if one can be found.  Will only work on
     * classes and methods
     *
     * @param parent The parent annotated element
     * @param collector For errors
     * @return null if there is no default name (no Named)
     */
    public static String getDefaultNameFromMethod(Method parent, Collector collector) {
        Named named = parent.getAnnotation(Named.class);
        if (named == null) {
            return null;
        }

        if (named.value() == null || named.value().equals("")) {
            collector.addThrowable(new IllegalArgumentException(
                    "@Named on the provide method of a factory must have an explicit value"));
        }

        return named.value();
    }

    /**
     * Returns the full set of qualifier annotations on this class
     *
     * @param annotatedGuy The element we are searching for qualifiers
     * @param name The name this element must have
     * @param collector The error collector
     * @return A non-null but possibly empty set of qualifiers
     */
    public static Set getAllQualifiers(
            AnnotatedElement annotatedGuy,
            String name,
            Collector collector) {

        Named namedQualifier = null;
        Set retVal = ReflectionHelper.getQualifierAnnotations(annotatedGuy);
        for (Annotation anno : retVal) {
            if (anno instanceof Named) {
                namedQualifier = (Named) anno;
                break;
            }
        }

        if (name == null) {
            if (namedQualifier != null) {
                collector.addThrowable(new IllegalArgumentException("No name was in the descriptor, but this element(" +
                        annotatedGuy + " has a Named annotation with value: " + namedQualifier.value()));

                retVal.remove(namedQualifier);
            }

            return retVal;
        }

        if (namedQualifier == null || namedQualifier.value().equals("")) {
            if (namedQualifier != null) {
                retVal.remove(namedQualifier);
            }

            namedQualifier = new NamedImpl(name);

            retVal.add(namedQualifier);
        }

        if (!name.equals(namedQualifier.value())) {
            collector.addThrowable(new IllegalArgumentException("The class had an @Named qualifier that was inconsistent." +
                    "  The expected name is " + name +
                    " but the annotation has name " + namedQualifier.value()));
        }

        return retVal;
    }
    
    private static AnnotationInformation getParamInformation(Annotation memberAnnotations[]) {
        boolean useDefault = true;
        
        Set qualifiers = null;
        boolean optional = false;
        boolean self = false;
        Unqualified unqualified = null;
        
        for (Annotation anno : memberAnnotations) {
            if (ReflectionHelper.isAnnotationAQualifier(anno)) {
                if (qualifiers == null) qualifiers = new HashSet();
                qualifiers.add(anno);
                useDefault = false;
            }
            else if (Optional.class.equals(anno.annotationType())) {
                optional = true;
                useDefault = false;
            }
            else if (Self.class.equals(anno.annotationType())) {
                self = true;
                useDefault = false;
            }
            else if (Unqualified.class.equals(anno.annotationType())) {
                unqualified = (Unqualified) anno;
                useDefault = false;
            }
        }
        
        if (useDefault) return DEFAULT_ANNOTATION_INFORMATION;
        
        if (qualifiers == null) qualifiers = DEFAULT_ANNOTATION_INFORMATION.qualifiers;
        
        return new AnnotationInformation(
                qualifiers,
                optional,
                self,
                unqualified);
    }

    /**
     * Returns all the injectees for a constructor
     * @param c The constructor to analyze
     * @param injecteeDescriptor The descriptor of the injectee
     * @return the list (in order) of parameters to the constructor
     */
    public static List getConstructorInjectees(Constructor c, ActiveDescriptor injecteeDescriptor) {
        Type genericTypeParams[] = c.getGenericParameterTypes();
        Annotation paramAnnotations[][] = c.getParameterAnnotations();

        List retVal = new LinkedList();

        for (int lcv = 0; lcv < genericTypeParams.length; lcv++) {
            AnnotationInformation ai = getParamInformation(paramAnnotations[lcv]);
            
            retVal.add(new InjecteeImpl(genericTypeParams[lcv],
                    ai.qualifiers,
                    lcv,
                    c,
                    ai.optional,
                    ai.self,
                    ai.unqualified,
                    injecteeDescriptor));
        }

        return retVal;
    }

    /**
     * Returns all the injectees for a constructor
     * @param c The constructor to analyze
     * @param injecteeDescriptor The descriptor of the injectee
     * @return the list (in order) of parameters to the constructor
     */
    public static List getMethodInjectees(Method c, ActiveDescriptor injecteeDescriptor) {
        Type genericTypeParams[] = c.getGenericParameterTypes();
        Annotation paramAnnotations[][] = c.getParameterAnnotations();

        List retVal = new LinkedList();

        for (int lcv = 0; lcv < genericTypeParams.length; lcv++) {
            AnnotationInformation ai = getParamInformation(paramAnnotations[lcv]);
            
            retVal.add(new InjecteeImpl(genericTypeParams[lcv],
                    ai.qualifiers,
                    lcv,
                    c,
                    ai.optional,
                    ai.self,
                    ai.unqualified,
                    injecteeDescriptor));
        }

        return retVal;
    }

    private static Set getFieldAdjustedQualifierAnnotations(Field f) {
        Set unadjustedAnnotations = ReflectionHelper.getQualifierAnnotations(f);

        // The getQualifierAnnotations will NOT add a Named annotation that has no
        // value.  So we must now determine if that is the case, and if so add
        // our own NamedImpl based on the name of the field
        Named n = f.getAnnotation(Named.class);
        if (n == null) return unadjustedAnnotations;

        if (n.value() == null || "".equals(n.value())) {
            unadjustedAnnotations.add(new NamedImpl(f.getName()));
        }

        return unadjustedAnnotations;
    }

    /**
     * Returns the injectees for a field
     * @param f The field to analyze
     * @param injecteeDescriptor The descriptor of the injectee
     * @return the list (in order) of parameters to the constructor
     */
    public static List getFieldInjectees(Field f, ActiveDescriptor injecteeDescriptor) {
        List retVal = new LinkedList();
        AnnotationInformation ai = getParamInformation(f.getAnnotations());

        retVal.add(new InjecteeImpl(f.getGenericType(),
                getFieldAdjustedQualifierAnnotations(f),
                -1,
                f,
                ai.optional,
                ai.self,
                ai.unqualified,
                injecteeDescriptor));

        return retVal;
    }

    /**
     * This method validates a list of injectees to ensure that any self injectees have
     * the proper set of requirements.  It adds IllegalArgumentExceptions to the collector
     * if it finds errors
     *
     * @param givenDescriptor The descriptor associated with this injectee, or null if there are none
     * @param injectees The list of injectees to check.  Only self injectees are validates
     * @param collector The collector to add any errors to
     */
    public static void validateSelfInjectees(ActiveDescriptor givenDescriptor,
                                             List injectees,
                                             Collector collector) {

        for (Injectee injectee : injectees) {
            if (!injectee.isSelf()) continue;

            Class requiredRawClass = ReflectionHelper.getRawClass(injectee.getRequiredType());
            if (requiredRawClass == null || !(ActiveDescriptor.class.equals(requiredRawClass))) {
                collector.addThrowable(new IllegalArgumentException("Injection point " + injectee +
                        " does not have the required type of ActiveDescriptor"));
            }

            if (injectee.isOptional()) {
                collector.addThrowable(new IllegalArgumentException("Injection point " + injectee +
                        " is marked both @Optional and @Self"));
            }

            if (!injectee.getRequiredQualifiers().isEmpty()) {
                collector.addThrowable(new IllegalArgumentException("Injection point " + injectee +
                        " is marked @Self but has other qualifiers"));
            }

            if (givenDescriptor == null) {
                collector.addThrowable(new IllegalArgumentException("A class with injection point " + injectee +
                        " is being created or injected via the non-managed ServiceLocator API"));
            }
        }

    }



    

    /**
     * This method returns a set of qualifiers from an array of qualifiers.
     *
     * TODO  It can also do some sanity checking here (i.e., multiple
     * qualifiers of the same type, that sort of thing)
     *
     * @param qualifiers The qualifiers to convert.  May not be null, but
     * may be zero length
     * @param name The name this set of qualifiers must have
     * @return The set containing all the qualifiers
     */
    public static Set fixAndCheckQualifiers(Annotation qualifiers[], String name) {
        Set retVal = new HashSet();

        Set dupChecker = new HashSet();
        Named named = null;
        for (Annotation qualifier : qualifiers) {
            String annotationType = qualifier.annotationType().getName();
            if (dupChecker.contains(annotationType)) {
                throw new IllegalArgumentException(annotationType + " appears more than once in the qualifier list");
            }
            dupChecker.add(annotationType);

            retVal.add(qualifier);
            if (qualifier instanceof Named) {
                named = (Named) qualifier;

                if (named.value().equals("")) {
                    throw new IllegalArgumentException("The @Named qualifier must have a value");
                }

                if (name != null && !name.equals(named.value())) {
                    throw new IllegalArgumentException("The name passed to the method (" +
                            name + ") does not match the value of the @Named qualifier (" + named.value() + ")");
                }
            }
        }

        if (named == null && name != null) {
            retVal.add(new NamedImpl(name));
        }

        return retVal;
    }

    /**
     * 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;
    }

    /**
     * Creates the service (without the need for an intermediate ServiceHandle
     * to be created)
     *
     * @param root The ultimate parent of this operation
     * @param injectee the injectee we are creating this service for
     * @param locator The locator to use to find services
     * @param handle The ServiceHandle (or null if there is none)
     * @param requestedClass The class for the service we are looking for
     * @return The created service
     */
    @SuppressWarnings("unchecked")
    public static  T createService(ActiveDescriptor root,
            Injectee injectee,
            ServiceLocatorImpl locator,
            ServiceHandle handle,
            Class requestedClass) {
        if (root == null) throw new IllegalArgumentException();

        T service = null;

        if (!root.isReified()) {
            root = (ActiveDescriptor) locator.reifyDescriptor(root, injectee);
        }

        if (isProxiable(root, injectee)) {
            if (!proxiesAvailable()) {
                throw new IllegalStateException("A descriptor " + root + " requires a proxy, but the proxyable library is not on the classpath");
            }
            
            return ProxyUtilities.generateProxy(requestedClass, locator, root, handle);
        }

        Context context;
        try {
            context = locator.resolveContext(root.getScopeAnnotation());
        }
        catch (Throwable th) {
            if (injectee != null && injectee.isOptional()) return null;
            
            Exception addMe = new IllegalStateException("While attempting to create a service for " + root +
                    " in scope " + root.getScope() + " an error occured while locating the context");

            if (th instanceof MultiException) {
                MultiException me = (MultiException) th;

                me.addError(addMe);

                throw me;
            }

            MultiException me = new MultiException(th);
            me.addError(addMe);
            throw me;
        }

        try {
            service = context.findOrCreate(root, handle);
        }
        catch (MultiException me) {
            throw me;
        }
        catch (Throwable th) {
            throw new MultiException(th);
        }

        if (service == null && !context.supportsNullCreation()) {
            throw new MultiException(new IllegalStateException("Context " +
                context + " findOrCreate returned a null for descriptor " + root +
                " and handle " + handle));
        }

        return service;
    }

    

    private static final HashSet NOT_INTERCEPTED = new HashSet();
    
    static {
        NOT_INTERCEPTED.add(ServiceLocator.class.getName());
        NOT_INTERCEPTED.add(InstanceLifecycleListener.class.getName());
        NOT_INTERCEPTED.add(InjectionResolver.class.getName());
        NOT_INTERCEPTED.add(ErrorService.class.getName());
        NOT_INTERCEPTED.add(ClassAnalyzer.class.getName());
        NOT_INTERCEPTED.add(DynamicConfigurationListener.class.getName());
        NOT_INTERCEPTED.add(DynamicConfigurationService.class.getName());
        NOT_INTERCEPTED.add(InterceptionService.class.getName());
        NOT_INTERCEPTED.add(ValidationService.class.getName());
        NOT_INTERCEPTED.add(Context.class.getName());
    }
    
    private final static Interceptors EMTPY_INTERCEPTORS = new Interceptors() {

        @Override
        public Map> getMethodInterceptors() {
            return null;
        }

        @Override
        public List getConstructorInterceptors() {
            return null;
        }
        
    };
    
    /* package */ static Interceptors getAllInterceptors(
            ServiceLocatorImpl impl,
            ActiveDescriptor descriptor,
            Class clazz,
            Constructor c) {
        if (descriptor == null || clazz == null || isFinal(clazz)) return EMTPY_INTERCEPTORS;
        ClassReflectionHelper crh = impl.getClassReflectionHelper();
        
        List interceptionServices = impl.getInterceptionServices();
        if (interceptionServices == null || interceptionServices.isEmpty()) return EMTPY_INTERCEPTORS;
        
        // Make sure it is not one of the special services
        for (String contract : descriptor.getAdvertisedContracts()) {
            if (NOT_INTERCEPTED.contains(contract)) return EMTPY_INTERCEPTORS;
        }
        
        final LinkedHashMap> retVal =
                new LinkedHashMap>();
        final ArrayList cRetVal = new ArrayList();
        
        for (InterceptionService interceptionService : interceptionServices) {
            Filter filter = interceptionService.getDescriptorFilter();
            if (filter instanceof IndexedFilter) {
                IndexedFilter indexedFilter = (IndexedFilter) filter;
                
                String indexedContract = indexedFilter.getAdvertisedContract();
                if (indexedContract != null) {
                    if (!descriptor.getAdvertisedContracts().contains(indexedContract)) continue;
                }
                String name = indexedFilter.getName();
                if (name != null) {
                    if (descriptor.getName() == null) continue;
                    if (!descriptor.getName().equals(name)) continue;
                }
            }
            
            if (filter.matches(descriptor)) {
                for (MethodWrapper methodWrapper : crh.getAllMethods(clazz)) {
                    Method method = methodWrapper.getMethod();
                    
                    if (isFinal(method)) continue;
                    
                    List interceptors = interceptionService.getMethodInterceptors(method);
                    if (interceptors != null && !interceptors.isEmpty()) {
                        List addToMe = retVal.get(method);
                        if (addToMe == null) {
                            addToMe = new ArrayList();
                            retVal.put(method, addToMe);
                        }
                        
                        addToMe.addAll(interceptors);
                    }
                }
                
                List cInterceptors = interceptionService.getConstructorInterceptors(c);
                if (cInterceptors != null && !cInterceptors.isEmpty()) {
                    cRetVal.addAll(cInterceptors);
                }
            }
        }
        
        return new Interceptors() {

            @Override
            public Map> getMethodInterceptors() {
                return retVal;
            }

            @Override
            public List getConstructorInterceptors() {
                return cRetVal;
            }
            
        };
    }
    
    /**
     * This code uses the TypeChecker but does some extra checking if
     * the types are annotations
     * 
     * @param requiredType The type this must conform to
     * @param beanType The type of the bean we are checking
     * @return true if beanType is safely assignable to requiredType
     */
    @SuppressWarnings("unchecked")
    public static boolean isTypeSafe(Type requiredType, Type beanType) {
        if (TypeChecker.isRawTypeSafe(requiredType, beanType)) return true;
        
        Class requiredClass = ReflectionHelper.getRawClass(requiredType);
        if (requiredClass == null) {
            return false;
        }
        
        // We do some extra checking if we are looking at annotations
        if (!requiredClass.isAnnotation()) return false;
        
        Class beanClass = ReflectionHelper.getRawClass(beanType);
        if (beanClass == null) {
            return false;
        }
        
        if (beanClass.isAnnotationPresent((Class) requiredClass)) {
            return true;
        }
                
         Class trueScope = Utilities.getScopeAnnotationType(beanClass, null);
         if (trueScope.equals((Class) requiredClass)) {
             return true;
         }
         
         return false;
    }
    
    private static Boolean proxiesAvailable = null;
    
    /**
     * Returns true if the system can create proxies, false otherwise
     * 
     * @return true if the system can create proxies, false otherwise
     */
    public synchronized static boolean proxiesAvailable() {
        if (proxiesAvailable != null) {
            return proxiesAvailable;
        }
        
        ClassLoader loader = Utilities.class.getClassLoader();
        if (loader == null) {
            loader = ClassLoader.getSystemClassLoader();
        }
        
        try {
            loader.loadClass("javassist.util.proxy.MethodHandler");
            proxiesAvailable = true;
            return true;
        }
        catch (Throwable th) {
            proxiesAvailable = false;
            return false;
        }
    }
    
    /**
     * The return type from getAllInterceptors
     * 
     * @author jwells
     *
     */
    public interface Interceptors {
        /**
         * Gets the method interceptors
         * @return The possibly null set of method interceptors
         */
        public Map> getMethodInterceptors();
        
        /**
         * Gets the constructor interceptors
         * @return The possibly null set of constructor interceptors
         */
        public List getConstructorInterceptors();
    }
    
    private static class AnnotationInformation {
        private final Set qualifiers;
        private final boolean optional;
        private final boolean self;
        private final Unqualified unqualified;
        
        private AnnotationInformation(Set qualifiers,
                boolean optional,
                boolean self,
                Unqualified unqualified) {
            this.qualifiers = qualifiers;
            this.optional = optional;
            this.self = self;
            this.unqualified = unqualified;
        }
    }
}