/**
 * OW2 Spec
 * Copyright (C) 2010 Bull S.A.S.
 * Contact: easybeans@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: BeanManager.java 5375 2010-02-25 17:25:09Z sauthieg $
 * --------------------------------------------------------------------------
 */

package javax.enterprise.inject.spi;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Set;

import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.AmbiguousResolutionException;
import javax.enterprise.inject.InjectionException;
import javax.enterprise.inject.UnsatisfiedResolutionException;

/**
 * Allows a portable extension to interact directly with the container. Provides operations
 * for obtaining contextual references for beans, along with many other operations of
 * use to portable extensions.
 *
 * Any bean may obtain an instance of BeanManager by injecting it:
 * <pre>
 &#064;Inject BeanManager manager;
 * </pre>
 *
 * Java EE components may obtain an instance of {@code BeanManager} from JNDI by looking
 * up the name {@code java:comp/BeanManager}.
 *
 * Any operation of {@code BeanManager} may be called at any time during the execution of the application.
 */
public interface BeanManager {

    /**
     * Obtains a contextual reference for a certain bean and a certain bean type of the bean.
     *
     * @param bean the Bean object representing the bean
     * @param beanType a bean type that must be implemented by any client proxy that is returned
     * @param ctx a CreationalContext that may be used to destroy any object with scope
     *        {@link javax.enterprise.context.Dependent} that is created
     * @return a contextual reference representing the bean
     * @throws IllegalArgumentException if the given type is not a bean type of the given bean
     */
    Object getReference(Bean<?> bean,
                        Type beanType,
                        CreationalContext<?> ctx);

    /**
     * Obtains an injectable reference for a certain {@linkplain InjectionPoint injection point}. 
     *
     * @param ij the target injection point
     * @param ctx a CreationalContext that may be used to destroy any object with scope
     *        {@link javax.enterprise.context.Dependent} that is created 
     * @return the injectable reference
     * @throws UnsatisfiedResolutionException if typesafe resolution results in an unsatisfied dependency 
     * @throws AmbiguousResolutionException typesafe resolution results in an unresolvable ambiguous dependency
     */
    Object getInjectableReference(InjectionPoint ij,
                                  CreationalContext<?> ctx);

    /**
     * Obtain an instance of a {@link javax.enterprise.context.spi.CreationalContext} for the
     * given {@linkplain javax.enterprise.context.spi.Contextual contextual type}, or for a
     * non-contextual object.
     * 
     * @param contextual the {@link javax.enterprise.context.spi.Contextual}, or a null value
     * in the case of a non-contextual object 
     * @param <T>
     * @return the new {@link javax.enterprise.context.spi.CreationalContext}
     */
    <T> CreationalContext<T> createCreationalContext(Contextual<T> contextual);

    /**
     * Return the set of beans which have the given required type and qualifiers and are available for
     * injection in the module or library containing the class into which the {@code BeanManager} was
     * injected or the Java EE component from whose JNDI environment namespace the {@code BeanManager}
     * was obtained, according to the rules of typesafe resolution. If no qualifiers are given,
     * the {@linkplain javax.enterprise.inject.Default default qualifier} is assumed.
     *  
     * @param beanType the required bean type
     * @param qualifiers the required qualifiers 
     * @return the resulting set of {@linkplain javax.enterprise.inject.spi.Bean beans}
     * @throws IllegalArgumentException if the given type represents a type variable 
     * @throws IllegalArgumentException if two instances of the same qualifier type are given 
     * @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given
     */
    Set<Bean<?>> getBeans(Type beanType,
                          Annotation... qualifiers);

    /**
     * Return the set of beans which have the given EL name and are available for injection in
     * the module or library containing the class into which the {@code BeanManager} was injected
     * or the Java EE component from whose JNDI environment namespace the {@code BeanManager} was
     * obtained, according to the rules of EL name resolution.
     *  
     * @param name the EL name 
     * @return the resulting set of {@linkplain javax.enterprise.inject.spi.Bean beans}
     */
    Set<Bean<?>> getBeans(String name);

    /**
     * Returns the {@link javax.enterprise.inject.spi.PassivationCapable} bean with the given identifier.
     *  
     * @param id the identifier 
     * @return a {@link javax.enterprise.inject.spi.Bean} that implements
     *         {@link javax.enterprise.inject.spi.PassivationCapable}  and has the given identifier,
     *         or a null value if there is no such bean
     */
    Bean<?> getPassivationCapableBean(String id);

