001/*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *                                                                           *
008 * Original code by                                                          *
009 *****************************************************************************/
010package org.picocontainer.injectors;
011
012import org.picocontainer.ComponentMonitor;
013import org.picocontainer.NameBinding;
014import org.picocontainer.Parameter;
015import org.picocontainer.PicoContainer;
016import org.picocontainer.annotations.Bind;
017
018import java.lang.annotation.Annotation;
019import java.lang.reflect.AccessibleObject;
020import java.lang.reflect.Field;
021import java.lang.reflect.InvocationTargetException;
022import java.lang.reflect.Type;
023import java.security.AccessController;
024import java.security.PrivilegedAction;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collections;
028import java.util.List;
029import java.util.Set;
030
031/**
032 * Injection happens after instantiation, and fields are marked as 
033 * injection points via a named field.
034 */
035@SuppressWarnings("serial")
036public class NamedFieldInjector<T> extends AbstractFieldInjector<T> {
037
038    private final List<String> fieldNames;
039
040    public NamedFieldInjector(Object key,
041                                  Class<?> impl,
042                                  Parameter[] parameters,
043                                  ComponentMonitor componentMonitor,
044                                  String fieldNames) {
045
046        super(key, impl, parameters, componentMonitor, true);
047        this.fieldNames = Arrays.asList(fieldNames.trim().split(" "));
048    }
049
050    @Override
051    protected void initializeInjectionMembersAndTypeLists() {
052        injectionMembers = new ArrayList<AccessibleObject>();
053        List<Annotation> bindingIds = new ArrayList<Annotation>();
054        final List<Type> typeList = new ArrayList<Type>();
055        final Field[] fields = getFields();
056        for (final Field field : fields) {
057            if (isNamedForInjection(field)) {
058                injectionMembers.add(field);
059                typeList.add(box(field.getType()));
060                bindingIds.add(getBinding(field));
061            }
062        }
063        injectionTypes = typeList.toArray(new Type[0]);
064        bindings = bindingIds.toArray(new Annotation[0]);
065    }
066
067    private Annotation getBinding(Field field) {
068        Annotation[] annotations = field.getAnnotations();
069        for (Annotation annotation : annotations) {
070            if (annotation.annotationType().isAnnotationPresent(Bind.class)) {
071                return annotation;
072            }
073        }
074        return null;
075    }
076
077    protected boolean isNamedForInjection(Field field) {
078        return fieldNames.contains(field.getName());
079    }
080
081    private Field[] getFields() {
082        return AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
083            public Field[] run() {
084                return getComponentImplementation().getDeclaredFields();
085            }
086        });
087    }
088
089
090    protected Object injectIntoMember(AccessibleObject member, Object componentInstance, Object toInject)
091        throws IllegalAccessException, InvocationTargetException {
092        Field field = (Field) member;
093        field.setAccessible(true);
094        field.set(componentInstance, toInject);
095        return null;
096    }
097
098    @Override
099    public String getDescriptor() {
100        return "NamedFieldInjector-";
101    }
102
103    @Override
104    protected NameBinding makeParameterNameImpl(final AccessibleObject member) {
105        return new NameBinding() {
106            public String getName() {
107                return ((Field) member).getName();
108            }
109        };
110    }
111
112    protected Object memberInvocationReturn(Object lastReturn, AccessibleObject member, Object instance) {
113        return instance;
114    }
115
116    List<String> getInjectionFieldNames() {
117        return Collections.unmodifiableList(fieldNames);
118    }
119
120
121}