001    /*****************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original code by                                                          *
009     *****************************************************************************/
010    package org.picocontainer;
011    
012    import static org.picocontainer.behaviors.Behaviors.caching;
013    import static org.picocontainer.behaviors.Behaviors.implementationHiding;
014    import org.picocontainer.behaviors.PropertyApplying;
015    import org.picocontainer.behaviors.Synchronizing;
016    import org.picocontainer.behaviors.Locking;
017    import org.picocontainer.behaviors.Automating;
018    import org.picocontainer.injectors.MethodInjection;
019    import org.picocontainer.containers.EmptyPicoContainer;
020    import org.picocontainer.containers.TransientPicoContainer;
021    import static org.picocontainer.injectors.Injectors.CDI;
022    import static org.picocontainer.injectors.Injectors.annotatedMethodDI;
023    import static org.picocontainer.injectors.Injectors.annotatedFieldDI;
024    import static org.picocontainer.injectors.Injectors.SDI;
025    import static org.picocontainer.injectors.Injectors.adaptiveDI;
026    import org.picocontainer.lifecycle.NullLifecycleStrategy;
027    import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
028    import org.picocontainer.lifecycle.StartableLifecycleStrategy;
029    import org.picocontainer.monitors.ConsoleComponentMonitor;
030    import org.picocontainer.monitors.NullComponentMonitor;
031    
032    import java.util.ArrayList;
033    import java.util.Stack;
034    import java.util.List;
035    
036    /**
037     * Helps assembles the myriad items available to a picocontainer.
038     * <p>Simple Example:</p>
039     * <pre>
040     * MutablePicoContainer mpc = new PicoBuilder()
041     * &nbsp;&nbsp;.withCaching()
042     * &nbsp;&nbsp;.withLifecycle()
043     * &nbsp;&nbsp;.build();
044     * </pre>
045     * @author Paul Hammant
046     */
047    public class PicoBuilder {
048    
049        private PicoContainer parentContainer;
050        private Class<? extends MutablePicoContainer> mpcClass = DefaultPicoContainer.class;
051        private ComponentMonitor componentMonitor;
052        private List<Object> containerComps = new ArrayList<Object>();
053        private boolean addChildToParent;
054    
055        public PicoBuilder(PicoContainer parentContainer, InjectionFactory injectionType) {
056            this.injectionType = injectionType;
057            if (parentContainer != null) {
058                this.parentContainer = parentContainer;
059            } else {
060                this.parentContainer = new EmptyPicoContainer();
061            }
062        }
063    
064        public PicoBuilder(PicoContainer parentContainer) {
065            this(parentContainer, adaptiveDI());
066        }
067    
068        public PicoBuilder(InjectionFactory injectionType) {
069            this(new EmptyPicoContainer(), injectionType);
070        }
071    
072        public PicoBuilder() {
073            this(new EmptyPicoContainer(), adaptiveDI());
074        }
075    
076        private final Stack<Object> componentFactories = new Stack<Object>();
077    
078        private InjectionFactory injectionType;
079    
080        private Class<? extends ComponentMonitor> componentMonitorClass = NullComponentMonitor.class;
081        private Class<? extends LifecycleStrategy> lifecycleStrategyClass = NullLifecycleStrategy.class;
082    
083        public PicoBuilder withLifecycle() {
084            lifecycleStrategyClass = StartableLifecycleStrategy.class;
085            return this;
086        }
087    
088        public PicoBuilder withReflectionLifecycle() {
089            lifecycleStrategyClass = ReflectionLifecycleStrategy.class;
090            return this;
091        }
092    
093        public PicoBuilder withConsoleMonitor() {
094            componentMonitorClass =  ConsoleComponentMonitor.class;
095            return this;
096        }
097    
098        public PicoBuilder withMonitor(Class<? extends ComponentMonitor> cmClass) {
099            if (cmClass == null) {
100                throw new NullPointerException("monitor class cannot be null");
101            }
102            if (!ComponentMonitor.class.isAssignableFrom(cmClass)) {
103                throw new ClassCastException(cmClass.getName() + " is not a " + ComponentMonitor.class.getName());
104    
105            }
106            componentMonitorClass = cmClass;
107            componentMonitor = null;
108            return this;
109        }
110    
111        public MutablePicoContainer build() {
112    
113            DefaultPicoContainer temp = new TransientPicoContainer();
114            temp.addComponent(PicoContainer.class, parentContainer);
115    
116            for (Object containerComp : containerComps) {
117                temp.addComponent(containerComp);
118            }
119    
120            ComponentFactory lastCaf = injectionType;
121            while (!componentFactories.empty()) {
122                Object componentFactory = componentFactories.pop();
123                DefaultPicoContainer temp2 = new TransientPicoContainer(temp);
124                temp2.addComponent("componentFactory", componentFactory);
125                if (lastCaf != null) {
126                    temp2.addComponent(ComponentFactory.class, lastCaf);
127                }
128                ComponentFactory penultimateCaf = lastCaf;
129                lastCaf = (ComponentFactory) temp2.getComponent("componentFactory");
130                if (lastCaf instanceof BehaviorFactory) {
131                    ((BehaviorFactory) lastCaf).wrap(penultimateCaf);
132                }
133            }
134    
135            temp.addComponent(ComponentFactory.class, lastCaf);
136            if (componentMonitorClass == null) {
137                temp.addComponent(ComponentMonitor.class, componentMonitor);
138            } else {
139                temp.addComponent(ComponentMonitor.class, componentMonitorClass);
140            }
141            temp.addComponent(LifecycleStrategy.class, lifecycleStrategyClass);
142            temp.addComponent("mpc", mpcClass);
143    
144    
145            MutablePicoContainer newContainer = (MutablePicoContainer) temp.getComponent("mpc");
146            if (addChildToParent) {
147                if (parentContainer instanceof MutablePicoContainer) {
148                    ((MutablePicoContainer)parentContainer).addChildContainer(newContainer);
149                } else {
150                    throw new PicoCompositionException("If using addChildContainer() the parent must be a MutablePicoContainer");
151                }
152            }
153            return newContainer;
154        }
155    
156        public PicoBuilder withHiddenImplementations() {
157            componentFactories.push(implementationHiding());
158            return this;
159        }
160    
161        public PicoBuilder withSetterInjection() {
162            injectionType = SDI();
163            return this;
164        }
165    
166        public PicoBuilder withAnnotatedMethodInjection() {
167            injectionType = annotatedMethodDI();
168            return this;
169        }
170    
171    
172        public PicoBuilder withAnnotatedFieldInjection() {
173            injectionType = annotatedFieldDI();
174            return this;
175        }
176    
177    
178        public PicoBuilder withConstructorInjection() {
179            injectionType = CDI();
180            return this;
181        }
182    
183        public PicoBuilder withCaching() {
184            componentFactories.push(caching());
185            return this;
186        }
187    
188        public PicoBuilder withComponentFactory(ComponentFactory componentFactory) {
189            if (componentFactory == null) {
190                throw new NullPointerException("CAF cannot be null");
191            }
192            componentFactories.push(componentFactory);
193            return this;
194        }
195    
196        public PicoBuilder withSynchronizing() {
197            componentFactories.push(Synchronizing.class);
198            return this;
199        }
200    
201        public PicoBuilder withLocking() {
202            componentFactories.push(Locking.class);
203            return this;
204        }
205    
206        public PicoBuilder withBehaviors(BehaviorFactory... factories) {
207            for (ComponentFactory componentFactory : factories) {
208                componentFactories.push(componentFactory);
209            }
210            return this;
211        }
212    
213        public PicoBuilder implementedBy(Class<? extends MutablePicoContainer> containerClass) {
214            mpcClass = containerClass;
215            return this;
216        }
217    
218        public PicoBuilder withMonitor(ComponentMonitor componentMonitor) {
219            this.componentMonitor = componentMonitor;
220            componentMonitorClass = null;
221            return this;
222        }
223    
224        public PicoBuilder withComponentFactory(Class<? extends ComponentFactory> componentFactoryClass) {
225            componentFactories.push(componentFactoryClass);
226            return this;
227        }
228    
229        public PicoBuilder withCustomContainerComponent(Object containerDependency) {
230            containerComps.add(containerDependency);
231            return this;
232        }
233    
234        public PicoBuilder withPropertyApplier() {
235            componentFactories.push(PropertyApplying.class);
236            return this;
237        }
238    
239        public PicoBuilder withAutomatic() {
240            componentFactories.push(Automating.class);
241            return this;
242        }
243    
244        public PicoBuilder withMethodInjection() {
245            componentFactories.push(new MethodInjection());
246            return this;
247        }
248    
249        public PicoBuilder addChildToParent() {
250            addChildToParent =  true;
251            return this;
252        }
253    }