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 }