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 tempContainer = new TransientPicoContainer();
114            tempContainer.addComponent(PicoContainer.class, parentContainer);
115    
116            addContainerComponents(tempContainer);
117    
118            ComponentFactory lastCaf = injectionType;
119            while (!componentFactories.empty()) {
120                lastCaf = buildComponentFactory(tempContainer, lastCaf);
121            }
122    
123            tempContainer.addComponent(ComponentFactory.class, lastCaf);
124    
125            buildComponentMonitor(tempContainer);
126    
127            tempContainer.addComponent(LifecycleStrategy.class, lifecycleStrategyClass);
128            tempContainer.addComponent("mpc", mpcClass);
129    
130            MutablePicoContainer newContainer = (MutablePicoContainer) tempContainer.getComponent("mpc");
131    
132            addChildToParent(newContainer);
133            return newContainer;
134        }
135    
136        private void buildComponentMonitor(DefaultPicoContainer tempContainer) {
137            if (componentMonitorClass == null) {
138                tempContainer.addComponent(ComponentMonitor.class, componentMonitor);
139            } else {
140                tempContainer.addComponent(ComponentMonitor.class, componentMonitorClass);
141            }
142        }
143    
144        private void addChildToParent(MutablePicoContainer newContainer) {
145            if (addChildToParent) {
146                if (parentContainer instanceof MutablePicoContainer) {
147                    ((MutablePicoContainer)parentContainer).addChildContainer(newContainer);
148                } else {
149                    throw new PicoCompositionException("If using addChildContainer() the parent must be a MutablePicoContainer");
150                }
151            }
152        }
153    
154        private void addContainerComponents(DefaultPicoContainer temp) {
155            for (Object containerComp : containerComps) {
156                temp.addComponent(containerComp);
157            }
158        }
159    
160        private ComponentFactory buildComponentFactory(DefaultPicoContainer container, final ComponentFactory lastCaf) {
161            Object componentFactory = componentFactories.pop();
162            DefaultPicoContainer tmpContainer = new TransientPicoContainer(container);
163            tmpContainer.addComponent("componentFactory", componentFactory);
164            if (lastCaf != null) {
165                tmpContainer.addComponent(ComponentFactory.class, lastCaf);
166            }
167            ComponentFactory newlastCaf = (ComponentFactory) tmpContainer.getComponent("componentFactory");
168            if (newlastCaf instanceof BehaviorFactory) {
169                ((BehaviorFactory) newlastCaf).wrap(lastCaf);
170            }
171            return newlastCaf;
172        }
173    
174        public PicoBuilder withHiddenImplementations() {
175            componentFactories.push(implementationHiding());
176            return this;
177        }
178    
179        public PicoBuilder withSetterInjection() {
180            injectionType = SDI();
181            return this;
182        }
183    
184        public PicoBuilder withAnnotatedMethodInjection() {
185            injectionType = annotatedMethodDI();
186            return this;
187        }
188    
189    
190        public PicoBuilder withAnnotatedFieldInjection() {
191            injectionType = annotatedFieldDI();
192            return this;
193        }
194    
195    
196        public PicoBuilder withConstructorInjection() {
197            injectionType = CDI();
198            return this;
199        }
200    
201        public PicoBuilder withCaching() {
202            componentFactories.push(caching());
203            return this;
204        }
205    
206        public PicoBuilder withComponentFactory(ComponentFactory componentFactory) {
207            if (componentFactory == null) {
208                throw new NullPointerException("CAF cannot be null");
209            }
210            componentFactories.push(componentFactory);
211            return this;
212        }
213    
214        public PicoBuilder withSynchronizing() {
215            componentFactories.push(Synchronizing.class);
216            return this;
217        }
218    
219        public PicoBuilder withLocking() {
220            componentFactories.push(Locking.class);
221            return this;
222        }
223    
224        public PicoBuilder withBehaviors(BehaviorFactory... factories) {
225            for (ComponentFactory componentFactory : factories) {
226                componentFactories.push(componentFactory);
227            }
228            return this;
229        }
230    
231        public PicoBuilder implementedBy(Class<? extends MutablePicoContainer> containerClass) {
232            mpcClass = containerClass;
233            return this;
234        }
235    
236        public PicoBuilder withMonitor(ComponentMonitor componentMonitor) {
237            this.componentMonitor = componentMonitor;
238            componentMonitorClass = null;
239            return this;
240        }
241    
242        public PicoBuilder withComponentFactory(Class<? extends ComponentFactory> componentFactoryClass) {
243            componentFactories.push(componentFactoryClass);
244            return this;
245        }
246    
247        public PicoBuilder withCustomContainerComponent(Object containerDependency) {
248            containerComps.add(containerDependency);
249            return this;
250        }
251    
252        public PicoBuilder withPropertyApplier() {
253            componentFactories.push(PropertyApplying.class);
254            return this;
255        }
256    
257        public PicoBuilder withAutomatic() {
258            componentFactories.push(Automating.class);
259            return this;
260        }
261    
262        public PicoBuilder withMethodInjection() {
263            componentFactories.push(new MethodInjection());
264            return this;
265        }
266    
267        public PicoBuilder addChildToParent() {
268            addChildToParent =  true;
269            return this;
270        }
271    }