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(), false));
148                    mpc.addComponent(Cod.class);
149                    Bowl bowl = mpc.getComponent(Bowl.class);
150                    assertEquals(1, bowl.cods.length);
151                    mpc.addComponent("Nemo", new Cod());
152                    bowl = mpc.getComponent(Bowl.class);
153                    assertEquals(2, bowl.cods.length);
154                    assertNotSame(bowl.cods[0], bowl.cods[1]);
155            }
156    
157            static public class CollectedBowl {
158                    private final Cod[] cods;
159                    private final Fish[] fishes;
160    
161                    public CollectedBowl(Collection cods, Collection fishes) {
162                            this.cods = (Cod[]) cods.toArray(new Cod[cods.size()]);
163                            this.fishes = (Fish[]) fishes.toArray(new Fish[fishes.size()]);
164                    }
165            }
166    
167            static public class GenericCollectedBowl extends CollectedBowl {
168    
169                    public GenericCollectedBowl(Collection<Cod> cods, Collection<Fish> fishes) {
170                super(cods, fishes);
171            }
172            }
173    
174            @Test
175            public void testCollections() {
176                    MutablePicoContainer mpc = new DefaultPicoContainer(new Caching());
177                    mpc.addComponent(CollectedBowl.class, CollectedBowl.class,
178                                    new ComponentParameter(Cod.class, false),
179                                    new ComponentParameter(Fish.class, false));
180                    mpc.addComponent(Cod.class);
181                    mpc.addComponent(Shark.class);
182                    Cod cod = mpc.getComponent(Cod.class);
183                    CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
184                    assertEquals(1, bowl.cods.length);
185                    assertEquals(2, bowl.fishes.length);
186                    assertSame(cod, bowl.cods[0]);
187                    assertNotSame(bowl.fishes[0], bowl.fishes[1]);
188            }
189    
190            @Test
191            public void testGenericCollections() {
192                    MutablePicoContainer mpc = new DefaultPicoContainer(new Caching());
193                    mpc.addComponent(GenericCollectedBowl.class);
194                    mpc.addComponent(Cod.class);
195                    mpc.addComponent(Shark.class);
196                    Cod cod = mpc.getComponent(Cod.class);
197                    CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
198                    assertEquals(1, bowl.cods.length);
199                    assertEquals(2, bowl.fishes.length);
200                    assertSame(cod, bowl.cods[0]);
201                    assertNotSame(bowl.fishes[0], bowl.fishes[1]);
202            }
203    
204            static public class MappedBowl {
205                    private final Fish[] fishes;
206    
207                    public MappedBowl(Map map) {
208                            Collection collection = map.values();
209                            this.fishes = (Fish[]) collection.toArray(new Fish[collection
210                                            .size()]);
211                    }
212            }
213    
214            @Test
215            public void testMaps() {
216                    MutablePicoContainer mpc = new DefaultPicoContainer();
217                    mpc.addComponent(MappedBowl.class, MappedBowl.class,
218                                    new ComponentParameter(Fish.class, false));
219                    mpc.addComponent(Cod.class);
220                    mpc.addComponent(Shark.class);
221                    MappedBowl bowl = mpc.getComponent(MappedBowl.class);
222                    assertEquals(2, bowl.fishes.length);
223                    assertNotSame(bowl.fishes[0], bowl.fishes[1]);
224            }
225    
226            public static class UngenericCollectionBowl {
227                    public UngenericCollectionBowl(Collection fish) {
228                    }
229            }
230    
231            @Test
232            public void testShouldNotInstantiateCollectionForUngenericCollectionParameters() {
233                    MutablePicoContainer pico = getDefaultPicoContainer();
234                    pico.addComponent(UngenericCollectionBowl.class);
235                    try {
236                            pico.getComponent(UngenericCollectionBowl.class);
237                            fail();
238                    } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
239                            // expected
240                    }
241            }
242    
243            public static class AnotherGenericCollectionBowl {
244                    private final String[] strings;
245    
246                    public AnotherGenericCollectionBowl(String[] strings) {
247                            this.strings = strings;
248                    }
249    
250                    public String[] getStrings() {
251                            return strings;
252                    }
253            }
254    
255            @Test
256            public void testShouldFailWhenThereAreNoComponentsToPutInTheArray() {
257                    MutablePicoContainer pico = getDefaultPicoContainer();
258                    pico.addComponent(AnotherGenericCollectionBowl.class);
259                    try {
260                            pico.getComponent(AnotherGenericCollectionBowl.class);
261                            fail();
262                    } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
263                            // expected
264                    }
265            }
266    
267            @Test
268            public void testAllowsEmptyArraysIfEspeciallySet() {
269                    MutablePicoContainer pico = getDefaultPicoContainer();
270                    pico.addComponent(AnotherGenericCollectionBowl.class,
271                                    AnotherGenericCollectionBowl.class,
272                                    ComponentParameter.ARRAY_ALLOW_EMPTY);
273                    AnotherGenericCollectionBowl bowl = pico
274                                    .getComponent(AnotherGenericCollectionBowl.class);
275                    assertNotNull(bowl);
276                    assertEquals(0, bowl.strings.length);
277            }
278    
279            public static class TouchableObserver implements Touchable {
280                    private final Touchable[] touchables;
281    
282                    public TouchableObserver(Touchable[] touchables) {
283                            this.touchables = touchables;
284    
285                    }
286    
287                    public void touch() {
288                            for (Touchable touchable : touchables) {
289                                    touchable.touch();
290                            }
291                    }
292            }
293    
294            @Test
295            public void testWillOmitSelfFromCollection() {
296                    MutablePicoContainer pico = getDefaultPicoContainer();
297                    pico.addComponent(SimpleTouchable.class);
298                    pico.addComponent(TouchableObserver.class);
299                    Touchable observer = pico.getComponent(TouchableObserver.class);
300                    assertNotNull(observer);
301                    observer.touch();
302                    SimpleTouchable touchable = pico.getComponent(SimpleTouchable.class);
303                    assertTrue(touchable.wasTouched);
304            }
305    
306            @Test
307            public void testWillRemoveComponentsWithMatchingKeyFromParent() {
308                    MutablePicoContainer parent = new DefaultPicoContainer();
309                    parent.addComponent("Tom", Cod.class);
310                    parent.addComponent("Dick", Cod.class);
311                    parent.addComponent("Harry", Cod.class);
312                    MutablePicoContainer child = new DefaultPicoContainer(parent);
313                    child.addComponent("Dick", Shark.class);
314                    child.addComponent(Bowl.class);
315                    Bowl bowl = child.getComponent(Bowl.class);
316                    assertEquals(3, bowl.fishes.length);
317                    assertEquals(2, bowl.cods.length);
318            }
319    
320            @Test
321            public void testBowlWithoutTom() {
322                    MutablePicoContainer mpc = new DefaultPicoContainer();
323                    mpc.addComponent("Tom", Cod.class);
324                    mpc.addComponent("Dick", Cod.class);
325                    mpc.addComponent("Harry", Cod.class);
326                    mpc.addComponent(Shark.class);
327                    mpc.addComponent(CollectedBowl.class, CollectedBowl.class,
328                                    new CollectionComponentParameter(Cod.class, false) {
329                                            protected boolean evaluate(ComponentAdapter adapter) {
330                                                    return !"Tom".equals(adapter.getComponentKey());
331                                            }
332                                    }, new CollectionComponentParameter(Fish.class, false));
333                    CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
334                    Cod tom = (Cod) mpc.getComponent("Tom");
335                    assertEquals(4, bowl.fishes.length);
336                    assertEquals(2, bowl.cods.length);
337                    assertFalse(Arrays.asList(bowl.cods).contains(tom));
338            }
339    
340            public static class DependsOnAll {
341                    public DependsOnAll(Set set, SortedSet sortedSet,
342                                    Collection collection, List list, SortedMap sortedMap, Map map
343                    // , ConcurrentMap concurrentMap, Queue queue, BlockingQueue
344                    // blockingQueue
345                    ) {
346                            assertNotNull(set);
347                            assertNotNull(sortedSet);
348                            assertNotNull(collection);
349                            assertNotNull(list);
350                            assertNotNull(sortedMap);
351                            assertNotNull(map);
352                            // assertNotNull(concurrentMap);
353                            // assertNotNull(queue);
354                            // assertNotNull(blockingQueue);
355                    }
356            }
357    
358            @Test
359            public void testDifferentCollectiveTypesAreResolved() {
360                    MutablePicoContainer pico = new DefaultPicoContainer();
361                    CollectionComponentParameter parameter = new CollectionComponentParameter(
362                                    Fish.class, true);
363                    pico.addComponent(DependsOnAll.class, DependsOnAll.class, parameter,
364                                    parameter, parameter, parameter, parameter, parameter);
365                    assertNotNull(pico.getComponent(DependsOnAll.class));
366            }
367    
368            @Test
369            public void testVerify() {
370                    MutablePicoContainer pico = new DefaultPicoContainer();
371                    CollectionComponentParameter parameterNonEmpty = CollectionComponentParameter.ARRAY;
372                    pico.addComponent(Shark.class);
373                    parameterNonEmpty.verify(pico, null, Fish[].class, null, false, null);
374                    try {
375                            parameterNonEmpty
376                                            .verify(pico, null, Cod[].class, null, false, null);
377                            fail("(PicoCompositionException expected");
378                    } catch (PicoCompositionException e) {
379                            assertTrue(e.getMessage().indexOf(Cod.class.getName()) > 0);
380                    }
381                    CollectionComponentParameter parameterEmpty = CollectionComponentParameter.ARRAY_ALLOW_EMPTY;
382                    parameterEmpty.verify(pico, null, Fish[].class, null, false, null);
383                    parameterEmpty.verify(pico, null, Cod[].class, null, false, null);
384            }
385    
386            // PICO-243 : this test will fail if executed on jdk1.3 without
387            // commons-collections
388            @Test
389            public void testOrderOfElementsOfAnArrayDependencyIsPreserved() {
390                    MutablePicoContainer pico = new DefaultPicoContainer();
391                    pico.addComponent("first", "first");
392                    pico.addComponent("second", "second");
393                    pico.addComponent("third", "third");
394                    pico.addComponent("fourth", "fourth");
395                    pico.addComponent("fifth", "fifth");
396                    pico.addComponent(Truc.class);
397    
398                    final List strings = pico.getComponents(String.class);
399                    assertEquals("first", strings.get(0));
400                    assertEquals("second", strings.get(1));
401                    assertEquals("third", strings.get(2));
402                    assertEquals("fourth", strings.get(3));
403                    assertEquals("fifth", strings.get(4));
404    
405                    pico.getComponent(Truc.class);
406            }
407    
408            public static final class Truc {
409                    public Truc(String[] s) {
410                            assertEquals("first", s[0]);
411                            assertEquals("second", s[1]);
412                            assertEquals("third", s[2]);
413                            assertEquals("fourth", s[3]);
414                            assertEquals("fifth", s[4]);
415                    }
416            }
417    
418    }