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    
054        public PicoBuilder(PicoContainer parentContainer, InjectionFactory injectionType) {
055            this.injectionType = injectionType;
056            if (parentContainer != null) {
057                this.parentContainer = parentContainer;
058            } else {
059                this.parentContainer = new EmptyPicoContainer();
060            }
061        }
062    
063        public PicoBuilder(PicoContainer parentContainer) {
064            this(parentContainer, adaptiveDI());
065        }
066    
067        public PicoBuilder(InjectionFactory injectionType) {
068            this(new EmptyPicoContainer(), injectionType);
069        }
070    
071        public PicoBuilder() {
072            this(new EmptyPicoContainer(), adaptiveDI());
073        }
074    
075        private final Stack<Object> componentFactories = new Stack<Object>();
076    
077        private InjectionFactory injectionType;
078    
079        private Class<? extends ComponentMonitor> componentMonitorClass = NullComponentMonitor.class;
080        private Class<? extends LifecycleStrategy> lifecycleStrategyClass = NullLifecycleStrategy.class;
081    
082        public PicoBuilder withLifecycle() {
083            lifecycleStrategyClass = StartableLifecycleStrategy.class;
084            return this;
085        }
086    
087        public PicoBuilder withReflectionLifecycle() {
088            lifecycleStrategyClass = ReflectionLifecycleStrategy.class;
089            return this;
090        }
091    
092        public PicoBuilder withConsoleMonitor() {
093            componentMonitorClass =  ConsoleComponentMonitor.class;
094            return this;
095        }
096    
097        public PicoBuilder withMonitor(Class<? extends ComponentMonitor> cmClass) {
098            if (cmClass == null) {
099                throw new NullPointerException("monitor class cannot be null");
100            }
101            if (!ComponentMonitor.class.isAssignableFrom(cmClass)) {
102                throw new ClassCastException(cmClass.getName() + " is not a " + ComponentMonitor.class.getName());
103    
104            }
105            componentMonitorClass = cmClass;
106            componentMonitor = null;
107            return this;
108        }
109    
110        public MutablePicoContainer build() {
111    
112            DefaultPicoContainer temp = new TransientPicoContainer();
113            temp.addComponent(PicoContainer.class, parentContainer);
114    
115            for (Object containerComp : containerComps) {
116                temp.addComponent(containerComp);
117            }
118    
119            ComponentFactory lastCaf = injectionType;
120            while (!componentFactories.empty()) {
121                Object componentFactory = componentFactories.pop();
122                DefaultPicoContainer temp2 = new TransientPicoContainer(temp);
123                temp2.addComponent("componentFactory", componentFactory);
124                if (lastCaf != null) {
125                    temp2.addComponent(ComponentFactory.class, lastCaf);
126                }
127                ComponentFactory penultimateCaf = lastCaf;
128                lastCaf = (ComponentFactory) temp2.getComponent("componentFactory");
129                if (lastCaf instanceof BehaviorFactory) {
130                    ((BehaviorFactory) lastCaf).wrap(penultimateCaf);
131                }
132            }
133    
134            temp.addComponent(ComponentFactory.class, lastCaf);
135            if (componentMonitorClass == null) {
136                temp.addComponent(ComponentMonitor.class, componentMonitor);
137            } else {
138                temp.addComponent(ComponentMonitor.class, componentMonitorClass);
139            }
140            temp.addComponent(LifecycleStrategy.class, lifecycleStrategyClass);
141            temp.addComponent("mpc", mpcClass);
142    
143    
144            return (MutablePicoContainer) temp.getComponent("mpc");
145        }
146    
147        public PicoBuilder withHiddenImplementations() {
148            componentFactories.push(implementationHiding());
149            return this;
150        }
151    
152        public PicoBuilder withSetterInjection() {
153            injectionType = SDI();
154            return this;
155        }
156    
157        public PicoBuilder withAnnotatedMethodInjection() {
158            injectionType = annotatedMethodDI();
159            return this;
160        }
161    
162    
163        public PicoBuilder withAnnotatedFieldInjection() {
164            injectionType = annotatedFieldDI();
165            return this;
166        }
167    
168    
169        public PicoBuilder withConstructorInjection() {
170            injectionType = CDI();
171            return this;
172        }
173    
174        public PicoBuilder withCaching() {
175            componentFactories.push(caching());
176            return this;
177        }
178    
179        public PicoBuilder withComponentFactory(ComponentFactory componentFactory) {
180            if (componentFactory == null) {
181                throw new NullPointerException("CAF cannot be null");
182            }
183            componentFactories.push(componentFactory);
184            return this;
185        }
186    
187        public PicoBuilder withSynchronizing() {
188            componentFactories.push(Synchronizing.class);
189            return this;
190        }
191    
192        public PicoBuilder withLocking() {
193            componentFactories.push(Locking.class);
194            return this;
195        }
196    
197        public PicoBuilder withBehaviors(BehaviorFactory... factories) {
198            for (ComponentFactory componentFactory : factories) {
199                componentFactories.push(componentFactory);
200            }
201            return this;
202        }
203    
204        public PicoBuilder implementedBy(Class<? extends MutablePicoContainer> containerClass) {
205            mpcClass = containerClass;
206            return this;
207        }
208    
209        public PicoBuilder withMonitor(ComponentMonitor componentMonitor) {
210            this.componentMonitor = componentMonitor;
211            componentMonitorClass = null;
212            return this;
213        }
214    
215        public PicoBuilder withComponentFactory(Class<? extends ComponentFactory> componentFactoryClass) {
216            componentFactories.push(componentFactoryClass);
217            return this;
218        }
219    
220        public PicoBuilder withCustomContainerComponent(Object containerDependency) {
221            containerComps.add(containerDependency);
222            return this;
223        }
224    
225        public PicoBuilder withPropertyApplier() {
226            componentFactories.push(PropertyApplying.class);
227            return this;
228        }
229    
230        public PicoBuilder withAutomatic() {
231            componentFactories.push(Automating.class);
232            return this;
233        }
234    
235        public PicoBuilder withMethodInjection() {
236            componentFactories.push(new MethodInjection());
237            return this;
238        }
239    }