    /**
     * Apply the ambiguous dependency resolution rules to a set of {@linkplain javax.enterprise.inject.spi.Bean beans}. 
     *
     * @param beans a set of {@linkplain javax.enterprise.inject.spi.Bean beans} of the given type
     * @param <X> a common type of the beans
     * @return
     * @throws AmbiguousResolutionException if the ambiguous dependency resolution rules fail
     */
    <X> Bean<? extends X> resolve(Set<Bean<? extends X>> beans);

    /**
     * Validate a certain {@linkplain javax.enterprise.inject.spi.InjectionPoint injection point}. 
     *
     * @param injectionPoint the injection point to validate
     * @throws InjectionException if there is a deployment problem (for example, an unsatisfied or
     *         unresolvable ambiguous dependency) associated with the injection point
     */
    void validate(InjectionPoint injectionPoint);

    /**
     * Fire an event and notify observers.
     * 
     * @param event the event object
     * @param qualifiers the event qualifiers
     * @throws IllegalArgumentException if the runtime type of the event object contains a type variable 
     * @throws IllegalArgumentException if two instances of the same qualifier type are given 
     * @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given
     */
    void fireEvent(Object event, Annotation... qualifiers);

    /**
     * Return the set of observers for an event.
     * 
     * @param event the event object
     * @param qualifiers the event qualifiers
     * @param <T> the type of the event
     * @return
     * @throws IllegalArgumentException if the runtime type of the event object contains a type variable
     * @throws IllegalArgumentException if two instances of the same qualifier type are given
     * @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given
     */
    <T> Set<ObserverMethod<? super T>> resolveObserverMethods(T event,
                                                              Annotation... qualifiers);

    /**
     * Return an ordered list of {@linkplain javax.enterprise.inject.spi.Decorator decorators} for a
     * set of bean types and a set of qualifiers and which are enabled in the module or library
     * containing the class into which the {@code BeanManager} was injected or the Java EE component
     * from whose JNDI environment namespace the {@code BeanManager} was obtained.
     *  
     * @param types the set of bean types of the decorated bean
     * @param qualifiers the qualifiers declared by the decorated bean 
     * @return the resulting set of {@linkplain javax.enterprise.inject.spi.Decorator decorators}
     * @throws IllegalArgumentException if the set of bean types is empty
     * @throws IllegalArgumentException if an annotation which is not a binding type is passed
     * @throws IllegalArgumentException if two instances of the same binding type are passed
     */
    List<Decorator<?>> resolveDecorators(Set<Type> types,
                                         Annotation... qualifiers);

    /**
     * Return an ordered list of enabled {@linkplain javax.enterprise.inject.spi.Interceptor interceptors}
     * for a set of interceptor bindings and a type of interception and which are enabled in the module or
     * library containing the class into which the {@code BeanManager} was injected or the Java EE component
     * from whose JNDI environment namespace the {@code BeanManager} was obtained. 
     *
     * @param type the type of the interception
     * @param interceptorBindings the interceptor bindings 
     * @return the resulting set of {@linkplain javax.enterprise.inject.spi.Interceptor interceptors}
     * @throws IllegalArgumentException if no interceptor binding type is given
     * @throws IllegalArgumentException if two instances of the same interceptor binding type are given
     * @throws IllegalArgumentException if an instance of an annotation that is not an interceptor binding
     *         type is given
     */
    List<Interceptor<?>> resolveInterceptors(InterceptionType type,
                                             Annotation... interceptorBindings);

    /**
     * Test the given annotation type to determine if it is a {@linkplain javax.enterprise.inject scope type}.
     * 
     * @param annotationType the annotation type 
     * @return <tt>true</tt> if the annotation type is a {@linkplain javax.enterprise.inject scope type}
     */
    boolean isScope(Class<? extends Annotation> annotationType);

    /**
     * Test the given annotation type to determine if it is a normal {@linkplain javax.enterprise.inject scope type}.
     *
     * @param annotationType the annotation type
     * @return <tt>true</tt> if the annotation type is a normal {@linkplain javax.enterprise.inject scope type}
     */
    boolean isNormalScope(Class<? extends Annotation> annotationType);

