/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.weld.injection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMember;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.InjectionPoint;

import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedField;
import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedMethod;
import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedType;
import org.jboss.weld.bean.builtin.ee.StaticEEResourceProducerField;
import org.jboss.weld.bootstrap.api.Service;
import org.jboss.weld.injection.spi.ResourceReferenceFactory;
import org.jboss.weld.logging.UtilLogger;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.util.collections.ImmutableSet;
import org.jboss.weld.util.reflection.Reflections;

/**
 * Abstract stateless resource injection processor.
 *
 * @author Martin Kouba
 *
 * @param <S>
 * @param <C> processor context
 */
public abstract class ResourceInjectionProcessor<S extends Service, C> {

    /**
     * @param fieldInjectionPoint
     * @param beanManager
     * @return {@link ResourceInjection} for static producer field or <code>null</code> if required services are not supported or the field is not annotated
     *         with the specific marker annotation
     * @see StaticEEResourceProducerField
     */
    protected <T, X> ResourceInjection<T> createStaticProducerFieldResourceInjection(FieldInjectionPoint<T, X> fieldInjectionPoint, BeanManagerImpl beanManager) {

        S injectionServices = getInjectionServices(beanManager);
        C processorContext = getProcessorContext(beanManager);

        if (injectionServices != null && fieldInjectionPoint.getAnnotated().isAnnotationPresent(getMarkerAnnotation(processorContext))
                && accept(fieldInjectionPoint.getAnnotated(), processorContext)) {
            return createFieldResourceInjection(fieldInjectionPoint, injectionServices, processorContext);
        }
        return null;
    }

    /**
     *
     * @param declaringBean
     * @param type
     * @param manager
     * @return the set of {@link ResourceInjection}s for the specified bean and type
     */
    protected <T> Set<ResourceInjection<?>> createResourceInjections(Bean<?> declaringBean, EnhancedAnnotatedType<T> type, BeanManagerImpl manager) {

        S injectionServices = getInjectionServices(manager);
        C processorContext = getProcessorContext(manager);

        if (injectionServices == null) {
            return Collections.emptySet();
        }


        Class<? extends Annotation> marker = getMarkerAnnotation(processorContext);

        final Collection<EnhancedAnnotatedField<?, ? super T>> fields = type.getDeclaredEnhancedFields(marker);
        final Collection<EnhancedAnnotatedMethod<?, ? super T>> methods = type.getDeclaredEnhancedMethods(marker);

        return createResourceInjections(fields, methods, declaringBean, type.getJavaClass(), manager);
    }

    /**
     *
     * @param fieldInjectionPoint
     * @param beanManager
     * @return {@link ResourceInjection} for the given field
     */
    private <T, X> ResourceInjection<T> createFieldResourceInjection(FieldInjectionPoint<T, X> fieldInjectionPoint, S injectionServices, C processorContext) {
        return new FieldResourceInjection<T, X>(fieldInjectionPoint, Reflections.<ResourceReferenceFactory<T>> cast(getResourceReferenceFactory(
                fieldInjectionPoint, injectionServices, processorContext)));
    }

    /**
     *
     * @param methodInjectionPoint
     * @param beanManager
     * @return {@link ResourceInjection} for the given setter method
     */
    private <T, X> ResourceInjection<T> createSetterResourceInjection(ParameterInjectionPoint<T, X> parameterInjectionPoint, S injectionServices,
            C processorContext) {
        return new SetterResourceInjection<T, X>(parameterInjectionPoint, Reflections.<ResourceReferenceFactory<T>> cast(getResourceReferenceFactory(
                parameterInjectionPoint, injectionServices, processorContext)));
    }

    public Class<? extends Annotation> getMarkerAnnotation(BeanManagerImpl manager) {
        return getMarkerAnnotation(getProcessorContext(manager));
    }

    protected abstract <T> ResourceReferenceFactory<T> getResourceReferenceFactory(InjectionPoint injectionPoint, S injectionServices, C processorContext);

    protected abstract Class<? extends Annotation> getMarkerAnnotation(C processorContext);

    protected abstract C getProcessorContext(BeanManagerImpl manager);

    protected abstract S getInjectionServices(BeanManagerImpl manager);

    protected <T> Set<ResourceInjection<?>> createResourceInjections(Iterable<EnhancedAnnotatedField<?, ? super T>> fields,
            Iterable<EnhancedAnnotatedMethod<?, ? super T>> methods, Bean<?> declaringBean, Class<?> declaringClass, BeanManagerImpl manager) {
        ImmutableSet.Builder<ResourceInjection<?>> resourceInjections = ImmutableSet.builder();

        S injectionServices = getInjectionServices(manager);
        C processorContext = getProcessorContext(manager);

        for (EnhancedAnnotatedField<?, ? super T> field : fields) {
            if (accept(field, processorContext)) {
                resourceInjections.add(createFieldResourceInjection(
                        InjectionPointFactory.silentInstance().createFieldInjectionPoint(field, declaringBean, declaringClass, manager), injectionServices,
                        processorContext));
            }
        }
        for (EnhancedAnnotatedMethod<?, ?> method : methods) {
            if (method.getParameters().size() != 1) {
                throw UtilLogger.LOG.resourceSetterInjectionNotAJavabean(method);
            }
            if (accept(method, processorContext)) {
                resourceInjections.add(createSetterResourceInjection(
                        InjectionPointFactory.silentInstance().createParameterInjectionPoint(method.getEnhancedParameters().get(0), declaringBean,
                                declaringClass, manager), injectionServices, processorContext));
            }
        }
        return resourceInjections.build();
    }

    /**
     * Allows an implementation to indicate whether it accepts a given injection point annotated with the marker annotation.
     */
    protected boolean accept(AnnotatedMember<?> member, C processorContext) {
        return true;
    }

    protected Type getResourceInjectionPointType(AnnotatedMember<?> member) {
        if (member instanceof AnnotatedField<?>) {
            return member.getBaseType();
        }
        if (member instanceof AnnotatedMethod<?>) {
            AnnotatedMethod<?> method = (AnnotatedMethod<?>) member;
            if (method.getParameters().size() != 1) {
                throw UtilLogger.LOG.resourceSetterInjectionNotAJavabean(method);
            }
            return method.getParameters().get(0).getBaseType();
        }
        throw new IllegalArgumentException("Unknown member " + member);
    }

}

@@ -1332,24 +1332,93 @@
 atedMethod;%0A
+import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedParameter;%0A
 import org.j
@@ -6811,32 +6811,132 @@
 ssorContext)) %7B%0A
+                EnhancedAnnotatedParameter%3C?, ?%3E parameter = method.getEnhancedParameters().get(0);%0A
                 
@@ -7086,45 +7086,17 @@
 int(
-method.getEnhancedParameters().get(0)
+parameter
 , de

Work around Eclipse compiler bug with generics and type inference