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