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 org.picocontainer.behaviors.Automating;
013    import org.picocontainer.behaviors.Locking;
014    import org.picocontainer.behaviors.PropertyApplying;
015    import org.picocontainer.behaviors.Synchronizing;
016    import org.picocontainer.containers.EmptyPicoContainer;
017    import org.picocontainer.containers.TransientPicoContainer;
018    import org.picocontainer.injectors.CompositeInjection;
019    import org.picocontainer.injectors.MethodInjection;
020    import org.picocontainer.lifecycle.JavaEE5LifecycleStrategy;
021    import org.picocontainer.lifecycle.NullLifecycleStrategy;
022    import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
023    import org.picocontainer.lifecycle.StartableLifecycleStrategy;
024    import org.picocontainer.monitors.ConsoleComponentMonitor;
025    import org.picocontainer.monitors.NullComponentMonitor;
026    
027    import java.util.ArrayList;
028    import java.util.List;
029    import java.util.Stack;
030    
031    import static org.picocontainer.behaviors.Behaviors.caching;
032    import static org.picocontainer.behaviors.Behaviors.implementationHiding;
033    import static org.picocontainer.injectors.Injectors.CDI;
034    import static org.picocontainer.injectors.Injectors.SDI;
035    import static org.picocontainer.injectors.Injectors.adaptiveDI;
036    import static org.picocontainer.injectors.Injectors.annotatedFieldDI;
037    import static org.picocontainer.injectors.Injectors.annotatedMethodDI;
038    import static org.picocontainer.injectors.Injectors.namedField;
039    import static org.picocontainer.injectors.Injectors.namedMethod;
040    import static org.picocontainer.injectors.Injectors.typedFieldDI;
041    
042    /**
043     * Helps assembles the myriad items available to a picocontainer.
044     * <p>Simple Example:</p>
045     * <pre>
046     * MutablePicoContainer mpc = new PicoBuilder()
047     * &nbsp;&nbsp;.withCaching()
048     * &nbsp;&nbsp;.withLifecycle()
049     * &nbsp;&nbsp;.build();
050     * </pre>
051     * @author Paul Hammant
052     */
053    public class PicoBuilder {
054    
055        private PicoContainer parentContainer;
056        private Class<? extends MutablePicoContainer> mpcClass = DefaultPicoContainer.class;
057        private ComponentMonitor componentMonitor;
058        private List<Object> containerComps = new ArrayList<Object>();
059        private boolean addChildToParent;
060        private LifecycleStrategy lifecycleStrategy;
061        private final Stack<Object> behaviors = new Stack<Object>();
062        private final List<InjectionFactory> injectors = new ArrayList<InjectionFactory>();
063        private Class<? extends ComponentMonitor> componentMonitorClass = NullComponentMonitor.class;
064        private Class<? extends LifecycleStrategy> lifecycleStrategyClass = NullLifecycleStrategy.class;
065    
066    
067        public PicoBuilder(PicoContainer parentContainer, InjectionFactory injectionType) {
068            this(parentContainer);
069            injectors.add(injectionType);
070        }
071    
072        public PicoBuilder(PicoContainer parentContainer) {
073            if (parentContainer != null) {
074                this.parentContainer = parentContainer;
075            } else {
076                this.parentContainer = new EmptyPicoContainer();
077            }
078        }
079    
080        public PicoBuilder(InjectionFactory injectionType) {
081            this(new EmptyPicoContainer(), injectionType);
082        }
083    
084        public PicoBuilder() {
085            this(new EmptyPicoContainer());
086        }
087    
088        public PicoBuilder withLifecycle() {
089            lifecycleStrategyClass = StartableLifecycleStrategy.class;
090            lifecycleStrategy = null;
091            return this;
092        }
093    
094        public PicoBuilder withReflectionLifecycle() {
095            lifecycleStrategyClass = ReflectionLifecycleStrategy.class;
096            lifecycleStrategy = null;
097            return this;
098        }
099    
100        public PicoBuilder withLifecycle(Class<? extends LifecycleStrategy> lifecycleStrategyClass) {
101            this.lifecycleStrategyClass = lifecycleStrategyClass;
102            lifecycleStrategy = null;
103            return this;
104        }
105    
106        public PicoBuilder withJavaEE5Lifecycle() {
107            this.lifecycleStrategyClass = JavaEE5LifecycleStrategy.class;
108            lifecycleStrategy = null;
109            return this;
110        }
111    
112        public PicoBuilder withLifecycle(LifecycleStrategy lifecycleStrategy) {
113            this.lifecycleStrategy = lifecycleStrategy;
114            lifecycleStrategyClass = null;
115            return this;
116        }
117    
118    
119        public PicoBuilder withConsoleMonitor() {
120            componentMonitorClass =  ConsoleComponentMonitor.class;
121            return this;
122        }
123    
124        public PicoBuilder withMonitor(Class<? extends ComponentMonitor> cmClass) {
125            if (cmClass == null) {
126                throw new NullPointerException("monitor class cannot be null");
127            }
128            if (!ComponentMonitor.class.isAssignableFrom(cmClass)) {
129                throw new ClassCastException(cmClass.getName() + " is not a " + ComponentMonitor.class.getName());
130    
131            }
132            componentMonitorClass = cmClass;
133            componentMonitor = null;
134            return this;
135        }
136    
137        public MutablePicoContainer build() {
138    
139            DefaultPicoContainer tempContainer = new TransientPicoContainer();
140            tempContainer.addComponent(PicoContainer.class, parentContainer);
141    
142            addContainerComponents(tempContainer);
143    
144            ComponentFactory componentFactory;
145            if (injectors.size() == 1) {
146                componentFactory = injectors.get(0);
147            } else if (injectors.size() == 0) {
148                componentFactory = adaptiveDI();
149            } else {
150                componentFactory = new CompositeInjection(injectors.toArray(new InjectionFactory[injectors.size()]));
151            }
152            while (!behaviors.empty()) {
153                componentFactory = buildComponentFactory(tempContainer, componentFactory);
154            }
155    
156            tempContainer.addComponent(ComponentFactory.class, componentFactory);
157    
158            buildComponentMonitor(tempContainer);
159    
160            if (lifecycleStrategy == null) {
161                tempContainer.addComponent(LifecycleStrategy.class, lifecycleStrategyClass);
162            } else {
163                tempContainer.addComponent(LifecycleStrategy.class, lifecycleStrategy);
164    
165            }
166            tempContainer.addComponent("mpc", mpcClass);
167    
168            MutablePicoContainer newContainer = (MutablePicoContainer) tempContainer.getComponent("mpc");
169    
170            addChildToParent(newContainer);
171            return newContainer;
172        }
173    
174        private void buildComponentMonitor(DefaultPicoContainer tempContainer) {
175            if (componentMonitorClass == null) {
176                tempContainer.addComponent(ComponentMonitor.class, componentMonitor);
177            } else {
178                tempContainer.addComponent(ComponentMonitor.class, componentMonitorClass);
179            }
180        }
181    
182        private void addChildToParent(MutablePicoContainer newContainer) {
183            if (addChildToParent) {
184                if (parentContainer instanceof MutablePicoContainer) {
185                    ((MutablePicoContainer)parentContainer).addChildContainer(newContainer);
186                } else {
187                    throw new PicoCompositionException("If using addChildContainer() the parent must be a MutablePicoContainer");
188                }
189            }
190        }
191    
192        private void addContainerComponents(DefaultPicoContainer temp) {
193            for (Object containerComp : containerComps) {
194                temp.addComponent(containerComp);
195            }
196        }
197    
198        private ComponentFactory buildComponentFactory(DefaultPicoContainer container, final ComponentFactory lastCaf) {
199            Object componentFactory = behaviors.pop();
200            DefaultPicoContainer tmpContainer = new TransientPicoContainer(container);
201            tmpContainer.addComponent("componentFactory", componentFactory);
202            if (lastCaf != null) {
203                tmpContainer.addComponent(ComponentFactory.class, lastCaf);
204            }
205            ComponentFactory newlastCaf = (ComponentFactory) tmpContainer.getComponent("componentFactory");
206            if (newlastCaf instanceof BehaviorFactory) {
207                ((BehaviorFactory) newlastCaf).wrap(lastCaf);
208            }
209            return newlastCaf;
210        }
211    
212        public PicoBuilder withHiddenImplementations() {
213            behaviors.push(implementationHiding());
214            return this;
215        }
216    
217        public PicoBuilder withSetterInjection() {
218            injectors.add(SDI());
219            return this;
220        }
221    
222        public PicoBuilder withAnnotatedMethodInjection() {
223            injectors.add(annotatedMethodDI());
224            return this;
225        }
226    
227    
228        public PicoBuilder withAnnotatedFieldInjection() {
229            injectors.add(annotatedFieldDI());
230            return this;
231        }
232    
233        public PicoBuilder withTypedFieldInjection() {
234            injectors.add(typedFieldDI());
235            return this;
236        }
237    
238    
239        public PicoBuilder withConstructorInjection() {
240            injectors.add(CDI());
241            return this;
242        }
243    
244        public PicoBuilder withNamedMethodInjection() {
245            injectors.add(namedMethod());
246            return this;
247        }
248    
249        public PicoBuilder withNamedFieldInjection() {
250            injectors.add(namedField());
251            return this;
252        }
253    
254        public PicoBuilder withCaching() {
255            behaviors.push(caching());
256            return this;
257        }
258    
259        public PicoBuilder withComponentFactory(ComponentFactory componentFactory) {
260            if (componentFactory == null) {
261                throw new NullPointerException("CAF cannot be null");
262            }
263            behaviors.push(componentFactory);
264            return this;
265        }
266    
267        public PicoBuilder withSynchronizing() {
268            behaviors.push(new Synchronizing());
269            return this;
270        }
271    
272        public PicoBuilder withLocking() {
273            behaviors.push(new Locking());
274            return this;
275        }
276    
277        public PicoBuilder withBehaviors(BehaviorFactory... factories) {
278            for (BehaviorFactory componentFactory : factories) {
279                behaviors.push(componentFactory);
280            }
281            return this;
282        }
283    
284        public PicoBuilder implementedBy(Class<? extends MutablePicoContainer> containerClass) {
285            mpcClass = containerClass;
286            return this;
287        }
288    
289        public PicoBuilder withMonitor(ComponentMonitor componentMonitor) {
290            this.componentMonitor = componentMonitor;
291            componentMonitorClass = null;
292            return this;
293        }
294    
295        public PicoBuilder withComponentFactory(Class<? extends ComponentFactory> componentFactoryClass) {
296            behaviors.push(componentFactoryClass);
297            return this;
298        }
299    
300        public PicoBuilder withCustomContainerComponent(Object containerDependency) {
301            containerComps.add(containerDependency);
302            return this;
303        }
304    
305        public PicoBuilder withPropertyApplier() {
306            behaviors.push(new PropertyApplying());
307            return this;
308        }
309    
310        public PicoBuilder withAutomatic() {
311            behaviors.push(new Automating());
312            return this;
313        }
314    
315        public PicoBuilder withMethodInjection() {
316            injectors.add(new MethodInjection());
317            return this;
318        }
319    
320        public PicoBuilder addChildToParent() {
321            addChildToParent =  true;
322            return this;
323        }
324    }