001    package org.picocontainer.adapters;
002    
003    import static org.junit.Assert.assertEquals;
004    
005    import java.io.Serializable;
006    import java.lang.annotation.ElementType;
007    import java.lang.annotation.Retention;
008    import java.lang.annotation.RetentionPolicy;
009    import java.lang.annotation.Target;
010    import java.lang.reflect.Field;
011    import java.lang.reflect.Type;
012    import java.util.Properties;
013    
014    import org.junit.Test;
015    import org.picocontainer.Characteristics;
016    import org.picocontainer.ComponentAdapter;
017    import org.picocontainer.ComponentMonitor;
018    import org.picocontainer.DefaultPicoContainer;
019    import org.picocontainer.InjectionFactory;
020    import org.picocontainer.LifecycleStrategy;
021    import org.picocontainer.MutablePicoContainer;
022    import org.picocontainer.Parameter;
023    import org.picocontainer.PicoCompositionException;
024    import org.picocontainer.PicoContainer;
025    import org.picocontainer.behaviors.AbstractBehaviorFactory;
026    import org.picocontainer.injectors.AbstractInjector;
027    import org.picocontainer.injectors.AbstractInjectionFactory;
028    
029    
030    /**
031     * @author Paul Hammant
032     * @author Jörg Schaible
033     */
034    public 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);
115                return new FieldInjector(
116                    componentKey, componentImplementation, parameters, componentMonitor,
117                    lifecycleStrategy, useNames);
118            }
119        }
120    
121        public static class FieldInjector<T> extends AbstractInjector<T> {
122    
123            protected FieldInjector(
124                Object componentKey, Class componentImplementation, Parameter[] parameters,
125                ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy, boolean useNames) {
126                super(
127                    componentKey, componentImplementation, parameters, monitor, lifecycleStrategy,
128                    useNames);
129            }
130    
131            @Override
132            public void verify(PicoContainer container) throws PicoCompositionException {
133                // @todo Auto-generated method stub
134            }
135    
136            public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
137                final T inst;
138                try {
139                    inst = getComponentImplementation().newInstance();
140                    Field[] declaredFields = getComponentImplementation().getDeclaredFields();
141                    for (int i = 0; i < declaredFields.length; i++ ) {
142                        final Field field = declaredFields[i];
143                        Named bindAnnotation = field.getAnnotation(Named.class);
144                        Object value;
145                        if (bindAnnotation != null) {
146                            value = container.getComponent(bindKey(field.getType(), bindAnnotation
147                                .value()));
148                        } else {
149                            value = container.getComponent(field.getType());
150                        }
151                        field.setAccessible(true);
152                        field.set(inst, value);
153                    }
154    
155                } catch (InstantiationException e) {
156                    return caughtInstantiationException(currentMonitor(), null, e, container);
157                } catch (IllegalAccessException e) {
158                    return caughtIllegalAccessException(currentMonitor(), null, e, container);
159                }
160                return inst;
161            }
162    
163            public void decorateComponentInstance(PicoContainer container, Type into, T instance) {
164            }
165    
166            public String getDescriptor() {
167                return "FieldInjector";
168            }
169    
170        }
171    }