    /**
     * Test the given annotation type to determine if it is a passivating {@linkplain javax.enterprise.inject scope type}.
     *
     * @param annotationType the annotation type
     * @return <tt>true</tt> if the annotation type is a passivating {@linkplain javax.enterprise.inject scope type}
     */
    boolean isPassivatingScope(Class<? extends Annotation> annotationType);

    /**
     * Test the given annotation type to determine if it is a qualifier type.
     *
     * @param annotationType the annotation type
     * @return <tt>true</tt> if the annotation type is a qualifier type
     */
    boolean isQualifier(Class<? extends Annotation> annotationType);

    /**
     * Test the given annotation type to determine if it is an interceptor binding type.
     *
     * @param annotationType the annotation type
     * @return <tt>true</tt> if the annotation type is an interceptor binding type
     */
    boolean isInterceptorBinding(Class<? extends Annotation> annotationType);

    /**
     * Test the given annotation type to determine if it is a {@linkplain javax.enterprise.inject.Stereotype stereotype}. 
     *
     * @param annotationType the annotation type 
     * @return <tt>true</tt> if the annotation type is a {@linkplain javax.enterprise.inject.Stereotype stereotype}
     */
    boolean isStereotype(Class<? extends Annotation> annotationType);

    /**
     * Obtains the set of meta-annotations for a certain interceptor binding type.
     * 
     * @param bindingType the interceptor binding type 
     * @return the set of meta-annotations
     */
    Set<Annotation> getInterceptorBindingDefinition(Class<? extends Annotation> bindingType);

    /**
     * Obtains meta-annotations for a certain {@linkplain javax.enterprise.inject.Stereotype stereotype}.
     *
     * @param stereotype the {@linkplain javax.enterprise.inject.Stereotype stereotype}
     * @return the set of meta-annotations
     */
    Set<Annotation> getStereotypeDefinition(Class<? extends Annotation> stereotype);

    /**
     * Obtains an active {@linkplain javax.enterprise.context.spi.Context context object} for the
     * given {@linkplain javax.enterprise.context scope}.
     *
     * @param scopeType the {@linkplain javax.enterprise.context scope}
     * @return the {@linkplain javax.enterprise.context.spi.Context context object}
     * @throws ContextNotActiveException if there is no active context object for the given scope
     * @throws IllegalArgumentException if there is more than one active context object for the given scope
     */
    Context getContext(Class<? extends Annotation> scopeType);

    /**
     * Returns a {@code ELResolver} that resolves beans by EL name. 
     */
    javax.el.ELResolver getELResolver();

    /**
     * Returns a wrapper {@code ExpressionFactory} that delegates {@code MethodExpression} and
     * {@code ValueExpression}  creation to the given {@code ExpressionFactory}. When a Unified EL expression
     * is evaluated using a {@code MethodExpression} or {@code ValueExpression} returned by the wrapper
     * {@code ExpressionFactory}, the container handles destruction of objects with scope
     * {@link javax.enterprise.context.Dependent}.
     * 
     * @param expressionFactory the {@code ExpressionFactory} to wrap 
     * @return the wrapped {@code ExpressionFactory}
     */
    javax.el.ExpressionFactory wrapExpressionFactory(javax.el.ExpressionFactory expressionFactory);

    /**
     * Obtain an {@link javax.enterprise.inject.spi.AnnotatedType} that may be used to read
     * the annotations of the given class or interface.
     * 
     * @param type the Class object 
     * @param <T> the class or interface
     * @return the {@link javax.enterprise.inject.spi.AnnotatedType}
     */
    <T> AnnotatedType<T> createAnnotatedType(Class<T> type);

    /**
     * Obtains an {@link javax.enterprise.inject.spi.InjectionTarget} for the given
     * {@link javax.enterprise.inject.spi.AnnotatedType}. The container ignores the annotations and types
     * declared by the elements of the actual Java class and uses the metadata provided via the
     * {@link javax.enterprise.inject.spi.Annotated} interface instead. 
     *
     * @param type Annotated
     * @param <T> the type
     * @return
     * @throws IllegalArgumentException if there is a definition error associated with any injection point of the type
     */
    <T> InjectionTarget<T> createInjectionTarget(AnnotatedType<T> type);
}
