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