001    /*****************************************************************************
002     * Copyright (C) NanoContainer 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 James Strachan                                           *
009     *****************************************************************************/
010    
011    package org.nanocontainer.script.groovy.buildernodes;
012    
013    import java.util.Map;
014    import org.nanocontainer.NanoContainer;
015    import org.nanocontainer.script.NanoContainerMarkupException;
016    import org.picocontainer.defaults.DefaultPicoContainer;
017    import java.security.PrivilegedAction;
018    import org.picocontainer.defaults.ComponentAdapterFactory;
019    import java.security.AccessController;
020    import org.picocontainer.defaults.DefaultComponentAdapterFactory;
021    import org.picocontainer.MutablePicoContainer;
022    import org.picocontainer.PicoContainer;
023    import org.nanocontainer.DefaultNanoContainer;
024    import org.nanocontainer.script.NodeBuilderDecorationDelegate;
025    import org.picocontainer.ComponentMonitor;
026    import org.picocontainer.defaults.DelegatingComponentMonitor;
027    import org.picocontainer.defaults.ComponentMonitorStrategy;
028    
029    /**
030     * Creates a new NanoContainer node.  There may or may not be a parent
031     * container involved.
032     * @author James Strachan
033     * @author Paul Hammant
034     * @author Aslak Hellesøy
035     * @author Michael Rimov
036     * @author Mauro Talevi
037     * @version $Revision: 2695 $
038     */
039    public class ChildContainerNode extends AbstractBuilderNode {
040    
041        /**
042         * Node name.
043         */
044        public static final String NODE_NAME = "container";
045    
046        /**
047         * Supported Attribute: 'class'  Reference to a classname of the container
048         * to use.
049         */
050        private static final String CLASS = "class";
051    
052        /**
053         * The node decoration delegate.
054         */
055        private final NodeBuilderDecorationDelegate decorationDelegate;
056    
057        /**
058         * Attribute: 'componentAdapterFactory' a reference to an instance of a
059         * component adapter factory.
060         */
061        private static final String COMPONENT_ADAPTER_FACTORY = "componentAdapterFactory";
062    
063        /**
064         * Attribute: 'componentMonitor' a reference to an instance of a component monitor.
065         */
066        private static final String COMPONENT_MONITOR = "componentMonitor";
067    
068    
069        /**
070         * Attribute that exists in test cases, but not necessarily used?
071         *
072         */
073        private static final String SCOPE = "scope";
074    
075    
076        /**
077         * Attribute: 'parent'  a reference to the parent for this new container.
078         */
079        private static final String PARENT = "parent";
080    
081    
082        /**
083         * Constructs a child container node.  It requires a <tt>NodeBuilderDecorationDelegate</tt>
084         * for construction.
085         * @param delegate NodeBuilderDecorationDelegate
086         */
087        public ChildContainerNode(NodeBuilderDecorationDelegate delegate) {
088            super(NODE_NAME);
089            decorationDelegate = delegate;
090    
091            this.addAttribute(CLASS)
092                .addAttribute(COMPONENT_ADAPTER_FACTORY)
093                .addAttribute(COMPONENT_MONITOR)
094                .addAttribute(PARENT)
095                .addAttribute(SCOPE);
096    
097    
098        }
099    
100        /**
101         * Creates a new container.  There may or may not be a parent to this container.
102         * Supported attributes are
103         * <p>{@inheritDoc}</p>
104         * @param current NanoContainer
105         * @param attributes Map
106         * @return Object
107         * @throws NanoContainerMarkupException
108         */
109        public Object createNewNode(Object current, Map attributes) throws
110            NanoContainerMarkupException {
111    
112            return createChildContainer(attributes,(NanoContainer) current);
113        }
114    
115        /**
116         * Retrieve the decoration delegate.
117         * @return NodeBuilderDecorationDelegate
118         */
119        private NodeBuilderDecorationDelegate getDecorationDelegate() {
120            return decorationDelegate;
121        }
122    
123    
124    
125        /**
126         * Creates a new container.  There may or may not be a parent to this container.
127         * Supported attributes are:
128         * <ul>
129         *  <li><tt>componentAdapterFactory</tt>: The ComponentAdapterFactory used for new container</li>
130         *  <li><tt>componentMonitor</tt>: The ComponentMonitor used for new container</li>
131         * </ul>
132         * @param attributes Map Attributes defined by the builder in the script.
133         * @param parent The parent container
134         * @return The NanoContainer
135         */
136        protected NanoContainer createChildContainer(Map attributes, NanoContainer parent) {
137    
138            ClassLoader parentClassLoader = null;
139            MutablePicoContainer childContainer = null;
140            if (parent != null) {
141                parentClassLoader = parent.getComponentClassLoader();
142                if ( isAttribute(attributes, COMPONENT_ADAPTER_FACTORY) ) {
143                    ComponentAdapterFactory componentAdapterFactory = createComponentAdapterFactory(attributes);
144                    childContainer = new DefaultPicoContainer(
145                            getDecorationDelegate().decorate(componentAdapterFactory, attributes), parent.getPico());
146                    if ( isAttribute(attributes, COMPONENT_MONITOR) ) {
147                        changeComponentMonitor(childContainer, createComponentMonitor(attributes));
148                    }
149                    parent.getPico().addChildContainer(childContainer);
150                } else if ( isAttribute(attributes, COMPONENT_MONITOR) ) {
151                    ComponentAdapterFactory componentAdapterFactory = new DefaultComponentAdapterFactory(
152                                                        createComponentMonitor(attributes));
153                    childContainer = new DefaultPicoContainer(
154                            getDecorationDelegate().decorate(componentAdapterFactory, attributes), parent.getPico());
155                } else {
156                    childContainer = parent.getPico().makeChildContainer();
157                }
158            } else {
159                parentClassLoader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
160                    public Object run() {
161                        return PicoContainer.class.getClassLoader();
162                    }
163                });
164                ComponentAdapterFactory componentAdapterFactory = createComponentAdapterFactory(attributes);
165                childContainer = new DefaultPicoContainer(
166                        getDecorationDelegate().decorate(componentAdapterFactory, attributes));
167                if ( isAttribute(attributes, COMPONENT_MONITOR) ) {
168                    changeComponentMonitor(childContainer, createComponentMonitor(attributes));
169                }
170            }
171    
172            MutablePicoContainer decoratedPico = getDecorationDelegate().decorate(childContainer);
173            if ( isAttribute(attributes, CLASS) )  {
174                Class clazz = (Class) attributes.get(CLASS);
175                return createNanoContainer(clazz, decoratedPico, parentClassLoader);
176            } else {
177                return new DefaultNanoContainer(parentClassLoader, decoratedPico);
178            }
179        }
180    
181        private void changeComponentMonitor(MutablePicoContainer childContainer, ComponentMonitor monitor) {
182            if ( childContainer instanceof ComponentMonitorStrategy ){
183                ((ComponentMonitorStrategy)childContainer).changeMonitor(monitor);
184            }
185        }
186    
187        private NanoContainer createNanoContainer(Class clazz, MutablePicoContainer decoratedPico, ClassLoader parentClassLoader) {
188            DefaultPicoContainer instantiatingContainer = new DefaultPicoContainer();
189            instantiatingContainer.registerComponentInstance(ClassLoader.class, parentClassLoader);
190            instantiatingContainer.registerComponentInstance(MutablePicoContainer.class, decoratedPico);
191            instantiatingContainer.registerComponentImplementation(NanoContainer.class, clazz);
192            Object componentInstance = instantiatingContainer.getComponentInstance(NanoContainer.class);
193            return (NanoContainer) componentInstance;
194        }
195    
196        private ComponentAdapterFactory createComponentAdapterFactory(Map attributes) {
197            final ComponentAdapterFactory factory = (ComponentAdapterFactory) attributes.remove(COMPONENT_ADAPTER_FACTORY);
198            if ( factory == null ){
199                return new DefaultComponentAdapterFactory();
200            }
201            return factory;
202        }
203    
204        private ComponentMonitor createComponentMonitor(Map attributes) {
205            final ComponentMonitor monitor = (ComponentMonitor) attributes.remove(COMPONENT_MONITOR);
206            if ( monitor == null ){
207                return new DelegatingComponentMonitor();
208            }
209            return monitor;
210        }
211    
212    
213    }