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 Joerg Schaible                                           *
009     *****************************************************************************/
010    package org.picocontainer.tck;
011    
012    import com.thoughtworks.xstream.XStream;
013    import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
014    import com.thoughtworks.xstream.io.xml.XppDriver;
015    
016    import junit.framework.Assert;
017    import junit.framework.AssertionFailedError;
018    
019    import org.picocontainer.ComponentAdapter;
020    import org.picocontainer.MutablePicoContainer;
021    import org.picocontainer.Parameter;
022    import org.picocontainer.PicoContainer;
023    import org.picocontainer.PicoCompositionException;
024    import org.picocontainer.injectors.AdaptiveInjectionFactory;
025    import org.picocontainer.visitors.AbstractPicoVisitor;
026    import org.picocontainer.ComponentFactory;
027    import org.picocontainer.parameters.ConstantParameter;
028    import org.picocontainer.injectors.ConstructorInjectionFactory;
029    import org.picocontainer.injectors.AbstractInjector;
030    import org.picocontainer.behaviors.AbstractBehavior;
031    import org.picocontainer.behaviors.CachingBehavior;
032    import org.picocontainer.DefaultPicoContainer;
033    import org.picocontainer.LifecycleStrategy;
034    import org.picocontainer.ObjectReference;
035    
036    import org.jmock.MockObjectTestCase;
037    
038    import java.io.ByteArrayInputStream;
039    import java.io.ByteArrayOutputStream;
040    import java.io.IOException;
041    import java.io.ObjectInputStream;
042    import java.io.ObjectOutputStream;
043    import java.lang.reflect.Constructor;
044    import java.util.ArrayList;
045    import java.util.Collection;
046    import java.util.HashSet;
047    import java.util.Iterator;
048    import java.util.LinkedList;
049    import java.util.List;
050    import java.util.Set;
051    
052    
053    /**
054     * Test suite for a ComponentAdapter implementation.
055     * 
056     * @author Jörg Schaible
057     */
058    public abstract class AbstractComponentAdapterTestCase extends MockObjectTestCase {
059    
060        public static final int SERIALIZABLE = 1;
061        public static final int VERIFYING = 2;
062        public static final int INSTANTIATING = 4;
063        public static final int RESOLVING = 8;
064    
065        protected abstract Class getComponentAdapterType();
066    
067        protected int getComponentAdapterNature() {
068            return SERIALIZABLE | VERIFYING | INSTANTIATING | RESOLVING;
069        }
070    
071        protected ComponentFactory createDefaultComponentAdapterFactory() {
072            return new AdaptiveInjectionFactory();
073        }
074    
075        // ============================================
076        // Default
077        // ============================================
078    
079        /**
080         * Prepare the test <em>verifyWithoutDependencyWorks</em>.
081         * 
082         * @param picoContainer container, may probably not be used.
083         * @return a ComponentAdapter of the type to test for a component without dependencies. Registration in the pico is
084         *         not necessary.
085         */
086        protected abstract ComponentAdapter prepDEF_verifyWithoutDependencyWorks(MutablePicoContainer picoContainer);
087    
088        final public void testDEF_verifyWithoutDependencyWorks() {
089            final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
090            final ComponentAdapter componentAdapter = prepDEF_verifyWithoutDependencyWorks(picoContainer);
091            assertSame(getComponentAdapterType(), componentAdapter.getClass());
092            componentAdapter.verify(picoContainer);
093        }
094    
095        /**
096         * Prepare the test <em>verifyDoesNotInstantiate</em>.
097         * 
098         * @param picoContainer container, may probably not be used.
099         * @return a ComponentAdapter of the type to test for a component that may throw on instantiation. Registration in
100         *         the pico is not necessary.
101         */
102        protected abstract ComponentAdapter prepDEF_verifyDoesNotInstantiate(MutablePicoContainer picoContainer);
103    
104        final public void testDEF_verifyDoesNotInstantiate() {
105            final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
106            final ComponentAdapter componentAdapter = prepDEF_verifyDoesNotInstantiate(picoContainer);
107            assertSame(getComponentAdapterType(), componentAdapter.getClass());
108            final ComponentAdapter notInstantiatablecomponentAdapter = new NotInstantiatableBehavior(
109                    componentAdapter);
110            final PicoContainer wrappedPicoContainer = wrapComponentInstances(
111                    NotInstantiatableBehavior.class, picoContainer, null);
112            notInstantiatablecomponentAdapter.verify(wrappedPicoContainer);
113        }
114    
115        /**
116         * Prepare the test <em>visitable</em>.
117         * 
118         * @return a ComponentAdapter of the type to test. If the ComponentAdapter supports {@link Parameter}, you have to
119         *         select a component, that have some.
120         */
121        protected abstract ComponentAdapter prepDEF_visitable();
122    
123        final public void testDEF_visitable() {
124            final ComponentAdapter componentAdapter = prepDEF_visitable();
125            final Class type = getComponentAdapterType();
126            assertSame(type, componentAdapter.getClass());
127            boolean hasParameters = supportsParameters(type);
128            final RecordingVisitor visitor = new RecordingVisitor();
129            visitor.traverse(componentAdapter);
130            final List visitedElements = new ArrayList(visitor.getVisitedElements());
131            assertSame(componentAdapter, visitedElements.get(0));
132            if (hasParameters) {
133                hasParameters = false;
134                for (final Iterator iter = visitedElements.iterator(); iter.hasNext() && !hasParameters;) {
135                    hasParameters = Parameter.class.isAssignableFrom(iter.next().getClass());
136                }
137                assertTrue("ComponentAdapter " + type + " supports parameters, provide some", hasParameters);
138            }
139        }
140    
141        /**
142         * Prepare the test <em>isAbleToTakeParameters</em>. Overload this function, if the ComponentAdapter to test
143         * supports {@link Parameter}.
144         * 
145         * @param picoContainer container, may probably not be used.
146         * @return a ComponentAdapter of the type to test. Select a component, that has some parameters. Registration in the
147         *         pico is not necessary.
148         */
149        protected ComponentAdapter prepDEF_isAbleToTakeParameters(MutablePicoContainer picoContainer) {
150            final Class type = getComponentAdapterType();
151            boolean hasParameters = supportsParameters(type);
152            if (hasParameters) {
153                fail("You have to overwrite this method for a useful test");
154            }
155            return null;
156        }
157    
158        final public void testDEF_isAbleToTakeParameters() {
159            final Class type = getComponentAdapterType();
160            boolean hasParameters = supportsParameters(type);
161            if (hasParameters) {
162                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
163                final ComponentAdapter componentAdapter = prepDEF_isAbleToTakeParameters(picoContainer);
164                assertSame(getComponentAdapterType(), componentAdapter.getClass());
165                final RecordingVisitor visitor = new RecordingVisitor();
166                visitor.traverse(componentAdapter);
167                final List visitedElements = visitor.getVisitedElements();
168                if (hasParameters) {
169                    hasParameters = false;
170                    for (final Iterator iter = visitedElements.iterator(); iter.hasNext() && !hasParameters;) {
171                        hasParameters = Parameter.class.isAssignableFrom(iter.next().getClass());
172                    }
173                    assertTrue("ComponentAdapter " + type + " supports parameters, provide some", hasParameters);
174                }
175                final Object instance = componentAdapter.getComponentInstance(picoContainer);
176                assertNotNull(instance);
177            }
178        }
179    
180        // ============================================
181        // Serializable
182        // ============================================
183    
184        /**
185         * Prepare the test <em>isSerializable</em>. Overload this function, if the ComponentAdapter supports
186         * serialization.
187         * 
188         * @param picoContainer container, may probably not be used.
189         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
190         */
191        protected ComponentAdapter prepSER_isSerializable(MutablePicoContainer picoContainer) {
192            throw new AssertionFailedError("You have to overwrite this method for a useful test");
193        }
194    
195        final public void testSER_isSerializable() throws IOException, ClassNotFoundException {
196            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
197                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
198                final ComponentAdapter componentAdapter = prepSER_isSerializable(picoContainer);
199                assertSame(getComponentAdapterType(), componentAdapter.getClass());
200                final Object instance = componentAdapter.getComponentInstance(picoContainer);
201                assertNotNull(instance);
202                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
203                final ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
204                outputStream.writeObject(componentAdapter);
205                outputStream.close();
206                final ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream
207                        .toByteArray()));
208                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)inputStream.readObject();
209                inputStream.close();
210                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
211                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer);
212                assertNotNull(instanceAfterSerialization);
213                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
214            }
215        }
216    
217        /**
218         * Prepare the test <em>isXStreamSerializable</em>. Overload this function, if the ComponentAdapter supports
219         * serialization.
220         * 
221         * @param picoContainer container, may probably not be used.
222         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
223         */
224        protected ComponentAdapter prepSER_isXStreamSerializable(MutablePicoContainer picoContainer) {
225            throw new AssertionFailedError("You have to overwrite this method for a useful test");
226        }
227    
228        final public void testSER_isXStreamSerializableWithPureReflection() {
229            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
230                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
231                final ComponentAdapter componentAdapter = prepSER_isXStreamSerializable(picoContainer);
232                assertSame(getComponentAdapterType(), componentAdapter.getClass());
233                final Object instance = componentAdapter.getComponentInstance(picoContainer);
234                assertNotNull(instance);
235                final XStream xstream = new XStream(new PureJavaReflectionProvider(), new XppDriver());
236                final String xml = xstream.toXML(componentAdapter);
237                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)xstream.fromXML(xml);
238                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
239                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer);
240                assertNotNull(instanceAfterSerialization);
241                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
242            }
243        }
244    
245        final public void testSER_isXStreamSerializable() {
246            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
247                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
248                final ComponentAdapter componentAdapter = prepSER_isXStreamSerializable(picoContainer);
249                assertSame(getComponentAdapterType(), componentAdapter.getClass());
250                final Object instance = componentAdapter.getComponentInstance(picoContainer);
251                assertNotNull(instance);
252                final XStream xstream = new XStream(new XppDriver());
253                final String xml = xstream.toXML(componentAdapter);
254                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)xstream.fromXML(xml);
255                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
256                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer);
257                assertNotNull(instanceAfterSerialization);
258                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
259            }
260        }
261    
262        // ============================================
263        // Verifying
264        // ============================================
265    
266        /**
267         * Prepare the test <em>verificationFailsWithUnsatisfiedDependency</em>. Overload this function, if the
268         * ComponentAdapter's verification can fail e.g. due to an unresolved dependency.
269         * 
270         * @param picoContainer container, may probably not be used.
271         * @return a ComponentAdapter of the type to test, that fails for the verification, e.g. because of a compoennt with
272         *         missing dependencies. Registration in the pico is not necessary.
273         */
274        protected ComponentAdapter prepVER_verificationFails(MutablePicoContainer picoContainer) {
275            throw new AssertionFailedError("You have to overwrite this method for a useful test");
276        }
277    
278        final public void testVER_verificationFails() {
279            if ((getComponentAdapterNature() & VERIFYING) > 0) {
280                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
281                final ComponentAdapter componentAdapter = prepVER_verificationFails(picoContainer);
282                assertSame(getComponentAdapterType(), componentAdapter.getClass());
283                try {
284                    componentAdapter.verify(picoContainer);
285                    fail("PicoCompositionException expected");
286                } catch (PicoCompositionException e) {
287                } catch (Exception e) {
288                    fail("PicoCompositionException expected, but got " + e.getClass().getName());
289                }
290                try {
291                    componentAdapter.getComponentInstance(picoContainer);
292                    fail("PicoCompositionException or PicoCompositionException expected");
293                } catch (PicoCompositionException e) {
294                } catch (Exception e) {
295                    fail("PicoCompositionException or PicoCompositionException expected, but got "
296                            + e.getClass().getName());
297                }
298            }
299        }
300    
301        // ============================================
302        // Instantiating
303        // ============================================
304    
305        /**
306         * Prepare the test <em>createsNewInstances</em>. Overload this function, if the ComponentAdapter is
307         * instantiating. It should create a new instance with every call.
308         * 
309         * @param picoContainer container, may probably not be used.
310         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
311         */
312        protected ComponentAdapter prepINS_createsNewInstances(MutablePicoContainer picoContainer) {
313            throw new AssertionFailedError("You have to overwrite this method for a useful test");
314        }
315    
316        final public void testINS_createsNewInstances() {
317            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
318                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
319                final ComponentAdapter componentAdapter = prepINS_createsNewInstances(picoContainer);
320                assertSame(getComponentAdapterType(), componentAdapter.getClass());
321                final Object instance = componentAdapter.getComponentInstance(picoContainer);
322                assertNotNull(instance);
323                assertNotSame(instance, componentAdapter.getComponentInstance(picoContainer));
324                assertSame(instance.getClass(), componentAdapter.getComponentInstance(picoContainer).getClass());
325            }
326        }
327    
328        /**
329         * Prepare the test <em>errorIsRethrown</em>. Overload this function, if the ComponentAdapter is instantiating.
330         * 
331         * @param picoContainer container, may probably not be used.
332         * @return a ComponentAdapter of the type to test with a component that fails with an {@link Error} at
333         *         instantiation. Registration in the pico is not necessary.
334         */
335        protected ComponentAdapter prepINS_errorIsRethrown(MutablePicoContainer picoContainer) {
336            throw new AssertionFailedError("You have to overwrite this method for a useful test");
337        }
338    
339        final public void testINS_errorIsRethrown() {
340            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
341                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
342                final ComponentAdapter componentAdapter = prepINS_errorIsRethrown(picoContainer);
343                assertSame(getComponentAdapterType(), componentAdapter.getClass());
344                try {
345                    componentAdapter.getComponentInstance(picoContainer);
346                    fail("Thrown Error excpected");
347                } catch (final Error e) {
348                    assertEquals("test", e.getMessage());
349                }
350            }
351        }
352    
353        /**
354         * Prepare the test <em>runtimeExceptionIsRethrown</em>. Overload this function, if the ComponentAdapter is
355         * instantiating.
356         * 
357         * @param picoContainer container, may probably not be used.
358         * @return a ComponentAdapter of the type to test with a component that fails with a {@link RuntimeException} at
359         *         instantiation. Registration in the pico is not necessary.
360         */
361        protected ComponentAdapter prepINS_runtimeExceptionIsRethrown(MutablePicoContainer picoContainer) {
362            throw new AssertionFailedError("You have to overwrite this method for a useful test");
363        }
364    
365        final public void testINS_runtimeExceptionIsRethrown() {
366            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
367                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
368                final ComponentAdapter componentAdapter = prepINS_runtimeExceptionIsRethrown(picoContainer);
369                assertSame(getComponentAdapterType(), componentAdapter.getClass());
370                try {
371                    componentAdapter.getComponentInstance(picoContainer);
372                    fail("Thrown RuntimeException excpected");
373                } catch (final RuntimeException e) {
374                    assertEquals("test", e.getMessage());
375                }
376            }
377        }
378    
379        /**
380         * Prepare the test <em>normalExceptionIsRethrownInsidePicoInvocationTargetInitializationException</em>. Overload
381         * this function, if the ComponentAdapter is instantiating.
382         * 
383         * @param picoContainer container, may probably not be used.
384         * @return a ComponentAdapter of the type to test with a component that fails with a
385         *         {@link PicoCompositionException} at instantiation. Registration in the pico is not
386         *         necessary.
387         */
388        protected ComponentAdapter prepINS_normalExceptionIsRethrownInsidePicoInitializationException(
389                MutablePicoContainer picoContainer) {
390            throw new AssertionFailedError("You have to overwrite this method for a useful test");
391        }
392    
393        final public void testINS_normalExceptionIsRethrownInsidePicoInitializationException() {
394            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
395                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
396                final ComponentAdapter componentAdapter = prepINS_normalExceptionIsRethrownInsidePicoInitializationException(picoContainer);
397                assertSame(getComponentAdapterType(), componentAdapter.getClass());
398                try {
399                    componentAdapter.getComponentInstance(picoContainer);
400                    fail("Thrown PicoCompositionException excpected");
401                } catch (final PicoCompositionException e) {
402                    assertTrue(e.getCause() instanceof Exception);
403                    assertTrue(e.getCause().getMessage().endsWith("test"));
404                }
405            }
406        }
407    
408        // ============================================
409        // Resolving
410        // ============================================
411    
412        /**
413         * Prepare the test <em>dependenciesAreResolved</em>. Overload this function, if the ComponentAdapter is resolves
414         * dependencies.
415         * 
416         * @param picoContainer container, used to register dependencies.
417         * @return a ComponentAdapter of the type to test with a component that has dependencies. Registration in the pico
418         *         is not necessary.
419         */
420        protected ComponentAdapter prepRES_dependenciesAreResolved(MutablePicoContainer picoContainer) {
421            throw new AssertionFailedError("You have to overwrite this method for a useful test");
422        }
423    
424        final public void testRES_dependenciesAreResolved() {
425            if ((getComponentAdapterNature() & RESOLVING) > 0) {
426                final List dependencies = new LinkedList();
427                final Object[] wrapperDependencies = new Object[]{dependencies};
428                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
429                final ComponentAdapter componentAdapter = prepRES_dependenciesAreResolved(picoContainer);
430                assertSame(getComponentAdapterType(), componentAdapter.getClass());
431                assertFalse(picoContainer.getComponentAdapters().contains(componentAdapter));
432                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
433                        CollectingBehavior.class, picoContainer, wrapperDependencies);
434                final Object instance = componentAdapter.getComponentInstance(wrappedPicoContainer);
435                assertNotNull(instance);
436                assertTrue(dependencies.size() > 0);
437            }
438        }
439    
440        /**
441         * Prepare the test <em>failingVerificationWithCyclicDependencyException</em>. Overload this function, if the
442         * ComponentAdapter is resolves dependencies.
443         * 
444         * @param picoContainer container, used to register dependencies.
445         * @return a ComponentAdapter of the type to test with a component that has cyclic dependencies. You have to
446         *         register the component itself in the pico.
447         */
448        protected ComponentAdapter prepRES_failingVerificationWithCyclicDependencyException(
449                MutablePicoContainer picoContainer) {
450            throw new AssertionFailedError("You have to overwrite this method for a useful test");
451        }
452    
453        final public void testRES_failingVerificationWithCyclicDependencyException() {
454            if ((getComponentAdapterNature() & RESOLVING) > 0) {
455                final Set cycleInstances = new HashSet();
456                final ObjectReference cycleCheck = new CachingBehavior.SimpleReference();
457                final Object[] wrapperDependencies = new Object[]{cycleInstances, cycleCheck};
458                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
459                final ComponentAdapter componentAdapter = prepRES_failingVerificationWithCyclicDependencyException(picoContainer);
460                assertSame(getComponentAdapterType(), componentAdapter.getClass());
461                assertTrue(picoContainer.getComponentAdapters().contains(componentAdapter));
462                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
463                        CycleDetectorBehavior.class, picoContainer, wrapperDependencies);
464                try {
465                    componentAdapter.verify(wrappedPicoContainer);
466                    fail("Thrown PicoVerificationException excpected");
467                } catch (final AbstractInjector.CyclicDependencyException cycle) {
468                    final Class[] dependencies = cycle.getDependencies();
469                    assertSame(dependencies[0], dependencies[dependencies.length - 1]);
470                }
471            }
472        }
473    
474        /**
475         * Prepare the test <em>failingInstantiationWithCyclicDependencyException</em>. Overload this function, if the
476         * ComponentAdapter is resolves dependencies.
477         * 
478         * @param picoContainer container, used to register dependencies.
479         * @return a ComponentAdapter of the type to test with a component that has cyclic dependencies. You have to
480         *         register the component itself in the pico.
481         */
482        protected ComponentAdapter prepRES_failingInstantiationWithCyclicDependencyException(
483                MutablePicoContainer picoContainer) {
484            throw new AssertionFailedError("You have to overwrite this method for a useful test");
485        }
486    
487        final public void testRES_failingInstantiationWithCyclicDependencyException() {
488            if ((getComponentAdapterNature() & RESOLVING) > 0) {
489                final Set cycleInstances = new HashSet();
490                final ObjectReference cycleCheck = new CachingBehavior.SimpleReference();
491                final Object[] wrapperDependencies = new Object[]{cycleInstances, cycleCheck};
492                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
493                final ComponentAdapter componentAdapter = prepRES_failingInstantiationWithCyclicDependencyException(picoContainer);
494                assertSame(getComponentAdapterType(), componentAdapter.getClass());
495                assertTrue(picoContainer.getComponentAdapters().contains(componentAdapter));
496                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
497                        CycleDetectorBehavior.class, picoContainer, wrapperDependencies);
498                try {
499                    componentAdapter.getComponentInstance(wrappedPicoContainer);
500                    fail("Thrown CyclicDependencyException excpected");
501                } catch (final AbstractInjector.CyclicDependencyException e) {
502                    final Class[] dependencies = e.getDependencies();
503                    assertSame(dependencies[0], dependencies[dependencies.length - 1]);
504                }
505            }
506        }
507    
508        // ============================================
509        // Model & Helpers
510        // ============================================
511    
512        static class RecordingVisitor extends AbstractPicoVisitor {
513            private final List visitedElements = new LinkedList();
514    
515            public void visitContainer(PicoContainer pico) {
516                visitedElements.add(pico);
517            }
518    
519            public void visitComponentAdapter(ComponentAdapter componentAdapter) {
520                visitedElements.add(componentAdapter);
521            }
522    
523            public void visitParameter(Parameter parameter) {
524                visitedElements.add(parameter);
525            }
526    
527            List getVisitedElements() {
528                return visitedElements;
529            }
530        }
531    
532        static public class NotInstantiatableBehavior extends AbstractBehavior {
533            public NotInstantiatableBehavior(final ComponentAdapter delegate) {
534                super(delegate);
535            }
536    
537            public Object getComponentInstance(final PicoContainer container) {
538                Assert.fail("Not instantiatable");
539                return null;
540            }
541        }
542    
543        static public class CollectingBehavior extends AbstractBehavior {
544            final List list;
545    
546            public CollectingBehavior(final ComponentAdapter delegate, final List list) {
547                super(delegate);
548                this.list = list;
549            }
550    
551            public Object getComponentInstance(final PicoContainer container) {
552                final Object result = super.getComponentInstance(container);
553                list.add(result);
554                return result;
555            }
556        }
557    
558        static public class CycleDetectorBehavior extends AbstractBehavior {
559            private final Set set;
560            private final ObjectReference reference;
561    
562            public CycleDetectorBehavior(
563                    final ComponentAdapter delegate, final Set set, final ObjectReference reference) {
564                super(delegate);
565                this.set = set;
566                this.reference = reference;
567            }
568    
569            public Object getComponentInstance(final PicoContainer container) {
570                if (set.contains(this)) {
571                    reference.set(this);
572                } else {
573                    set.add(this);
574                }
575                return super.getComponentInstance(container);
576            }
577        }
578    
579        public static final class RecordingLifecycleStrategy implements LifecycleStrategy {
580            private final StringBuffer recorder;
581            
582            public RecordingLifecycleStrategy(StringBuffer recorder) {
583                this.recorder = recorder;
584            }
585        
586            public void start(Object component) {
587                recorder.append("<start");
588            }
589        
590            public void stop(Object component) {
591                recorder.append("<stop");
592            }
593        
594            public void dispose(Object component) {
595                recorder.append("<dispose");
596            }
597            
598            public boolean hasLifecycle(Class type) {
599                return true;
600            }
601            
602            public String recording() {
603                return recorder.toString();
604            }
605        }
606    
607        final protected PicoContainer wrapComponentInstances(
608                final Class decoratingComponentAdapterClass, final PicoContainer picoContainer,
609                final Object[] wrapperDependencies) {
610            assertTrue(AbstractBehavior.class.isAssignableFrom(decoratingComponentAdapterClass));
611            final MutablePicoContainer mutablePicoContainer = new DefaultPicoContainer();
612            final int size = (wrapperDependencies != null ? wrapperDependencies.length : 0) + 1;
613            final Collection allComponentAdapters = picoContainer.getComponentAdapters();
614            for (Object allComponentAdapter : allComponentAdapters) {
615                final Parameter[] parameters = new Parameter[size];
616                parameters[0] = new ConstantParameter(allComponentAdapter);
617                for (int i = 1; i < parameters.length; i++) {
618                    parameters[i] = new ConstantParameter(wrapperDependencies[i - 1]);
619                }
620                final MutablePicoContainer instantiatingPicoContainer = new DefaultPicoContainer(
621                    new ConstructorInjectionFactory());
622                instantiatingPicoContainer.addComponent(
623                    "decorator", decoratingComponentAdapterClass, parameters);
624                mutablePicoContainer.addAdapter((ComponentAdapter)instantiatingPicoContainer
625                    .getComponent("decorator"));
626            }
627            return mutablePicoContainer;
628        }
629    
630        private boolean supportsParameters(final Class type) {
631            boolean hasParameters = false;
632            final Constructor[] constructors = type.getConstructors();
633            for (int i = 0; i < constructors.length && !hasParameters; i++) {
634                final Constructor constructor = constructors[i];
635                final Class[] parameterTypes = constructor.getParameterTypes();
636                for (final Class parameterType : parameterTypes) {
637                    if (Parameter.class.isAssignableFrom(parameterType)
638                        || (parameterType.isArray() && Parameter.class.isAssignableFrom(parameterType
639                        .getComponentType()))) {
640                        hasParameters = true;
641                        break;
642                    }
643                }
644            }
645            return hasParameters;
646        }
647    }