001    package org.picocontainer.adapters;
002    
003    import org.junit.Test;
004    import org.picocontainer.Characteristics;
005    import org.picocontainer.ComponentAdapter;
006    import org.picocontainer.ComponentMonitor;
007    import org.picocontainer.DefaultPicoContainer;
008    import org.picocontainer.LifecycleStrategy;
009    import org.picocontainer.MutablePicoContainer;
010    import org.picocontainer.Parameter;
011    import org.picocontainer.PicoCompositionException;
012    import org.picocontainer.PicoContainer;
013    import org.picocontainer.behaviors.AbstractBehaviorFactory;
014    import org.picocontainer.injectors.AbstractInjectionFactory;
015    import org.picocontainer.injectors.AbstractInjector;
016    
017    import java.lang.annotation.ElementType;
018    import java.lang.annotation.Retention;
019    import java.lang.annotation.RetentionPolicy;
020    import java.lang.annotation.Target;
021    import java.lang.reflect.Field;
022    import java.lang.reflect.Type;
023    import java.util.Properties;
024    
025    import static java.lang.reflect.Modifier.isStatic;
026    import static org.junit.Assert.assertEquals;
027    
028    
029    /**
030     * @author Paul Hammant
031     * @author Jörg Schaible
032     */
033    @SuppressWarnings("serial")
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, 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    }