001package org.picocontainer.adapters;
002
003import org.junit.Test;
004import org.picocontainer.Characteristics;
005import org.picocontainer.ComponentAdapter;
006import org.picocontainer.ComponentMonitor;
007import org.picocontainer.DefaultPicoContainer;
008import org.picocontainer.LifecycleStrategy;
009import org.picocontainer.MutablePicoContainer;
010import org.picocontainer.Parameter;
011import org.picocontainer.PicoCompositionException;
012import org.picocontainer.PicoContainer;
013import org.picocontainer.behaviors.AbstractBehaviorFactory;
014import org.picocontainer.injectors.AbstractInjectionFactory;
015import org.picocontainer.injectors.AbstractInjector;
016
017import java.lang.annotation.ElementType;
018import java.lang.annotation.Retention;
019import java.lang.annotation.RetentionPolicy;
020import java.lang.annotation.Target;
021import java.lang.reflect.Field;
022import java.lang.reflect.Type;
023import java.util.Properties;
024
025import static java.lang.reflect.Modifier.isStatic;
026import static org.junit.Assert.assertEquals;
027
028
029/**
030 * @author Paul Hammant
031 * @author Jörg Schaible
032 */
033@SuppressWarnings("serial")
034public class SimpleNamedBindingAnnotationTestCase {
035
036    @Test public void testNamedBinding() {
037        MutablePicoContainer mpc = new DefaultPicoContainer(new FieldInjection());
038        mpc.addComponent(FruitBasket.class);
039        mpc.addComponent(bindKey(Apple.class, "one"), AppleImpl1.class);
040        mpc.addComponent(bindKey(Apple.class, "two"), AppleImpl2.class);
041        mpc.addComponent(bindKey(Apple.class, "three"), AppleImpl3.class);
042        mpc.addComponent(bindKey(Apple.class, "four"), AppleImpl4.class);
043        // this level of terseness is the other way ....
044        // this should not be barfing if if we can get binding to annotations working
045        FruitBasket fb = mpc.getComponent(FruitBasket.class);
046        assertEquals(fb.one.getX(), 1);
047        assertEquals(fb.two.getX(), 2);
048        assertEquals(fb.three.getX(), 3);
049        assertEquals(fb.four.getX(), 4);
050    }
051
052    public interface Apple {
053        int getX();
054    }
055
056    public static class AppleImpl1 implements Apple {
057        public int getX() {
058            return 1;
059        }
060    }
061
062    public static class AppleImpl2 implements Apple {
063        public int getX() {
064            return 2;
065        }
066    }
067
068    public static class AppleImpl3 implements Apple {
069        public int getX() {
070            return 3;
071        }
072    }
073
074    public static class AppleImpl4 implements Apple {
075        public int getX() {
076            return 4;
077        }
078    }
079
080    public static class FruitBasket {
081        private @Named("one")
082        Apple one;
083        private @Named("two")
084        Apple two;
085        private @Named("three")
086        Apple three;
087        private @Named("four")
088        Apple four;
089
090        public FruitBasket() {
091        }
092    }
093
094    // to become an annotation
095    @Retention(RetentionPolicy.RUNTIME)
096    @Target({ElementType.FIELD, ElementType.PARAMETER})
097    public @interface Named {
098        String value();
099    }
100
101    // implicitly this function goes into DPC
102    public static String bindKey(Class type, String bindingId) {
103        return type.getName() + "/" + bindingId;
104    }
105
106    public class FieldInjection extends AbstractInjectionFactory {
107
108        public <T> ComponentAdapter<T> createComponentAdapter(
109            ComponentMonitor componentMonitor, LifecycleStrategy lifecycleStrategy,
110            Properties componentProperties, Object componentKey,
111            Class<T> componentImplementation, Parameter ... parameters)
112            throws PicoCompositionException {
113            boolean useNames = AbstractBehaviorFactory.arePropertiesPresent(
114                componentProperties, Characteristics.USE_NAMES, true);
115            return new FieldInjector(componentKey, componentImplementation, parameters, componentMonitor, useNames);
116        }
117    }
118
119    public static class FieldInjector<T> extends AbstractInjector<T> {
120
121        protected FieldInjector(Object componentKey, Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, boolean useNames) {
122            super(componentKey, componentImplementation, parameters, monitor, useNames);
123        }
124
125        @Override
126        public void verify(PicoContainer container) throws PicoCompositionException {
127            // TODO Auto-generated method stub
128        }
129
130        public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
131            final T inst;
132            try {
133                inst = getComponentImplementation().newInstance();
134                Field[] declaredFields = getComponentImplementation().getDeclaredFields();
135                for (final Field field : declaredFields) {
136                    if (!isStatic(field.getModifiers())) {
137                        Named bindAnnotation = field.getAnnotation(Named.class);
138                        Object value;
139                        if (bindAnnotation != null) {
140                            value = container.getComponent(bindKey(field.getType(), bindAnnotation.value()));
141                        } else {
142                            value = container.getComponent(field.getType());
143                        }
144                        field.setAccessible(true);
145                        field.set(inst, value);                        
146                    }
147                }
148
149            } catch (InstantiationException e) {
150                return caughtInstantiationException(currentMonitor(), null, e, container);
151            } catch (IllegalAccessException e) {
152                return caughtIllegalAccessException(currentMonitor(), null, e, container);
153            }
154            return inst;
155        }
156
157        public String getDescriptor() {
158            return "FieldInjector";
159        }
160
161    }
162}