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