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