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    @RunWith(JMock.class)
058    public class CollectionComponentParameterTestCase {
059    
060            private Mockery mockery = mockeryWithCountingNamingScheme();
061    
062            @Test
063            public void testShouldInstantiateArrayOfStrings() {
064                    CollectionComponentParameter ccp = new CollectionComponentParameter();
065                    final ComponentAdapter componentAdapter = mockery
066                                    .mock(ComponentAdapter.class);
067                    final PicoContainer picoContainer = mockery.mock(PicoContainer.class);
068                    mockery.checking(new Expectations() {
069                            {
070                                    atLeast(1).of(componentAdapter).getComponentKey();
071                                    will(returnValue("x"));
072                                    one(picoContainer).getComponentAdapters();
073                                    will(returnValue(new HashSet()));
074                                    one(picoContainer).getComponentAdapters(
075                                                    with(equal(String.class)));
076                                    will(returnValue(Arrays.asList(new InstanceAdapter("y",
077                                                    "Hello", new NullLifecycleStrategy(),
078                                                    new NullComponentMonitor()), new InstanceAdapter("z",
079                                                    "World", new NullLifecycleStrategy(),
080                                                    new NullComponentMonitor()))));
081                                    one(picoContainer).getComponent(with(equal("z")));
082                                    will(returnValue("World"));
083                                    one(picoContainer).getComponent(with(equal("y")));
084                                    will(returnValue("Hello"));
085                                    one(picoContainer).getParent();
086                                    will(returnValue(null));
087                            }
088                    });
089                    List expected = Arrays.asList("Hello", "World");
090                    Collections.sort(expected);
091                    List actual = Arrays.asList((Object[]) ccp.resolveInstance(
092                                    picoContainer, componentAdapter, String[].class,
093                                    null, false, null));
094                    Collections.sort(actual);
095                    assertEquals(expected, actual);
096            }
097    
098            static public interface Fish {
099            }
100    
101            static public class Cod implements Fish {
102                    public String toString() {
103                            return "Cod";
104                    }
105            }
106    
107            static public class Shark implements Fish {
108                    public String toString() {
109                            return "Shark";
110                    }
111            }
112    
113            static public class Bowl {
114                    private final Cod[] cods;
115                    private final Fish[] fishes;
116    
117                    public Bowl(Cod cods[], Fish fishes[]) {
118                            this.cods = cods;
119                            this.fishes = fishes;
120                    }
121            }
122    
123            private MutablePicoContainer getDefaultPicoContainer() {
124                    MutablePicoContainer mpc = new DefaultPicoContainer(new Caching());
125                    mpc.addComponent(Bowl.class);
126                    mpc.addComponent(Cod.class);
127                    mpc.addComponent(Shark.class);
128                    return mpc;
129            }
130    
131            @Test
132            public void testNativeArrays() {
133                    MutablePicoContainer mpc = getDefaultPicoContainer();
134                    Cod cod = mpc.getComponent(Cod.class);
135                    Bowl bowl = mpc.getComponent(Bowl.class);
136                    assertEquals(1, bowl.cods.length);
137                    assertEquals(2, bowl.fishes.length);
138                    assertSame(cod, bowl.cods[0]);
139                    assertNotSame(bowl.fishes[0], bowl.fishes[1]);
140            }
141    
142            @Test
143            public void testCollectionsAreGeneratedOnTheFly() {
144                    MutablePicoContainer mpc = new DefaultPicoContainer();
145                    mpc
146                                    .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            @Test
169            public void testCollections() {
170                    MutablePicoContainer mpc = new DefaultPicoContainer(new Caching());
171                    mpc.addComponent(CollectedBowl.class, CollectedBowl.class,
172                                    new ComponentParameter(Cod.class, false),
173                                    new ComponentParameter(Fish.class, false));
174                    mpc.addComponent(Cod.class);
175                    mpc.addComponent(Shark.class);
176                    Cod cod = mpc.getComponent(Cod.class);
177                    CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
178                    assertEquals(1, bowl.cods.length);
179                    assertEquals(2, bowl.fishes.length);
180                    assertSame(cod, bowl.cods[0]);
181                    assertNotSame(bowl.fishes[0], bowl.fishes[1]);
182            }
183    
184            static public class MappedBowl {
185                    private final Fish[] fishes;
186    
187                    public MappedBowl(Map map) {
188                            Collection collection = map.values();
189                            this.fishes = (Fish[]) collection.toArray(new Fish[collection
190                                            .size()]);
191                    }
192            }
193    
194            @Test
195            public void testMaps() {
196                    MutablePicoContainer mpc = new DefaultPicoContainer();
197                    mpc.addComponent(MappedBowl.class, MappedBowl.class,
198                                    new ComponentParameter(Fish.class, false));
199                    mpc.addComponent(Cod.class);
200                    mpc.addComponent(Shark.class);
201                    MappedBowl bowl = mpc.getComponent(MappedBowl.class);
202                    assertEquals(2, bowl.fishes.length);
203                    assertNotSame(bowl.fishes[0], bowl.fishes[1]);
204            }
205    
206            public static class UngenericCollectionBowl {
207                    public UngenericCollectionBowl(Collection fish) {
208                    }
209            }
210    
211            @Test
212            public void testShouldNotInstantiateCollectionForUngenericCollectionParameters() {
213                    MutablePicoContainer pico = getDefaultPicoContainer();
214                    pico.addComponent(UngenericCollectionBowl.class);
215                    try {
216                            pico.getComponent(UngenericCollectionBowl.class);
217                            fail();
218                    } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
219                            // expected
220                    }
221            }
222    
223            public static class AnotherGenericCollectionBowl {
224                    private final String[] strings;
225    
226                    public AnotherGenericCollectionBowl(String[] strings) {
227                            this.strings = strings;
228                    }
229    
230                    public String[] getStrings() {
231                            return strings;
232                    }
233            }
234    
235            @Test
236            public void testShouldFailWhenThereAreNoComponentsToPutInTheArray() {
237                    MutablePicoContainer pico = getDefaultPicoContainer();
238                    pico.addComponent(AnotherGenericCollectionBowl.class);
239                    try {
240                            pico.getComponent(AnotherGenericCollectionBowl.class);
241                            fail();
242                    } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
243                            // expected
244                    }
245            }
246    
247            @Test
248            public void testAllowsEmptyArraysIfEspeciallySet() {
249                    MutablePicoContainer pico = getDefaultPicoContainer();
250                    pico.addComponent(AnotherGenericCollectionBowl.class,
251                                    AnotherGenericCollectionBowl.class,
252                                    ComponentParameter.ARRAY_ALLOW_EMPTY);
253                    AnotherGenericCollectionBowl bowl = pico
254                                    .getComponent(AnotherGenericCollectionBowl.class);
255                    assertNotNull(bowl);
256                    assertEquals(0, bowl.strings.length);
257            }
258    
259            public static class TouchableObserver implements Touchable {
260                    private final Touchable[] touchables;
261    
262                    public TouchableObserver(Touchable[] touchables) {
263                            this.touchables = touchables;
264    
265                    }
266    
267                    public void touch() {
268                            for (Touchable touchable : touchables) {
269                                    touchable.touch();
270                            }
271                    }
272            }
273    
274            @Test
275            public void testWillOmitSelfFromCollection() {
276                    MutablePicoContainer pico = getDefaultPicoContainer();
277                    pico.addComponent(SimpleTouchable.class);
278                    pico.addComponent(TouchableObserver.class);
279                    Touchable observer = pico.getComponent(TouchableObserver.class);
280                    assertNotNull(observer);
281                    observer.touch();
282                    SimpleTouchable touchable = pico.getComponent(SimpleTouchable.class);
283                    assertTrue(touchable.wasTouched);
284            }
285    
286            @Test
287            public void testWillRemoveComponentsWithMatchingKeyFromParent() {
288                    MutablePicoContainer parent = new DefaultPicoContainer();
289                    parent.addComponent("Tom", Cod.class);
290                    parent.addComponent("Dick", Cod.class);
291                    parent.addComponent("Harry", Cod.class);
292                    MutablePicoContainer child = new DefaultPicoContainer(parent);
293                    child.addComponent("Dick", Shark.class);
294                    child.addComponent(Bowl.class);
295                    Bowl bowl = child.getComponent(Bowl.class);
296                    assertEquals(3, bowl.fishes.length);
297                    assertEquals(2, bowl.cods.length);
298            }
299    
300            @Test
301            public void testBowlWithoutTom() {
302                    MutablePicoContainer mpc = new DefaultPicoContainer();
303                    mpc.addComponent("Tom", Cod.class);
304                    mpc.addComponent("Dick", Cod.class);
305                    mpc.addComponent("Harry", Cod.class);
306                    mpc.addComponent(Shark.class);
307                    mpc.addComponent(CollectedBowl.class, CollectedBowl.class,
308                                    new CollectionComponentParameter(Cod.class, false) {
309                                            protected boolean evaluate(ComponentAdapter adapter) {
310                                                    return !"Tom".equals(adapter.getComponentKey());
311                                            }
312                                    }, new CollectionComponentParameter(Fish.class, false));
313                    CollectedBowl bowl = mpc.getComponent(CollectedBowl.class);
314                    Cod tom = (Cod) mpc.getComponent("Tom");
315                    assertEquals(4, bowl.fishes.length);
316                    assertEquals(2, bowl.cods.length);
317                    assertFalse(Arrays.asList(bowl.cods).contains(tom));
318            }
319    
320            public static class DependsOnAll {
321                    public DependsOnAll(Set set, SortedSet sortedSet,
322                                    Collection collection, List list, SortedMap sortedMap, Map map
323                    // , ConcurrentMap concurrentMap, Queue queue, BlockingQueue
324                    // blockingQueue
325                    ) {
326                            assertNotNull(set);
327                            assertNotNull(sortedSet);
328                            assertNotNull(collection);
329                            assertNotNull(list);
330                            assertNotNull(sortedMap);
331                            assertNotNull(map);
332                            // assertNotNull(concurrentMap);
333                            // assertNotNull(queue);
334                            // assertNotNull(blockingQueue);
335                    }
336            }
337    
338            @Test
339            public void testDifferentCollectiveTypesAreResolved() {
340                    MutablePicoContainer pico = new DefaultPicoContainer();
341                    CollectionComponentParameter parameter = new CollectionComponentParameter(
342                                    Fish.class, true);
343                    pico.addComponent(DependsOnAll.class, DependsOnAll.class, parameter,
344                                    parameter, parameter, parameter, parameter, parameter);
345                    assertNotNull(pico.getComponent(DependsOnAll.class));
346            }
347    
348            @Test
349            public void testVerify() {
350                    MutablePicoContainer pico = new DefaultPicoContainer();
351                    CollectionComponentParameter parameterNonEmpty = CollectionComponentParameter.ARRAY;
352                    pico.addComponent(Shark.class);
353                    parameterNonEmpty.verify(pico, null, Fish[].class, null, false, null);
354                    try {
355                            parameterNonEmpty
356                                            .verify(pico, null, Cod[].class, null, false, null);
357                            fail("(PicoCompositionException expected");
358                    } catch (PicoCompositionException e) {
359                            assertTrue(e.getMessage().indexOf(Cod.class.getName()) > 0);
360                    }
361                    CollectionComponentParameter parameterEmpty = CollectionComponentParameter.ARRAY_ALLOW_EMPTY;
362                    parameterEmpty.verify(pico, null, Fish[].class, null, false, null);
363                    parameterEmpty.verify(pico, null, Cod[].class, null, false, null);
364            }
365    
366            // PICO-243 : this test will fail if executed on jdk1.3 without
367            // commons-collections
368            @Test
369            public void testOrderOfElementsOfAnArrayDependencyIsPreserved() {
370                    MutablePicoContainer pico = new DefaultPicoContainer();
371                    pico.addComponent("first", "first");
372                    pico.addComponent("second", "second");
373                    pico.addComponent("third", "third");
374                    pico.addComponent("fourth", "fourth");
375                    pico.addComponent("fifth", "fifth");
376                    pico.addComponent(Truc.class);
377    
378                    final List strings = pico.getComponents(String.class);
379                    assertEquals("first", strings.get(0));
380                    assertEquals("second", strings.get(1));
381                    assertEquals("third", strings.get(2));
382                    assertEquals("fourth", strings.get(3));
383                    assertEquals("fifth", strings.get(4));
384    
385                    pico.getComponent(Truc.class);
386            }
387    
388            public static final class Truc {
389                    public Truc(String[] s) {
390                            assertEquals("first", s[0]);
391                            assertEquals("second", s[1]);
392                            assertEquals("third", s[2]);
393                            assertEquals("fourth", s[3]);
394                            assertEquals("fifth", s[4]);
395                    }
396            }
397    
398    }