package org.oddjob.values; import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.ConversionException; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.DynaClass; import org.apache.commons.beanutils.DynaProperty; import org.apache.commons.beanutils.LazyDynaBean; import org.apache.commons.beanutils.LazyDynaMap; import org.apache.commons.beanutils.MutableDynaClass; import org.oddjob.arooa.ArooaConstants; import org.oddjob.arooa.ArooaValue; import org.oddjob.arooa.beanutils.BeanUtilsPropertyAccessor; import org.oddjob.framework.SimpleJob; /** * @oddjob.description This job provides a 'variables' * like declaration within Oddjob. * <p> * The variables job is like a bean where any property can be set * with any value. * <p> * Because names are properties, they can only be valid simple property * names. 'java.version' is not valid simple property because it is * interpreted as a value 'java' that has a property 'version'. To allow * these type of properties to be referenced in Oddjob use * {@link org.oddjob.values.properties.PropertiesJob}. * * @oddjob.example * * A simple variable. * * {@oddjob.xml.resource org/oddjob/values/VariablesExample.xml} * * * @author rob */ public class VariablesJob extends SimpleJob implements DynaBean { private final Map<String, Object> values = new LinkedHashMap<String, Object>(); /** The LazyDynaBean to delagate to. * * For the refererence: * * * @oddjob.property <i>Any type</i> * @oddjob.description Any type. * @oddjob.required No. * */ private final LazyDynaBean dynaBean = new LazyDynaMap(values); /** * The DynaClass - every type is an ArooaValue. */ private final MutableDynaClass dynaClass = new VariablesDynaClass( dynaBean); /** * Add a name value pair. This method will be called during parsing. * * @param name The name of the variable. * @param value The runtime configurable for the value. */ public void setValue(String name, ArooaValue value) { logger().debug("Setting [" + name + "] = [" + value + "]"); dynaBean.set(name, value); } /* * (non-Javadoc) * @see org.oddjob.jobs.AbstractJob#execute() */ @Override protected int execute() throws Exception { return 0; } @Override protected void onReset() { // Stop concurrent modification exception. List<String> keySet = new ArrayList<String>(values.keySet()); for (String name : keySet) { if (ArooaConstants.ID_PROPERTY.equals(name)) { continue; } values.remove(name); dynaClass.remove(name); } } /** * Does the specified mapped property contain a value for the specified * key value? * * @param name Name of the property to check * @param key Name of the key to check * * @exception IllegalArgumentException if there is no property * of the specified name */ @Override public boolean contains(String name, String key) { return dynaBean.contains(name, key); } /** * Return the value of a simple property with the specified name. * * @param name Name of the property whose value is to be retrieved * * @exception IllegalArgumentException if there is no property * of the specified name */ @Override public Object get(String name) { return dynaBean.get(name); } /** * Return the value of an indexed property with the specified name. * * @param name Name of the property whose value is to be retrieved * @param index Index of the value to be retrieved * * @exception IllegalArgumentException if there is no property * of the specified name * @exception IllegalArgumentException if the specified property * exists, but is not indexed * @exception IndexOutOfBoundsException if the specified index * is outside the range of the underlying property * @exception NullPointerException if no array or List has been * initialized for this property */ @Override public Object get(String name, int index) { return dynaBean.get(name, index); } /** * Return the value of a mapped property with the specified name, * or <code>null</code> if there is no value for the specified key. * * @param name Name of the property whose value is to be retrieved * @param key Key of the value to be retrieved * * @exception IllegalArgumentException if there is no property * of the specified name * @exception IllegalArgumentException if the specified property * exists, but is not mapped */ @Override public Object get(String name, String key) { return dynaBean.get(name, key); } /** * Return the <code>DynaClass</code> instance that describes the set of * properties available for this DynaBean. */ @Override public DynaClass getDynaClass() { return dynaClass; } /** * Remove any existing value for the specified key on the * specified mapped property. * * @param name Name of the property for which a value is to * be removed * @param key Key of the value to be removed * * @exception IllegalArgumentException if there is no property * of the specified name */ @Override public void remove(String name, String key) { dynaBean.remove(name, key); } /** * Set the value of a simple property with the specified name. * * @param name Name of the property whose value is to be set * @param value Value to which this property is to be set * * @exception ConversionException if the specified value cannot be * converted to the type required for this property * @exception IllegalArgumentException if there is no property * of the specified name * @exception NullPointerException if an attempt is made to set a * primitive property to null */ @Override public void set(String name, Object value) { logger().debug("Setting [" + name + "] = [" + value + "]"); // if (! (value instanceof ArooaValue)) { // throw new IllegalArgumentException("Variables can only be ArooaValue objects."); // } // will throw an exception if it fails. BeanUtilsPropertyAccessor.validateSimplePropertyName(name); dynaBean.set(name, value); } /** * Set the value of an indexed property with the specified name. * * @param name Name of the property whose value is to be set * @param index Index of the property to be set * @param value Value to which this property is to be set * * @exception ConversionException if the specified value cannot be * converted to the type required for this property * @exception IllegalArgumentException if there is no property * of the specified name * @exception IllegalArgumentException if the specified property * exists, but is not indexed * @exception IndexOutOfBoundsException if the specified index * is outside the range of the underlying property */ @Override public void set(String name, int index, Object value) { logger().debug("Setting [" + name + "[" + index + "]] = [" + value + "]"); dynaBean.set(name, index, value); } /** * Set the value of a mapped property with the specified name. * * @param name Name of the property whose value is to be set * @param key Key of the property to be set * @param value Value to which this property is to be set * * @exception ConversionException if the specified value cannot be * converted to the type required for this property * @exception IllegalArgumentException if there is no property * of the specified name * @exception IllegalArgumentException if the specified property * exists, but is not mapped */ @Override public void set(String name, String key, Object value) { logger().debug("Setting [" + name + "(" + key + ")] = [" + value + "]"); dynaBean.set(name, key, value); } @Override public String toString() { return "Variables: " + get(ArooaConstants.ID_PROPERTY); } /** * <p>A DynaClass for the variables which always returns * {@link org.oddjob.arooa.ArooaValue} as the type * for all properties. * * @see org.apche.commons.beanutils.LazyDynaBean */ static class VariablesDynaClass implements MutableDynaClass, Serializable { private static final long serialVersionUID = 20070124; private final MutableDynaClass delegate; VariablesDynaClass(DynaBean dynaBean) { this.delegate = (MutableDynaClass) dynaBean.getDynaClass(); } /* * (non-Javadoc) * @see org.apache.commons.beanutils.DynaClass#getDynaProperty(java.lang.String) */ @Override public DynaProperty getDynaProperty(String name) { DynaProperty dynaProperty = delegate.getDynaProperty(name); if (dynaProperty == null) { throw new NullPointerException("LazyDynaClass should always return a DynaProperty!"); } if (dynaProperty.getContentType() != null && ArooaValue.class.isAssignableFrom(dynaProperty.getContentType())) { return dynaProperty; } return new DynaProperty(dynaProperty.getName(), ArooaValue.class, dynaProperty.getContentType()); } /* * (non-Javadoc) * @see org.apache.commons.beanutils.DynaClass#getDynaProperties() */ @Override public DynaProperty[] getDynaProperties() { return delegate.getDynaProperties(); } /* * (non-Javadoc) * @see org.apache.commons.beanutils.DynaClass#getName() */ @Override public String getName() { return delegate.getName(); } /* * (non-Javadoc) * @see org.apache.commons.beanutils.DynaClass#newInstance() */ @Override public DynaBean newInstance() throws IllegalAccessException, InstantiationException { throw new UnsupportedOperationException(); } @Override public void add(String name) { delegate.add(name); } @Override public void add(String name, @SuppressWarnings("rawtypes") Class type) { delegate.add(name, type); } @Override public void add(String name, @SuppressWarnings("rawtypes") Class type, boolean readable, boolean writeable) { delegate.add(name, type, readable, writeable); } @Override public boolean isRestricted() { return delegate.isRestricted(); } @Override public void remove(String name) { delegate.remove(name); } @Override public void setRestricted(boolean restricted) { delegate.setRestricted(restricted); } } }