001    /*******************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.
003     * ---------------------------------------------------------------------------
004     * The software in this package is published under the terms of the BSD style
005     * license a copy of which has been included with this distribution in the
006     * LICENSE.txt file. 
007     ******************************************************************************/
008    package org.picocontainer.script.groovy.nodes;
009    
010    import java.util.Map;
011    
012    import org.picocontainer.MutablePicoContainer;
013    import org.picocontainer.script.ScriptedPicoContainerMarkupException;
014    import org.codehaus.groovy.runtime.InvokerHelper;
015    
016    /**
017     * Creates on-the-spot Javabeans configurations and registers the result with
018     * the container @link MutablePicoContainer#addComponent}.
019     * 
020     * @author James Strachan
021     * @author Paul Hammant
022     * @author Aslak Hellesøy
023     * @author Michael Rimov
024     * @author Mauro Talevi
025     */
026    @SuppressWarnings("serial")
027    public class BeanNode extends AbstractBuilderNode {
028    
029        /**
030         * The name of the node we're handling.
031         */
032        public static final String NODE_NAME = "bean";
033    
034        /**
035         * Bean class attribute.
036         */
037        public static final String BEAN_CLASS = "beanClass";
038    
039    
040        /**
041         * Default constructor.
042         */
043        public BeanNode() {
044            super(NODE_NAME);
045        }
046    
047        public Object createNewNode(Object current,  Map<String, Object> attributes) {
048            Object bean = createBean(attributes);
049            ((MutablePicoContainer) current).addComponent(bean);
050            return bean;
051        }
052    
053    
054        /**
055         * Instantiates the bean and sets the appropriate attributes.  It then
056         * @param attributes Map
057         * @return Object resulting JavaBean.
058         */
059        @SuppressWarnings("unchecked")
060        protected Object createBean(final  Map<String, Object> attributes) {
061            Class<?> type = (Class<?>) attributes.remove(BEAN_CLASS);
062            if (type == null) {
063                throw new ScriptedPicoContainerMarkupException("Bean must have a beanClass attribute");
064            }
065            try {
066                Object bean = type.newInstance();
067                // now let's set the properties on the bean
068                for (Object o : attributes.entrySet()) {
069                    Map.Entry entry = (Map.Entry) o;
070                    String name = entry.getKey().toString();
071                    Object value = entry.getValue();
072                    InvokerHelper.setProperty(bean, name, value);
073                }
074                return bean;
075            } catch (IllegalAccessException e) {
076                throw new ScriptedPicoContainerMarkupException("Failed to create bean of type '" + type + "'. Reason: " + e, e);
077            } catch (InstantiationException e) {
078                throw new ScriptedPicoContainerMarkupException("Failed to create bean of type " + type + "'. Reason: " + e, e);
079            }
080        }
081    
082        /**
083         * {@inheritDoc}
084         * <p>This version only checks for 'beanClass' and lets all other attributes
085         * through (since they become property values)</p>
086         * @param specifiedAttributes Map
087         * @throws ScriptedPicoContainerMarkupException
088         */
089        public void validateScriptedAttributes(Map<String, Object> specifiedAttributes) throws ScriptedPicoContainerMarkupException {
090            if (!specifiedAttributes.containsKey(BEAN_CLASS)) {
091                throw new ScriptedPicoContainerMarkupException("Attribute " + BEAN_CLASS + " is required.");
092            }
093            //Assume all other attributes
094        }
095    }