diff --git a/src/main/org/apache/tools/ant/DynamicObjectAttribute.java b/src/main/org/apache/tools/ant/DynamicObjectAttribute.java new file mode 100644 index 000000000..9a9aca908 --- /dev/null +++ b/src/main/org/apache/tools/ant/DynamicObjectAttribute.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant; + +/** + * Enables a task to control unknown attributes. + * Same as {@link DynamicAttribute} but authorize arbitrary Object as value + * rather than String + * + * @see DynamicAttribute + * @since Ant 1.9 + */ +public interface DynamicObjectAttribute { + + /** + * Set a named attribute to the given value + * + * @param name the name of the attribute + * @param value the new value of the attribute + * @throws BuildException when any error occurs + */ + void setDynamicAttribute(String name, Object value) + throws BuildException; + +} diff --git a/src/main/org/apache/tools/ant/Evaluable.java b/src/main/org/apache/tools/ant/Evaluable.java new file mode 100644 index 000000000..abf2ba280 --- /dev/null +++ b/src/main/org/apache/tools/ant/Evaluable.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant; + +/** + * Kind of task attribute that can be evaluated before being assigned + * + * @see RuntimeConfigurable + */ +public interface Evaluable { + + Object eval(); + +} diff --git a/src/main/org/apache/tools/ant/IntrospectionHelper.java b/src/main/org/apache/tools/ant/IntrospectionHelper.java index 1c8e39399..ed34ed3e9 100644 --- a/src/main/org/apache/tools/ant/IntrospectionHelper.java +++ b/src/main/org/apache/tools/ant/IntrospectionHelper.java @@ -389,6 +389,11 @@ public final class IntrospectionHelper { dc.setDynamicAttribute(uri, localName, qName, value.toString()); return; } + if (element instanceof DynamicObjectAttribute) { + DynamicObjectAttribute dc = (DynamicObjectAttribute) element; + dc.setDynamicAttribute(attributeName.toLowerCase(Locale.ENGLISH), value); + return; + } if (element instanceof DynamicAttribute) { DynamicAttribute dc = (DynamicAttribute) element; dc.setDynamicAttribute(attributeName.toLowerCase(Locale.ENGLISH), value.toString()); diff --git a/src/main/org/apache/tools/ant/RuntimeConfigurable.java b/src/main/org/apache/tools/ant/RuntimeConfigurable.java index 63143261b..d3502d6e3 100644 --- a/src/main/org/apache/tools/ant/RuntimeConfigurable.java +++ b/src/main/org/apache/tools/ant/RuntimeConfigurable.java @@ -25,8 +25,8 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Iterator; +import java.util.Map.Entry; import org.apache.tools.ant.util.CollectionUtils; import org.apache.tools.ant.taskdefs.MacroDef; @@ -45,13 +45,14 @@ public class RuntimeConfigurable implements Serializable { private static final long serialVersionUID = 1L; /** Empty Hashtable. */ - private static final Hashtable EMPTY_HASHTABLE = new Hashtable(0); + private static final Hashtable EMPTY_HASHTABLE = + new Hashtable(0); /** Name of the element to configure. */ private String elementTag = null; /** List of child element wrappers. */ - private List/**/ children = null; + private List children = null; /** The element to configure. It is only used during * maybeConfigure. @@ -77,7 +78,7 @@ public class RuntimeConfigurable implements Serializable { * the "refid" attribute, so now (ANT 1.7) the refid * attribute will be processed first. */ - private LinkedHashMap/**/ attributeMap = null; + private LinkedHashMap attributeMap = null; /** Text appearing within the element. */ private StringBuffer characters = null; @@ -181,14 +182,25 @@ public class RuntimeConfigurable implements Serializable { * @param value the attribute's value. */ public synchronized void setAttribute(String name, String value) { + setAttribute(name, (Object) value); + } + + /** + * Set an attribute to a given value. + * + * @param name the name of the attribute. + * @param value the attribute's value. + * @since 1.9 + */ + public synchronized void setAttribute(String name, Object value) { if (name.equalsIgnoreCase(ProjectHelper.ANT_TYPE)) { - this.polyType = value; + this.polyType = value == null ? null : value.toString(); } else { if (attributeMap == null) { - attributeMap = new LinkedHashMap(); + attributeMap = new LinkedHashMap(); } if (name.equalsIgnoreCase("refid") && !attributeMap.isEmpty()) { - LinkedHashMap newAttributeMap = new LinkedHashMap(); + LinkedHashMap newAttributeMap = new LinkedHashMap(); newAttributeMap.put(name, value); newAttributeMap.putAll(attributeMap); attributeMap = newAttributeMap; @@ -196,7 +208,7 @@ public class RuntimeConfigurable implements Serializable { attributeMap.put(name, value); } if (name.equals("id")) { - this.id = value; + this.id = value == null ? null : value.toString(); } } } @@ -215,9 +227,9 @@ public class RuntimeConfigurable implements Serializable { * @return Attribute name to attribute value map. * @since Ant 1.6 */ - public synchronized Hashtable getAttributeMap() { + public synchronized Hashtable getAttributeMap() { return (attributeMap == null) - ? EMPTY_HASHTABLE : new Hashtable(attributeMap); + ? EMPTY_HASHTABLE : new Hashtable(attributeMap); } /** @@ -379,15 +391,19 @@ public class RuntimeConfigurable implements Serializable { IntrospectionHelper.getHelper(p, target.getClass()); if (attributeMap != null) { - for (Iterator iter = attributeMap.entrySet().iterator(); iter.hasNext();) { - Map.Entry entry = (Map.Entry) iter.next(); - String name = (String) entry.getKey(); - String value = (String) entry.getValue(); + for (Entry entry : attributeMap.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); // reflect these into the target, defer for // MacroInstance where properties are expanded for the // nested sequential - Object attrValue = PropertyHelper.getPropertyHelper(p).parseProperties(value); + Object attrValue; + if (value instanceof Evaluable) { + attrValue = ((Evaluable) value).eval(); + } else { + attrValue = PropertyHelper.getPropertyHelper(p).parseProperties(value.toString()); + } if (target instanceof MacroInstance) { for (Iterator attrs = ((MacroInstance) target).getMacroDef().getAttributes().iterator(); attrs.hasNext();) { MacroDef.Attribute attr = (MacroDef.Attribute) attrs.next();