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     *****************************************************************************/
010    package org.picocontainer.defaults;
011    
012    import org.picocontainer.ComponentAdapter;
013    import org.picocontainer.MutablePicoContainer;
014    import org.picocontainer.PicoContainer;
015    import org.picocontainer.PicoCompositionException;
016    import org.picocontainer.DefaultPicoContainer;
017    import org.picocontainer.behaviors.CachingBehaviorFactory;
018    import org.picocontainer.injectors.AbstractInjector;
019    import org.picocontainer.injectors.ConstructorInjector;
020    import org.picocontainer.injectors.AdaptiveInjectionFactory;
021    import org.picocontainer.monitors.NullComponentMonitor;
022    import org.picocontainer.lifecycle.NullLifecycleStrategy;
023    import org.picocontainer.parameters.CollectionComponentParameter;
024    import org.picocontainer.parameters.ComponentParameter;
025    import org.picocontainer.adapters.InstanceAdapter;
026    import org.picocontainer.testmodel.SimpleTouchable;
027    import org.picocontainer.testmodel.Touchable;
028    
029    import org.jmock.Mock;
030    import org.jmock.MockObjectTestCase;
031    
032    import java.util.Arrays;
033    import java.util.Collection;
034    import java.util.Collections;
035    import java.util.HashSet;
036    import java.util.List;
037    import java.util.Map;
038    import java.util.Set;
039    import java.util.SortedMap;
040    import java.util.SortedSet;
041    
042    
043    /**
044     * @author Aslak Hellesøy
045     * @author Jörg Schaible
046     * @version $Revision: 3714 $
047     */
048    public class CollectionComponentParameterTestCase
049            extends MockObjectTestCase {
050    
051        public void testShouldInstantiateArrayOfStrings() {
052            CollectionComponentParameter ccp = new CollectionComponentParameter();
053    
054            Mock componentAdapterMock = mock(ComponentAdapter.class);
055            componentAdapterMock.expects(atLeastOnce()).method("getComponentKey").will(returnValue("x"));
056            Mock containerMock = mock(PicoContainer.class);
057            containerMock.expects(once()).method("getComponentAdapters").withNoArguments().will(returnValue(new HashSet()));
058            containerMock.expects(once()).method("getComponentAdapters").with(eq(String.class)).will(
059                    returnValue(Arrays.asList(new InstanceAdapter("y", "Hello", new NullLifecycleStrategy(),
060                                                                            new NullComponentMonitor()), new InstanceAdapter("z", "World", new NullLifecycleStrategy(),
061                                                                            new NullComponentMonitor()))));
062            containerMock.expects(once()).method("getComponent").with(eq("z")).will(returnValue("World"));
063            containerMock.expects(once()).method("getComponent").with(eq("y")).will(returnValue("Hello"));
064            containerMock.expects(once()).method("getParent").withNoArguments().will(returnValue(null));
065    
066            List expected = Arrays.asList("Hello", "World");
067            Collections.sort(expected);
068            List actual = Arrays.asList((Object[]) ccp.resolveInstance(
069                    (PicoContainer) containerMock.proxy(), (ComponentAdapter) componentAdapterMock.proxy(), String[].class, null));
070            Collections.sort(actual);
071            assertEquals(expected, actual);
072        }
073    
074        static public interface Fish {
075        }
076    
077        static public class Cod
078                implements Fish {
079            public String toString() {
080                return "Cod";
081            }
082        }
083    
084        static public class Shark
085                implements Fish {
086            public String toString() {
087                return "Shark";
088            }
089        }
090    
091        static public class Bowl {
092            private final Cod[] cods;
093            private final Fish[] fishes;
094    
095            public Bowl(Cod cods[], Fish fishes[]) {
096                this.cods = cods;
097                this.fishes = fishes;
098            }
099        }
100    
101        private MutablePicoContainer getDefaultPicoContainer() {
102            MutablePicoContainer mpc = new DefaultPicoContainer(new CachingBehaviorFactory());
103            mpc.addComponent(Bowl.class);
104            mpc.addComponent(Cod.class);
105            mpc.addComponent(Shark.class);
106            return mpc;
107        }
108    
109        public void testNativeArrays() {
110            MutablePicoContainer mpc = getDefaultPicoContainer();
111            Cod cod = mpc.getComponent(Cod.class);
112            Bowl bowl = mpc.getComponent(Bowl.class);
113            assertEquals(1, bowl.cods.length);
114            assertEquals(2, bowl.fishes.length);
115            assertSame(cod, bowl.cods[0]);
116            assertNotSame(bowl.fishes[0], bowl.fishes[1]);
117        }
118    
119        public void testCollectionsAreGeneratedOnTheFly() {
120            MutablePicoContainer mpc = new DefaultPicoContainer();
121            mpc.addAdapter(new ConstructorInjector(Bowl.class, Bowl.class, null, new NullComponentMonitor(), new NullLifecycleStrategy()));
122            mpc.addComponent(Cod.class);
123            Bowl bowl = mpc.getComponent(Bowl.class);
124            assertEquals(1, bowl.cods.length);
125            mpc.addComponent("Nemo", new Cod());
126            bowl = mpc.getComponent(Bowl.class);
127            assertEquals(2, bowl.cods.length);
128            assertNotSame(bowl.cods[0], bowl.cods[1]);
129        }
130    
131        static public class CollectedBowl {
132            private final Cod[] cods;
133            private final Fish[] fishes;
134    
135            public CollectedBowl(Collection cods, Collection fishes) {
136                this.cods = (Cod[]) cods.toArray(new Cod[cods.size()]);
137                this.fishes = (Fish[]) fishes.toArray(new Fish[fishes.size()]);
138            }
139        }
140    
141        public void testCollections() {
142            MutablePicoContainer mpc = new DefaultPicoContainer(new CachingBehaviorFactory());
143            mpc.addComponent(CollectedBowl.class, CollectedBowl.class,
144                             new ComponentParameter(Cod.class, false), new ComponentParameter(Fish.class, false));
145            mpc.addComponent(Cod.class);
146            mpc.addComponent(Shark.class);
147            Cod cod = mpc.getComponent(Cod.class);
148            CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
149            assertEquals(1, bowl.cods.length);
150            assertEquals(2, bowl.fishes.length);
151            assertSame(cod, bowl.cods[0]);
152            assertNotSame(bowl.fishes[0], bowl.fishes[1]);
153        }
154    
155        static public class MappedBowl {
156            private final Fish[] fishes;
157    
158            public MappedBowl(Map map) {
159                Collection collection = map.values();
160                this.fishes = (Fish[]) collection.toArray(new Fish[collection.size()]);
161            }
162        }
163    
164        public void testMaps() {
165            MutablePicoContainer mpc = new DefaultPicoContainer();
166            mpc.addComponent(MappedBowl.class, MappedBowl.class, new ComponentParameter(
167                    Fish.class, false));
168            mpc.addComponent(Cod.class);
169            mpc.addComponent(Shark.class);
170            MappedBowl bowl = mpc.getComponent(MappedBowl.class);
171            assertEquals(2, bowl.fishes.length);
172            assertNotSame(bowl.fishes[0], bowl.fishes[1]);
173        }
174    
175        public static class UngenericCollectionBowl {
176            public UngenericCollectionBowl(Collection fish) {
177            }
178        }
179    
180        public void testShouldNotInstantiateCollectionForUngenericCollectionParameters() {
181            MutablePicoContainer pico = getDefaultPicoContainer();
182            pico.addComponent(UngenericCollectionBowl.class);
183            try {
184                pico.getComponent(UngenericCollectionBowl.class);
185                fail();
186            } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
187                // expected
188            }
189        }
190    
191        public static class AnotherGenericCollectionBowl {
192            private final String[] strings;
193    
194            public AnotherGenericCollectionBowl(String[] strings) {
195                this.strings = strings;
196            }
197    
198            public String[] getStrings() {
199                return strings;
200            }
201        }
202    
203        public void testShouldFailWhenThereAreNoComponentsToPutInTheArray() {
204            MutablePicoContainer pico = getDefaultPicoContainer();
205            pico.addComponent(AnotherGenericCollectionBowl.class);
206            try {
207                pico.getComponent(AnotherGenericCollectionBowl.class);
208                fail();
209            } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
210                // expected
211            }
212        }
213    
214        public void testAllowsEmptyArraysIfEspeciallySet() {
215            MutablePicoContainer pico = getDefaultPicoContainer();
216            pico.addComponent(
217                    AnotherGenericCollectionBowl.class, AnotherGenericCollectionBowl.class,
218                    ComponentParameter.ARRAY_ALLOW_EMPTY);
219            AnotherGenericCollectionBowl bowl = pico
220                    .getComponent(AnotherGenericCollectionBowl.class);
221            assertNotNull(bowl);
222            assertEquals(0, bowl.strings.length);
223        }
224    
225        public static class TouchableObserver
226                implements Touchable {
227            private final Touchable[] touchables;
228    
229            public TouchableObserver(Touchable[] touchables) {
230                this.touchables = touchables;
231    
232            }
233    
234            public void touch() {
235                for (Touchable touchable : touchables) {
236                    touchable.touch();
237                }
238            }
239        }
240    
241        public void testWillOmitSelfFromCollection() {
242            MutablePicoContainer pico = getDefaultPicoContainer();
243            pico.addComponent(SimpleTouchable.class);
244            pico.addComponent(TouchableObserver.class);
245            Touchable observer = pico.getComponent(TouchableObserver.class);
246            assertNotNull(observer);
247            observer.touch();
248            SimpleTouchable touchable = pico.getComponent(SimpleTouchable.class);
249            assertTrue(touchable.wasTouched);
250        }
251    
252        public void testWillRemoveComponentsWithMatchingKeyFromParent() {
253            MutablePicoContainer parent = new DefaultPicoContainer();
254            parent.addComponent("Tom", Cod.class);
255            parent.addComponent("Dick", Cod.class);
256            parent.addComponent("Harry", Cod.class);
257            MutablePicoContainer child = new DefaultPicoContainer(parent);
258            child.addComponent("Dick", Shark.class);
259            child.addComponent(Bowl.class);
260            Bowl bowl = child.getComponent(Bowl.class);
261            assertEquals(3, bowl.fishes.length);
262            assertEquals(2, bowl.cods.length);
263        }
264    
265        public void testBowlWithoutTom() {
266            MutablePicoContainer mpc = new DefaultPicoContainer();
267            mpc.addComponent("Tom", Cod.class);
268            mpc.addComponent("Dick", Cod.class);
269            mpc.addComponent("Harry", Cod.class);
270            mpc.addComponent(Shark.class);
271            mpc.addComponent(CollectedBowl.class, CollectedBowl.class, new CollectionComponentParameter(Cod.class, false) {
272                protected boolean evaluate(ComponentAdapter adapter) {
273                    return !"Tom".equals(adapter.getComponentKey());
274                }
275            },
276                             new CollectionComponentParameter(Fish.class, false));
277            CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
278            Cod tom = (Cod) mpc.getComponent("Tom");
279            assertEquals(4, bowl.fishes.length);
280            assertEquals(2, bowl.cods.length);
281            assertFalse(Arrays.asList(bowl.cods).contains(tom));
282        }
283    
284        public static class DependsOnAll {
285            public DependsOnAll(Set set, SortedSet sortedSet, Collection collection, List list, SortedMap sortedMap, Map map
286            // , ConcurrentMap concurrentMap, Queue queue, BlockingQueue blockingQueue
287            ) {
288                assertNotNull(set);
289                assertNotNull(sortedSet);
290                assertNotNull(collection);
291                assertNotNull(list);
292                assertNotNull(sortedMap);
293                assertNotNull(map);
294                //            assertNotNull(concurrentMap);
295                //            assertNotNull(queue);
296                //            assertNotNull(blockingQueue);
297            }
298        }
299    
300        public void testDifferentCollectiveTypesAreResolved() {
301            MutablePicoContainer pico = new DefaultPicoContainer();
302            CollectionComponentParameter parameter = new CollectionComponentParameter(Fish.class, true);
303            pico.addComponent(DependsOnAll.class, DependsOnAll.class,
304                              parameter, parameter, parameter, parameter, parameter, parameter);
305            assertNotNull(pico.getComponent(DependsOnAll.class));
306        }
307    
308        public void testVerify() {
309            MutablePicoContainer pico = new DefaultPicoContainer();
310            CollectionComponentParameter parameterNonEmpty = CollectionComponentParameter.ARRAY;
311            pico.addComponent(Shark.class);
312            parameterNonEmpty.verify(pico, null, Fish[].class, null);
313            try {
314                parameterNonEmpty.verify(pico, null, Cod[].class, null);
315                fail("(PicoCompositionException expected");
316            } catch (PicoCompositionException e) {
317                assertTrue(e.getMessage().indexOf(Cod.class.getName())>0);
318            }
319            CollectionComponentParameter parameterEmpty = CollectionComponentParameter.ARRAY_ALLOW_EMPTY;
320            parameterEmpty.verify(pico, null, Fish[].class, null);
321            parameterEmpty.verify(pico, null, Cod[].class, null);
322        }
323    
324        // PICO-243 : this test will fail if executed on jdk1.3 without commons-collections
325        public void testOrderOfElementsOfAnArrayDependencyIsPreserved() {
326            MutablePicoContainer pico = new DefaultPicoContainer();
327            pico.addComponent("first", "first");
328            pico.addComponent("second", "second");
329            pico.addComponent("third", "third");
330            pico.addComponent("fourth", "fourth");
331            pico.addComponent("fifth", "fifth");
332            pico.addComponent(Truc.class);
333    
334            final List strings = pico.getComponents(String.class);
335            assertEquals("first", strings.get(0));
336            assertEquals("second", strings.get(1));
337            assertEquals("third", strings.get(2));
338            assertEquals("fourth", strings.get(3));
339            assertEquals("fifth", strings.get(4));
340    
341            pico.getComponent(Truc.class);
342        }
343    
344        public static final class Truc {
345            public Truc(String[] s) {
346                assertEquals("first", s[0]);
347                assertEquals("second", s[1]);
348                assertEquals("third", s[2]);
349                assertEquals("fourth", s[3]);
350                assertEquals("fifth", s[4]);
351            }
352        }
353    
354    }