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 *****************************************************************************/
010package org.picocontainer;
011
012import org.picocontainer.adapters.AbstractAdapter;
013import org.picocontainer.adapters.InstanceAdapter;
014import org.picocontainer.behaviors.AbstractBehaviorFactory;
015import org.picocontainer.behaviors.AdaptingBehavior;
016import org.picocontainer.behaviors.Cached;
017import org.picocontainer.behaviors.Caching;
018import org.picocontainer.behaviors.HiddenImplementation;
019import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
020import org.picocontainer.containers.AbstractDelegatingPicoContainer;
021import org.picocontainer.containers.EmptyPicoContainer;
022import org.picocontainer.containers.ImmutablePicoContainer;
023import org.picocontainer.converters.BuiltInConverters;
024import org.picocontainer.converters.ConvertsNothing;
025import org.picocontainer.injectors.AbstractInjector;
026import org.picocontainer.injectors.AdaptingInjection;
027import org.picocontainer.injectors.FactoryInjector;
028import org.picocontainer.lifecycle.DefaultLifecycleState;
029import org.picocontainer.lifecycle.LifecycleState;
030import org.picocontainer.lifecycle.StartableLifecycleStrategy;
031import org.picocontainer.monitors.NullComponentMonitor;
032import org.picocontainer.parameters.DefaultConstructorParameter;
033
034import java.io.Serializable;
035import java.lang.annotation.Annotation;
036import java.lang.ref.WeakReference;
037import java.lang.reflect.Type;
038import java.util.ArrayList;
039import java.util.Collection;
040import java.util.Collections;
041import java.util.Enumeration;
042import java.util.HashMap;
043import java.util.HashSet;
044import java.util.LinkedHashSet;
045import java.util.List;
046import java.util.Map;
047import java.util.Properties;
048import java.util.Set;
049
050import static org.picocontainer.parameters.BasicComponentParameter.findInjectorOrInstanceAdapter;
051import static org.picocontainer.parameters.BasicComponentParameter.makeFoundAmbiguousStrings;
052
053/**
054 * <p/>
055 * The Standard {@link PicoContainer}/{@link MutablePicoContainer} implementation.
056 * Constructing a container c with a parent p container will cause c to look up components
057 * in p if they cannot be found inside c itself.
058 * </p>
059 * <p/>
060 * Using {@link Class} objects as keys to the various registerXXX() methods makes
061 * a subtle semantic difference:
062 * </p>
063 * <p/>
064 * If there are more than one registered components of the same type and one of them are
065 * registered with a {@link java.lang.Class} key of the corresponding type, this addComponent
066 * will take precedence over other components during type resolution.
067 * </p>
068 * <p/>
069 * Another place where keys that are classes make a subtle difference is in
070 * {@link HiddenImplementation}.
071 * </p>
072 * <p/>
073 * This implementation of {@link MutablePicoContainer} also supports
074 * {@link ComponentMonitorStrategy}.
075 * </p>
076 *
077 * @author Paul Hammant
078 * @author Aslak Helles&oslash;y
079 * @author Jon Tirs&eacute;n
080 * @author Thomas Heller
081 * @author Mauro Talevi
082 */
083@SuppressWarnings("serial")
084public class DefaultPicoContainer implements MutablePicoContainer, Converting, ComponentMonitorStrategy, Serializable  {
085
086    private String name;
087
088        /**
089         * Component factory instance.
090         */
091        protected final ComponentFactory componentFactory;
092    
093        /**
094         * Parent picocontainer
095         */
096    private PicoContainer parent;
097    
098    /**
099     * All picocontainer children.
100     */
101    private final Set<PicoContainer> children = new HashSet<PicoContainer>();
102
103    /**
104     * Current state of the container.
105     */
106    private LifecycleState lifecycleState = new DefaultLifecycleState();
107
108    /**
109     * Keeps track of child containers started status.
110     */
111    private final Set<WeakReference<PicoContainer>> childrenStarted = new HashSet<WeakReference<PicoContainer>>();
112
113    /**
114     * Lifecycle strategy instance.
115     */
116    protected final LifecycleStrategy lifecycleStrategy;
117
118    /**
119     * Properties set at the container level, that will affect subsequent components added.
120     */
121    private final Properties containerProperties = new Properties();
122    
123    /**
124     * Component monitor instance.  Receives event callbacks.
125     */
126    protected ComponentMonitor componentMonitor;
127
128    /**
129     * Map used for looking up component adapters by their key.
130     */
131        private final Map<Object, ComponentAdapter<?>> componentKeyToAdapterCache = new HashMap<Object, ComponentAdapter<?> >();
132
133
134        private final Set<ComponentAdapter<?>> componentAdapters = new LinkedHashSet<ComponentAdapter<?>>();
135
136
137        protected final List<ComponentAdapter<?>> orderedComponentAdapters = new ArrayList<ComponentAdapter<?>>();
138
139
140    private transient IntoThreadLocal intoThreadLocal;
141    private Converters converters;
142
143
144    /**
145     * Creates a new container with a custom ComponentFactory and a parent container.
146     * <p/>
147     * <em>
148     * Important note about caching: If you intend the components to be cached, you should pass
149     * in a factory that creates {@link Cached} instances, such as for example
150     * {@link Caching}. Caching can delegate to
151     * other ComponentAdapterFactories.
152     * </em>
153     *
154     * @param componentFactory the factory to use for creation of ComponentAdapters.
155     * @param parent                  the parent container (used for component dependency lookups).
156     */
157    public DefaultPicoContainer(final ComponentFactory componentFactory, final PicoContainer parent) {
158        this(componentFactory, new StartableLifecycleStrategy(new NullComponentMonitor()), parent, new NullComponentMonitor());
159    }
160
161    /**
162     * Creates a new container with a custom ComponentFactory, LifecycleStrategy for instance registration,
163     * and a parent container.
164     * <p/>
165     * <em>
166     * Important note about caching: If you intend the components to be cached, you should pass
167     * in a factory that creates {@link Cached} instances, such as for example
168     * {@link Caching}. Caching can delegate to
169     * other ComponentAdapterFactories.
170     * </em>
171     *
172     * @param componentFactory the factory to use for creation of ComponentAdapters.
173     * @param lifecycleStrategy
174     *                                the lifecycle strategy chosen for registered
175     *                                instance (not implementations!)
176     * @param parent                  the parent container (used for component dependency lookups).
177     */
178    public DefaultPicoContainer(final ComponentFactory componentFactory,
179                                final LifecycleStrategy lifecycleStrategy,
180                                final PicoContainer parent) {
181        this(componentFactory, lifecycleStrategy, parent, new NullComponentMonitor() );
182    }
183
184    public DefaultPicoContainer(final ComponentFactory componentFactory,
185                                final LifecycleStrategy lifecycleStrategy,
186                                final PicoContainer parent, final ComponentMonitor componentMonitor) {
187        if (componentFactory == null) {
188                        throw new NullPointerException("componentFactory");
189                }
190        if (lifecycleStrategy == null) {
191                        throw new NullPointerException("lifecycleStrategy");
192                }
193        this.componentFactory = componentFactory;
194        this.lifecycleStrategy = lifecycleStrategy;
195        this.parent = parent;
196        if (parent != null && !(parent instanceof EmptyPicoContainer)) {
197            this.parent = new ImmutablePicoContainer(parent);
198        }
199        this.componentMonitor = componentMonitor;
200    }
201
202    /**
203     * Creates a new container with the AdaptingInjection using a
204     * custom ComponentMonitor
205     *
206     * @param monitor the ComponentMonitor to use
207     * @param parent  the parent container (used for component dependency lookups).
208     */
209    public DefaultPicoContainer(final ComponentMonitor monitor, final PicoContainer parent) {
210        this(new AdaptingBehavior(), new StartableLifecycleStrategy(monitor), parent, monitor);
211    }
212
213    /**
214     * Creates a new container with the AdaptingInjection using a
215     * custom ComponentMonitor and lifecycle strategy
216     *
217     * @param monitor           the ComponentMonitor to use
218     * @param lifecycleStrategy the lifecycle strategy to use.
219     * @param parent            the parent container (used for component dependency lookups).
220     */
221    public DefaultPicoContainer(final ComponentMonitor monitor, final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
222        this(new AdaptingBehavior(), lifecycleStrategy, parent, monitor);
223    }
224
225    /**
226     * Creates a new container with the AdaptingInjection using a
227     * custom lifecycle strategy
228     *
229     * @param lifecycleStrategy the lifecycle strategy to use.
230     * @param parent            the parent container (used for component dependency lookups).
231     */
232    public DefaultPicoContainer(final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
233        this(new NullComponentMonitor(), lifecycleStrategy, parent);
234    }
235
236
237    /**
238     * Creates a new container with a custom ComponentFactory and no parent container.
239     *
240     * @param componentFactory the ComponentFactory to use.
241     */
242    public DefaultPicoContainer(final ComponentFactory componentFactory) {
243        this(componentFactory, null);
244    }
245
246    /**
247     * Creates a new container with the AdaptingInjection using a
248     * custom ComponentMonitor
249     *
250     * @param monitor the ComponentMonitor to use
251     */
252    public DefaultPicoContainer(final ComponentMonitor monitor) {
253        this(monitor, new StartableLifecycleStrategy(monitor), null);
254    }
255
256    /**
257     * Creates a new container with a (caching) {@link AdaptingInjection}
258     * and a parent container.
259     *
260     * @param parent the parent container (used for component dependency lookups).
261     */
262    public DefaultPicoContainer(final PicoContainer parent) {
263        this(new AdaptingBehavior(), parent);
264    }
265
266    /** Creates a new container with a {@link AdaptingBehavior} and no parent container. */
267    public DefaultPicoContainer() {
268        this(new AdaptingBehavior(), null);
269    }
270
271    /** {@inheritDoc} **/
272    public Collection<ComponentAdapter<?>> getComponentAdapters() {
273        return Collections.unmodifiableSet(getModifiableComponentAdapterList());
274    }
275
276
277    /** {@inheritDoc} **/
278    public final ComponentAdapter<?> getComponentAdapter(final Object componentKey) {
279        ComponentAdapter<?> adapter = getComponentKeyToAdapterCache().get(componentKey);
280        if (adapter == null && parent != null) {
281            adapter = getParent().getComponentAdapter(componentKey);
282            if (adapter != null) {
283                adapter = new KnowsContainerAdapter(adapter, getParent());
284            }
285        }
286        if (adapter == null) {
287            Object inst = componentMonitor.noComponentFound(this, componentKey);
288            if (inst != null) {
289                adapter = new LateInstance(componentKey, inst);
290            }
291        }
292        return adapter;
293    }
294
295    public static class LateInstance extends AbstractAdapter {
296        private final Object instance;
297        private LateInstance(Object componentKey, Object instance) {
298            super(componentKey, instance.getClass());
299            this.instance = instance;
300        }
301
302        public Object getComponentInstance() {
303            return instance;
304        }
305
306        public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
307            return instance;
308        }
309
310        public void verify(PicoContainer container) throws PicoCompositionException {
311        }
312
313        public String getDescriptor() {
314            return "LateInstance";
315        }
316    }
317
318    public static class KnowsContainerAdapter<T> implements ComponentAdapter<T> {
319        private final ComponentAdapter<T> ca;
320        private final PicoContainer ctr;
321
322        public KnowsContainerAdapter(ComponentAdapter<T> ca, PicoContainer ctr) {
323            this.ca = ca;
324            this.ctr = ctr;
325        }
326
327        public T getComponentInstance(Type into) throws PicoCompositionException {
328            return getComponentInstance(ctr, into);
329        }
330
331        public Object getComponentKey() {
332            return ca.getComponentKey();
333        }
334
335        public Class<? extends T> getComponentImplementation() {
336            return ca.getComponentImplementation();
337        }
338
339        public T getComponentInstance(PicoContainer container) throws PicoCompositionException {
340            return ca.getComponentInstance(container);
341        }
342
343        public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
344            return ca.getComponentInstance(container, into);
345        }
346
347        public void verify(PicoContainer container) throws PicoCompositionException {
348            ca.verify(container);
349        }
350
351        public void accept(PicoVisitor visitor) {
352            ca.accept(visitor);
353        }
354
355        public ComponentAdapter getDelegate() {
356            return ca.getDelegate();
357        }
358
359        public <U extends ComponentAdapter> U findAdapterOfType(Class<U> adapterType) {
360            return ca.findAdapterOfType(adapterType);
361        }
362
363        public String getDescriptor() {
364            return null;
365        }
366    }
367
368    /** {@inheritDoc} **/
369    public <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final NameBinding componentNameBinding) {
370        return getComponentAdapter(componentType, componentNameBinding, null);
371    }
372
373    /** {@inheritDoc} **/
374    private <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final NameBinding componentNameBinding, final Class<? extends Annotation> binding) {
375        // See http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-115
376        ComponentAdapter<?> adapterByKey = getComponentAdapter(componentType);
377        if (adapterByKey != null) {
378            return typeComponentAdapter(adapterByKey);
379        }
380
381        List<ComponentAdapter<T>> found = binding == null ? getComponentAdapters(componentType) : getComponentAdapters(componentType, binding);
382
383        if (found.size() == 1) {
384            return found.get(0);
385        } else if (found.isEmpty()) {
386            if (parent != null) {
387                return getParent().getComponentAdapter(componentType, componentNameBinding);
388            } else {
389                return null;
390            }
391        } else {
392            if (componentNameBinding != null) {
393                String parameterName = componentNameBinding.getName();
394                if (parameterName != null) {
395                    ComponentAdapter<?> ca = getComponentAdapter(parameterName);
396                    if (ca != null && componentType.isAssignableFrom(ca.getComponentImplementation())) {
397                        return typeComponentAdapter(ca);
398                    }
399                }
400            }
401            String[] foundStrings = makeFoundAmbiguousStrings(found);
402            throw new AbstractInjector.AmbiguousComponentResolutionException(componentType, foundStrings);
403        }
404    }
405
406    /** {@inheritDoc} **/
407    public <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final Class<? extends Annotation> binding) {
408        // 1
409        return getComponentAdapter(componentType, null, binding);
410    }
411
412    /** {@inheritDoc} **/
413    public <T> List<ComponentAdapter<T>> getComponentAdapters(final Class<T> componentType) {
414        return getComponentAdapters(componentType,  null);
415    }
416
417    /** {@inheritDoc} **/
418    public <T> List<ComponentAdapter<T>> getComponentAdapters(final Class<T> componentType, final Class<? extends Annotation> binding) {
419        if (componentType == null) {
420            return Collections.emptyList();
421        }
422        List<ComponentAdapter<T>> found = new ArrayList<ComponentAdapter<T>>();
423        for (ComponentAdapter<?> componentAdapter : getComponentAdapters()) {
424            Object k = componentAdapter.getComponentKey();
425
426            if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation()) &&
427                (!(k instanceof BindKey) || (k instanceof BindKey && (((BindKey<?>)k).getAnnotation() == null || binding == null ||
428                                                                      ((BindKey<?>)k).getAnnotation() == binding)))) {
429                found.add((ComponentAdapter<T>)typeComponentAdapter(componentAdapter));
430            }
431        }
432        return found;
433    }
434
435    protected MutablePicoContainer addAdapterInternal(ComponentAdapter<?> componentAdapter) {
436        Object componentKey = componentAdapter.getComponentKey();
437        if (getComponentKeyToAdapterCache().containsKey(componentKey)) {
438            throw new PicoCompositionException("Duplicate Keys not allowed. Duplicate for '" + componentKey + "'");
439        }
440        getModifiableComponentAdapterList().add(componentAdapter);
441        getComponentKeyToAdapterCache().put(componentKey, componentAdapter);
442        return this;
443    }
444
445    /**
446     * {@inheritDoc}
447     * This method can be used to override the ComponentAdapter created by the {@link ComponentFactory}
448     * passed to the constructor of this container.
449     */
450    public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) {
451        return addAdapter(componentAdapter,  this.containerProperties);
452    }
453
454    /** {@inheritDoc} **/
455    public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter, final Properties properties) {
456        Properties tmpProperties = (Properties)properties.clone();
457        AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.USE_NAMES);
458        if (AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.NONE) == false && componentFactory instanceof BehaviorFactory) {
459            MutablePicoContainer container = addAdapterInternal(((BehaviorFactory)componentFactory).addComponentAdapter(
460                componentMonitor,
461                lifecycleStrategy,
462                tmpProperties,
463                componentAdapter));
464            throwIfPropertiesLeft(tmpProperties);
465            return container;
466        } else {
467            return addAdapterInternal(componentAdapter);
468        }
469
470    }
471
472
473    /** {@inheritDoc} **/
474    public <T> ComponentAdapter<T> removeComponent(final Object componentKey) {
475        lifecycleState.removingComponent();
476
477        ComponentAdapter<T> adapter = (ComponentAdapter<T>) getComponentKeyToAdapterCache().remove(componentKey);
478        getModifiableComponentAdapterList().remove(adapter);
479        getOrderedComponentAdapters().remove(adapter);          
480        return adapter;
481    }
482
483    /**
484     * {@inheritDoc}
485     * The returned ComponentAdapter will be an {@link org.picocontainer.adapters.InstanceAdapter}.
486     */
487    public MutablePicoContainer addComponent(final Object implOrInstance) {
488        return addComponent(implOrInstance, this.containerProperties);
489    }
490
491    private MutablePicoContainer addComponent(final Object implOrInstance, final Properties props) {
492        Class<?> clazz;
493        if (implOrInstance instanceof String) {
494            return addComponent(implOrInstance, implOrInstance);
495        }
496        if (implOrInstance instanceof Class) {
497            clazz = (Class<?>)implOrInstance;
498        } else {
499            clazz = implOrInstance.getClass();
500        }
501        return addComponent(clazz, implOrInstance, props);
502    }
503
504
505    public MutablePicoContainer addConfig(final String name, final Object val) {
506        return addAdapterInternal(new InstanceAdapter<Object>(name, val, lifecycleStrategy, componentMonitor));
507    }
508
509
510    /**
511     * {@inheritDoc}
512     * The returned ComponentAdapter will be instantiated by the {@link ComponentFactory}
513     * passed to the container's constructor.
514     */
515    public MutablePicoContainer addComponent(final Object componentKey,
516                                             final Object componentImplementationOrInstance,
517                                             final Parameter... parameters) {
518        return this.addComponent(componentKey, componentImplementationOrInstance, this.containerProperties, parameters);
519    }
520
521    private MutablePicoContainer addComponent(final Object componentKey,
522                                             final Object componentImplementationOrInstance,
523                                             final Properties properties,
524                                             Parameter... parameters) {
525        if (parameters != null && parameters.length == 0) {
526            parameters = null; // backwards compatibility!  solve this better later - Paul
527        }
528        
529        //New replacement for Parameter.ZERO.
530        if (parameters != null && parameters.length == 1 && DefaultConstructorParameter.INSTANCE.equals(parameters[0])) {
531                parameters = new Parameter[0];
532        }
533        
534        if (componentImplementationOrInstance instanceof Class) {
535            Properties tmpProperties = (Properties) properties.clone();
536            ComponentAdapter<?> adapter = componentFactory.createComponentAdapter(componentMonitor,
537                                                                                               lifecycleStrategy,
538                                                                                               tmpProperties,
539                                                                                               componentKey,
540                                                                                               (Class<?>)componentImplementationOrInstance,
541                                                                                               parameters);
542            AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.USE_NAMES);
543            throwIfPropertiesLeft(tmpProperties);
544            if (lifecycleState.isStarted()) {
545                addAdapterIfStartable(adapter);
546                potentiallyStartAdapter(adapter);
547            }
548            return addAdapterInternal(adapter);
549        } else {
550            ComponentAdapter<?> adapter =
551                new InstanceAdapter<Object>(componentKey, componentImplementationOrInstance, lifecycleStrategy, componentMonitor);
552            if (lifecycleState.isStarted()) {
553                addAdapterIfStartable(adapter);
554                potentiallyStartAdapter(adapter);
555            }
556            return addAdapter(adapter, properties);
557        }
558    }
559
560    private void throwIfPropertiesLeft(final Properties tmpProperties) {
561        if(tmpProperties.size() > 0) {
562            throw new PicoCompositionException("Unprocessed Characteristics:" + tmpProperties +", please refer to http://picocontainer.org/unprocessed-properties-help.html");
563        }
564    }
565
566    private synchronized void addOrderedComponentAdapter(final ComponentAdapter<?> componentAdapter) {
567        if (!getOrderedComponentAdapters().contains(componentAdapter)) {
568            getOrderedComponentAdapters().add(componentAdapter);
569        }
570    }
571
572    public List<Object> getComponents() throws PicoException {
573        return getComponents(Object.class);
574    }
575
576    public <T> List<T> getComponents(final Class<T> componentType) {
577        if (componentType == null) {
578            return Collections.emptyList();
579        }
580
581        Map<ComponentAdapter<T>, T> adapterToInstanceMap = new HashMap<ComponentAdapter<T>, T>();
582        List<T> result = new ArrayList<T>();
583        synchronized(this) {
584            for (ComponentAdapter<?> componentAdapter : getModifiableComponentAdapterList()) {
585                if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation())) {
586                    ComponentAdapter<T> typedComponentAdapter = typeComponentAdapter(componentAdapter);
587                    T componentInstance = getLocalInstance(typedComponentAdapter);
588    
589                    adapterToInstanceMap.put(typedComponentAdapter, componentInstance);
590                }
591            }
592            
593            for (ComponentAdapter<?> componentAdapter : getOrderedComponentAdapters()) {
594                final T componentInstance = adapterToInstanceMap.get(componentAdapter);
595                if (componentInstance != null) {
596                    // may be null in the case of the "implicit" addAdapter
597                    // representing "this".
598                    result.add(componentInstance);
599                }
600            }
601        }
602        return result;
603    }
604
605    private <T> T getLocalInstance(final ComponentAdapter<T> typedComponentAdapter) {
606        T componentInstance = typedComponentAdapter.getComponentInstance(this, ComponentAdapter.NOTHING.class);
607
608        // This is to ensure all are added. (Indirect dependencies will be added
609        // from InstantiatingComponentAdapter).
610        addOrderedComponentAdapter(typedComponentAdapter);
611
612        return componentInstance;
613    }
614
615    @SuppressWarnings({ "unchecked" })
616    private static <T> ComponentAdapter<T> typeComponentAdapter(final ComponentAdapter<?> componentAdapter) {
617        return (ComponentAdapter<T>)componentAdapter;
618    }
619
620    public Object getComponent(final Object componentKeyOrType) {
621        return getComponent(componentKeyOrType, null);
622    }
623
624    public Object getComponent(final Object componentKeyOrType, Type into) {
625        synchronized (this) {
626            if (intoThreadLocal == null) {
627                intoThreadLocal = new IntoThreadLocal();
628            }
629        }
630        intoThreadLocal.set(into);
631        try {
632            return getComponent(componentKeyOrType, (Class<? extends Annotation>) null);
633        } finally {
634            intoThreadLocal.set(null);
635        }
636    }
637
638    public Object getComponent(final Object componentKeyOrType, final Class<? extends Annotation> annotation) {
639        ComponentAdapter<?> componentAdapter = null;
640        Object component;
641        try {
642            if (annotation != null) {
643                componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, annotation);
644                component = componentAdapter == null ? null : getInstance(componentAdapter, null);
645            } else if (componentKeyOrType instanceof Class) {
646                componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, (NameBinding) null);
647                component = componentAdapter == null ? null : getInstance(componentAdapter, (Class<?>)componentKeyOrType);
648            } else {
649                componentAdapter = getComponentAdapter(componentKeyOrType);
650                component = componentAdapter == null ? null : getInstance(componentAdapter, null);
651            }
652        } catch (AbstractInjector.AmbiguousComponentResolutionException e) {
653            if (componentAdapter != null) {
654                e.setComponent(findInjectorOrInstanceAdapter(componentAdapter).toString());
655            }
656            throw e;
657        }
658        return decorateComponent(component, componentAdapter);
659    }
660
661    /**
662     * This is invoked when getComponent(..) is called.  It allows extendees to decorate a
663     * component before it is returned to the caller.
664     * @param component the component that will be returned for getComponent(..)
665     * @param componentAdapter the component adapter that made that component
666     * @return the component (the same as that passed in by default)
667     */
668    protected Object decorateComponent(Object component, ComponentAdapter<?> componentAdapter) {
669        if (componentAdapter instanceof ComponentLifecycle<?>
670                && lifecycleStrategy.isLazy(componentAdapter) // is Lazy
671                && !((ComponentLifecycle<?>) componentAdapter).isStarted()) {
672            ((ComponentLifecycle<?>)componentAdapter).start(this);
673        }
674        return component;
675    }
676
677    public <T> T getComponent(final Class<T> componentType) {
678        Object o = getComponent((Object)componentType, null);
679        return componentType.cast(o);
680    }
681
682    public <T> T getComponent(final Class<T> componentType, final Class<? extends Annotation> binding) {
683        Object o = getComponent((Object)componentType, binding);
684        return componentType.cast(o);
685    }
686
687
688    private Object getInstance(final ComponentAdapter<?> componentAdapter, Class componentKey) {
689        // check whether this is our adapter
690        // we need to check this to ensure up-down dependencies cannot be followed
691        final boolean isLocal = getModifiableComponentAdapterList().contains(componentAdapter);
692
693        if (isLocal || componentAdapter instanceof LateInstance) {
694            Object instance;
695            try {
696                if (componentAdapter instanceof FactoryInjector) {
697                    instance = ((FactoryInjector) componentAdapter).getComponentInstance(this, getInto());
698                } else {
699                    instance = componentAdapter.getComponentInstance(this, getInto());
700                }
701            } catch (AbstractInjector.CyclicDependencyException e) {
702                if (parent != null) {
703                    instance = getParent().getComponent(componentAdapter.getComponentKey());
704                    if (instance != null) {
705                        return instance;
706                    }
707                }
708                throw e;
709            }
710            addOrderedComponentAdapter(componentAdapter);
711
712            return instance;
713        } else if (parent != null) {
714            Object key = componentKey;
715            if (key == null) {
716                key = componentAdapter.getComponentKey();
717            }
718            return getParent().getComponent(key);
719        }
720
721        return null;
722    }
723
724    private Type getInto() {
725        if (intoThreadLocal == null) {
726            return null;
727        }
728        return intoThreadLocal.get();
729    }
730
731
732    /** {@inheritDoc} **/
733    public PicoContainer getParent() {
734        return parent;
735    }
736
737    /** {@inheritDoc} **/
738    public <T> ComponentAdapter<T> removeComponentByInstance(final T componentInstance) {
739        for (ComponentAdapter<?> componentAdapter : getModifiableComponentAdapterList()) {
740            if (getLocalInstance(componentAdapter).equals(componentInstance)) {
741                return removeComponent(componentAdapter.getComponentKey());
742            }
743        }
744        return null;
745    }
746
747    /**
748     * Start the components of this PicoContainer and all its logical child containers.
749     * The starting of the child container is only attempted if the parent
750     * container start successfully.  The child container for which start is attempted
751     * is tracked so that upon stop, only those need to be stopped.
752     * The lifecycle operation is delegated to the component adapter,
753     * if it is an instance of {@link Behavior lifecycle manager}.
754     * The actual {@link LifecycleStrategy lifecycle strategy} supported
755     * depends on the concrete implementation of the adapter.
756     *
757     * @see Behavior
758     * @see LifecycleStrategy
759     * @see #makeChildContainer()
760     * @see #addChildContainer(PicoContainer)
761     * @see #removeChildContainer(PicoContainer)
762     */
763    public synchronized void start() {
764
765        lifecycleState.starting();
766
767        startAdapters();
768        childrenStarted.clear();
769        for (PicoContainer child : children) {
770            childrenStarted.add(new WeakReference<PicoContainer>(child));
771            if (child instanceof Startable) {
772                ((Startable)child).start();
773            }
774        }
775    }
776
777    /**
778     * Stop the components of this PicoContainer and all its logical child containers.
779     * The stopping of the child containers is only attempted for those that have been
780     * started, possibly not successfully.
781     * The lifecycle operation is delegated to the component adapter,
782     * if it is an instance of {@link Behavior lifecycle manager}.
783     * The actual {@link LifecycleStrategy lifecycle strategy} supported
784     * depends on the concrete implementation of the adapter.
785     *
786     * @see Behavior
787     * @see LifecycleStrategy
788     * @see #makeChildContainer()
789     * @see #addChildContainer(PicoContainer)
790     * @see #removeChildContainer(PicoContainer)
791     */
792    public synchronized void stop() {
793
794        lifecycleState.stopping();
795
796        for (PicoContainer child : children) {
797            if (childStarted(child)) {
798                if (child instanceof Startable) {
799                    ((Startable)child).stop();
800                }
801            }
802        }
803        stopAdapters();
804        lifecycleState.stopped();
805    }
806
807    /**
808     * Checks the status of the child container to see if it's been started
809     * to prevent IllegalStateException upon stop
810     *
811     * @param child the child PicoContainer
812     *
813     * @return A boolean, <code>true</code> if the container is started
814     */
815    private boolean childStarted(final PicoContainer child) {
816        for (WeakReference<PicoContainer> eachChild : childrenStarted) {
817                PicoContainer ref = eachChild.get();
818                if (ref == null) {
819                        continue;
820                }
821                
822                if (child.equals(ref)) {
823                        return true;
824                }
825        }
826        return false;
827    }
828
829    /**
830     * Dispose the components of this PicoContainer and all its logical child containers.
831     * The lifecycle operation is delegated to the component adapter,
832     * if it is an instance of {@link Behavior lifecycle manager}.
833     * The actual {@link LifecycleStrategy lifecycle strategy} supported
834     * depends on the concrete implementation of the adapter.
835     *
836     * @see Behavior
837     * @see LifecycleStrategy
838     * @see #makeChildContainer()
839     * @see #addChildContainer(PicoContainer)
840     * @see #removeChildContainer(PicoContainer)
841     */
842    public synchronized void dispose() {
843        if (lifecycleState.isStarted()) {
844                stop();
845        }
846
847        lifecycleState.disposing();
848
849        for (PicoContainer child : children) {
850            if (child instanceof MutablePicoContainer) {
851                ((Disposable)child).dispose();
852            }
853        }
854        disposeAdapters();
855
856        lifecycleState.disposed();
857    }
858
859    public synchronized void setLifecycleState(LifecycleState lifecycleState) {
860        this.lifecycleState = lifecycleState;
861    }
862
863    public MutablePicoContainer makeChildContainer() {
864        DefaultPicoContainer pc = new DefaultPicoContainer(componentFactory, lifecycleStrategy, this, componentMonitor);
865        addChildContainer(pc);
866        return pc;
867    }
868    
869    /**
870     * Checks for identical references in the child container.  It doesn't
871     * traverse an entire hierarchy, namely it simply checks for child containers
872     * that are equal to the current container.
873     * @param child
874     */
875    private void checkCircularChildDependencies(PicoContainer child) {
876        final String MESSAGE = "Cannot have circular dependency between parent %s and child: %s";
877        if (child == this) {
878                throw new IllegalArgumentException(String.format(MESSAGE,this,child));
879        }
880        
881        //Todo: Circular Import Dependency on AbstractDelegatingPicoContainer
882        if (child instanceof AbstractDelegatingPicoContainer) {
883                AbstractDelegatingPicoContainer delegateChild = (AbstractDelegatingPicoContainer) child;
884                while(delegateChild != null) {
885                        PicoContainer delegateInstance = delegateChild.getDelegate();
886                        if (this == delegateInstance) {
887                                        throw new IllegalArgumentException(String.format(MESSAGE,this,child));
888                        }
889                        if (delegateInstance instanceof AbstractDelegatingPicoContainer) {
890                                delegateChild = (AbstractDelegatingPicoContainer) delegateInstance;
891                        } else {
892                                delegateChild = null;
893                        }
894                }
895        }
896        
897    }
898
899    public MutablePicoContainer addChildContainer(final PicoContainer child) {
900        checkCircularChildDependencies(child);
901        if (children.add(child)) {
902            // TODO Should only be added if child container has also be started
903            if (lifecycleState.isStarted()) {
904                childrenStarted.add(new WeakReference<PicoContainer>(child));
905            }
906        }
907        return this;
908    }
909
910    public boolean removeChildContainer(final PicoContainer child) {
911        final boolean result = children.remove(child);
912        WeakReference<PicoContainer> foundRef = null;
913        for (WeakReference<PicoContainer> eachChild : childrenStarted) {
914                PicoContainer ref = eachChild.get();
915                if (ref.equals(child)) {
916                        foundRef = eachChild;
917                        break;
918                }
919        }
920        
921        if (foundRef != null) {
922                childrenStarted.remove(foundRef);
923        }
924        
925        return result;
926    }
927
928    public MutablePicoContainer change(final Properties... properties) {
929        for (Properties c : properties) {
930            Enumeration<String> e = (Enumeration<String>) c.propertyNames();
931            while (e.hasMoreElements()) {
932                String s = e.nextElement();
933                containerProperties.setProperty(s,c.getProperty(s));
934            }
935        }
936        return this;
937    }
938
939    public MutablePicoContainer as(final Properties... properties) {
940        return new AsPropertiesPicoContainer(properties);
941    }
942
943    public void accept(final PicoVisitor visitor) {
944        
945        //TODO Pico 3 : change accept signatures to allow abort at any point in the traversal.
946        boolean shouldContinue = visitor.visitContainer(this);
947        if (!shouldContinue) {
948                return;
949        }
950        
951        
952        componentFactory.accept(visitor); // will cascade through behaviors
953        final List<ComponentAdapter<?>> componentAdapters = new ArrayList<ComponentAdapter<?>>(getComponentAdapters());
954        for (ComponentAdapter<?> componentAdapter : componentAdapters) {
955            componentAdapter.accept(visitor);
956        }
957        final List<PicoContainer> allChildren = new ArrayList<PicoContainer>(children);
958        for (PicoContainer child : allChildren) {
959            child.accept(visitor);
960        }
961    }
962
963    /**
964     * Changes monitor in the ComponentFactory, the component adapters
965     * and the child containers, if these support a ComponentMonitorStrategy.
966     * {@inheritDoc}
967     */
968    public void changeMonitor(final ComponentMonitor monitor) {
969        this.componentMonitor = monitor;
970        if (lifecycleStrategy instanceof ComponentMonitorStrategy) {
971            ((ComponentMonitorStrategy)lifecycleStrategy).changeMonitor(monitor);
972        }
973        for (ComponentAdapter<?> adapter : getModifiableComponentAdapterList()) {
974            if (adapter instanceof ComponentMonitorStrategy) {
975                ((ComponentMonitorStrategy)adapter).changeMonitor(monitor);
976            }
977        }
978        for (PicoContainer child : children) {
979            if (child instanceof ComponentMonitorStrategy) {
980                ((ComponentMonitorStrategy)child).changeMonitor(monitor);
981            }
982        }
983    }
984
985    /**
986     * Returns the first current monitor found in the ComponentFactory, the component adapters
987     * and the child containers, if these support a ComponentMonitorStrategy.
988     * {@inheritDoc}
989     *
990     * @throws PicoCompositionException if no component monitor is found in container or its children
991     */
992    public ComponentMonitor currentMonitor() {
993        return componentMonitor;
994    }
995
996    /**
997     * Loops over all component adapters and invokes
998     * start(PicoContainer) method on the ones which are LifecycleManagers
999     */
1000    private void startAdapters() {
1001        Collection<ComponentAdapter<?>> adapters = getComponentAdapters();
1002        for (ComponentAdapter<?> adapter : adapters) {
1003            addAdapterIfStartable(adapter);
1004        }
1005        adapters = getOrderedComponentAdapters();
1006        // clone the adapters
1007        List<ComponentAdapter<?>> adaptersClone = new ArrayList<ComponentAdapter<?>>(adapters);
1008        for (final ComponentAdapter<?> adapter : adaptersClone) {
1009            potentiallyStartAdapter(adapter);
1010        }
1011    }
1012
1013    protected void potentiallyStartAdapter(ComponentAdapter<?> adapter) {
1014        if (adapter instanceof ComponentLifecycle) {
1015            if (!lifecycleStrategy.isLazy(adapter)) {
1016                ((ComponentLifecycle<?>)adapter).start(this);
1017            }
1018        }
1019    }
1020
1021    private void addAdapterIfStartable(ComponentAdapter<?> adapter) {
1022        if (adapter instanceof ComponentLifecycle) {
1023            ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter;
1024            if (componentLifecycle.componentHasLifecycle()) {
1025                // create an instance, it will be added to the ordered CA list
1026                instantiateComponentAsIsStartable(adapter);
1027                addOrderedComponentAdapter(adapter);
1028            }
1029        }
1030    }
1031
1032    protected void instantiateComponentAsIsStartable(ComponentAdapter<?> adapter) {
1033        if (!lifecycleStrategy.isLazy(adapter)) {
1034            adapter.getComponentInstance(DefaultPicoContainer.this, ComponentAdapter.NOTHING.class);
1035        }
1036    }
1037
1038    /**
1039     * Loops over started component adapters (in inverse order) and invokes
1040     * stop(PicoContainer) method on the ones which are LifecycleManagers
1041     */
1042    private void stopAdapters() {
1043        for (int i = getOrderedComponentAdapters().size() - 1; 0 <= i; i--) {
1044            ComponentAdapter<?> adapter = getOrderedComponentAdapters().get(i);
1045            if (adapter instanceof ComponentLifecycle) {
1046                ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter;
1047                if (componentLifecycle.componentHasLifecycle() && componentLifecycle.isStarted()) {
1048                    componentLifecycle.stop(DefaultPicoContainer.this);
1049                }
1050            }
1051        }
1052    }
1053
1054    /**
1055     * Loops over all component adapters (in inverse order) and invokes
1056     * dispose(PicoContainer) method on the ones which are LifecycleManagers
1057     */
1058    private void disposeAdapters() {
1059        for (int i = getOrderedComponentAdapters().size() - 1; 0 <= i; i--) {
1060            ComponentAdapter<?> adapter = getOrderedComponentAdapters().get(i);
1061            if (adapter instanceof ComponentLifecycle) {
1062                ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter;
1063                componentLifecycle.dispose(DefaultPicoContainer.this);
1064            }
1065        }
1066    }
1067
1068
1069
1070        /**
1071         * @return the orderedComponentAdapters
1072         */
1073        protected List<ComponentAdapter<?>> getOrderedComponentAdapters() {
1074                return orderedComponentAdapters;
1075        }
1076
1077        /**
1078         * @return the componentKeyToAdapterCache
1079         */
1080        protected Map<Object, ComponentAdapter<?>> getComponentKeyToAdapterCache() {
1081                return componentKeyToAdapterCache;
1082        }
1083
1084        /**
1085         * @return the componentAdapters
1086         */
1087        protected Set<ComponentAdapter<?>> getModifiableComponentAdapterList() {
1088                return componentAdapters;
1089        }
1090
1091    public synchronized void setName(String name) {
1092        this.name = name;
1093    }
1094
1095    @Override
1096    public String toString() {
1097        return String.format("%s:%d<%s", (name != null ? name : super.toString()), this.componentAdapters.size(), (parent != null && !(parent instanceof EmptyPicoContainer)? parent.toString() : "|"));
1098    }
1099
1100    /**
1101     * If this container has a set of converters, then return it.
1102     * If it does not, and the parent (or their parent ..) does, use that
1103     * If they do not, return a NullObject implementation (ConversNothing)
1104     * @return the converters
1105     */    
1106    public synchronized Converters getConverters() {
1107        if (converters == null) {
1108            if (parent == null || (parent instanceof Converting && ((Converting) parent).getConverters() instanceof ConvertsNothing)) {
1109                converters = new BuiltInConverters();
1110            } else {
1111                return ((Converting) parent).getConverters();
1112            }
1113        }
1114        return converters;
1115    }
1116
1117    @SuppressWarnings("synthetic-access")
1118    private class AsPropertiesPicoContainer extends AbstractDelegatingMutablePicoContainer {
1119
1120                private final Properties properties;
1121
1122        public AsPropertiesPicoContainer(final Properties... props) {
1123            super(DefaultPicoContainer.this);
1124            properties = (Properties) containerProperties.clone();
1125            for (Properties c : props) {
1126                Enumeration<?> e = c.propertyNames();
1127                while (e.hasMoreElements()) {
1128                    String s = (String)e.nextElement();
1129                    properties.setProperty(s,c.getProperty(s));
1130                }
1131            }
1132        }
1133
1134        @Override
1135        @SuppressWarnings("unused")
1136        public MutablePicoContainer as( Properties... props) {
1137            throw new PicoCompositionException("Syntax 'as(FOO).as(BAR)' not allowed, do 'as(FOO, BAR)' instead");
1138        }
1139
1140        @Override
1141                public MutablePicoContainer makeChildContainer() {
1142            return getDelegate().makeChildContainer();
1143        }
1144
1145        @Override
1146                public MutablePicoContainer addComponent(final Object componentKey,
1147                                                 final Object componentImplementationOrInstance,
1148                                                 final Parameter... parameters) throws PicoCompositionException {
1149            return DefaultPicoContainer.this.addComponent(componentKey,
1150                                      componentImplementationOrInstance,
1151                                      properties,
1152                                      parameters);
1153        }
1154
1155        @Override
1156                public MutablePicoContainer addComponent(final Object implOrInstance) throws PicoCompositionException {
1157            return DefaultPicoContainer.this.addComponent(implOrInstance, properties);
1158        }
1159
1160        @Override
1161                public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) throws PicoCompositionException {
1162            return DefaultPicoContainer.this.addAdapter(componentAdapter, properties);
1163        }
1164
1165        /**
1166         * {@inheritDoc}
1167         * @see org.picocontainer.MutablePicoContainer#getLifecycleState()
1168         */
1169        @Override
1170        public LifecycleState getLifecycleState() {
1171            return DefaultPicoContainer.this.getLifecycleState();
1172        }
1173
1174        /**
1175         * {@inheritDoc}
1176         * @see org.picocontainer.MutablePicoContainer#getName()
1177         */
1178        @Override
1179        public String getName() {
1180            return DefaultPicoContainer.this.getName();
1181        }
1182    }
1183
1184    private static class IntoThreadLocal extends ThreadLocal<Type> implements Serializable {
1185        @Override
1186        protected Type initialValue() {
1187            return ComponentAdapter.NOTHING.class;
1188        }
1189    }
1190
1191    /**
1192     * {@inheritDoc}
1193     * @see org.picocontainer.MutablePicoContainer#getLifecycleState()
1194     */
1195    public synchronized LifecycleState getLifecycleState() {
1196        return lifecycleState;
1197    }
1198
1199    /**
1200     * {@inheritDoc}
1201     * @see org.picocontainer.MutablePicoContainer#getName()
1202     */
1203    public synchronized String getName() {
1204        return this.name;
1205    }
1206
1207}