/* * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package org.apache.tools.ant.taskdefs; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.HashMap; import org.apache.tools.ant.AntTypeDefinition; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.ComponentHelper; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectHelper; import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.Task; import org.apache.tools.ant.TaskContainer; import org.apache.tools.ant.UnknownElement; /** * Describe class MacroDef here. * * @author Peter Reilly * @since Ant 1.6 */ public class MacroDef extends AntlibDefinition { private NestedSequential nestedSequential; private String name; private List attributes = new ArrayList(); private Map elements = new HashMap(); /** * Name of the definition * @param name the name of the definition */ public void setName(String name) { this.name = name; } /** * This is the sequential nested element of the macrodef. * * @return a sequential element to be configured. */ public NestedSequential createSequential() { if (this.nestedSequential != null) { throw new BuildException("Only one sequential allowed"); } this.nestedSequential = new NestedSequential(); return this.nestedSequential; } /** * The class corresponding to the sequential nested element. * This is a simple task container. */ public static class NestedSequential implements TaskContainer { private List nested = new ArrayList(); /** * Add a task or type to the container. * * @param task an unknown element. */ public void addTask(Task task) { nested.add(task); } /** * @return the list of unknown elements */ public List getNested() { return nested; } /** * A compare function to compare this with another * NestedSequential. * It calls similar on the nested unknown elements. * * @param other the nested sequential to compare with. * @return true if they are similar, false otherwise */ public boolean similar(NestedSequential other) { if (nested.size() != other.nested.size()) { return false; } for (int i = 0; i < nested.size(); ++i) { UnknownElement me = (UnknownElement) nested.get(i); UnknownElement o = (UnknownElement) other.nested.get(i); if (!me.similar(o)) { return false; } } return true; } } /** * Convert the nested sequential to an unknown element * @return the nested sequential as an unknown element. */ public UnknownElement getNestedTask() { UnknownElement ret = new UnknownElement("sequential"); ret.setTaskName("sequential"); ret.setNamespace(""); ret.setQName("sequential"); new RuntimeConfigurable(ret, "sequential"); for (int i = 0; i < nestedSequential.getNested().size(); ++i) { UnknownElement e = (UnknownElement) nestedSequential.getNested().get(i); ret.addChild(e); ret.getWrapper().addChild(e.getWrapper()); } return ret; } /** * @return the nested Attributes */ public List getAttributes() { return attributes; } /** * @return the nested elements */ public Map getElements() { return elements; } /** * Check if a character is a valid character for an element or * attribute name * @param c the character to check * @return true if the character is a letter or digit or '.' or '-' * attribute name */ public static boolean isValidNameCharacter(char c) { // ? is there an xml api for this ? return Character.isLetterOrDigit(c) || c == '.' || c == '-'; } /** * Check if a string is a valid name for an element or * attribute * @param name the string to check * @return true if the name consists of valid name characters */ private static boolean isValidName(String name) { if (name.length() == 0) { return false; } for (int i = 0; i < name.length(); ++i) { if (!isValidNameCharacter(name.charAt(i))) { return false; } } return true; } /** * Add an attribute element. * * @param attribute an attribute nested element. */ public void addConfiguredAttribute(Attribute attribute) { if (attribute.getName() == null) { throw new BuildException( "the attribute nested element needed a \"name\" attribute"); } attributes.add(attribute); } /** * Add an element element. * * @param element an element nested element. */ public void addConfiguredElement(TemplateElement element) { if (element.getName() == null) { throw new BuildException( "the element nested element needed a \"name\" attribute"); } elements.put(element.getName(), element); } /** * Create a new ant type based on the embedded tasks and types. * */ public void execute() { if (nestedSequential == null) { throw new BuildException("Missing sequential element"); } if (name == null) { throw new BuildException("Name not specified"); } name = ProjectHelper.genComponentName(getURI(), name); MyAntTypeDefinition def = new MyAntTypeDefinition(this); def.setName(name); def.setClass(MacroInstance.class); ComponentHelper helper = ComponentHelper.getComponentHelper( getProject()); helper.addDataTypeDefinition(def); } /** * A nested element for the MacroDef task. * */ public static class Attribute { private String name; private String defaultValue; /** * The name of the attribute. * * @param name the name of the attribute */ public void setName(String name) { if (!isValidName(name)) { throw new BuildException( "Illegal name [" + name + "] for attribute"); } this.name = name; } /** * @return the name of the attribute */ public String getName() { return name; } /** * The default value to use if the parameter is not * used in the templated instance. * * @param defaultValue the default value */ public void setDefault(String defaultValue) { this.defaultValue = defaultValue; } /** * @return the default value, null if not set */ public String getDefault() { return defaultValue; } /** * equality method * * @param obj an Object value * @return a boolean value */ public boolean equals(Object obj) { if (obj == null) { return false; } if (obj.getClass() != getClass()) { return false; } Attribute other = (Attribute) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (defaultValue == null) { if (other.defaultValue != null) { return false; } } else if (!defaultValue.equals(other.defaultValue)) { return false; } return true; } } /** * A nested element for the MacroDef task. * */ public static class TemplateElement { private String name; private boolean optional = false; /** * The name of the element. * * @param name the name of the element. */ public void setName(String name) { if (!isValidName(name)) { throw new BuildException( "Illegal name [" + name + "] for attribute"); } this.name = name; } /** * @return the name of the element. */ public String getName() { return name; } /** * is this element optional ? * * @param optional if true this element may be left out, default * is false. */ public void setOptional(boolean optional) { this.optional = optional; } /** * @return the optional attribute */ public boolean isOptional() { return optional; } /** * equality method * * @param obj an Object value * @return a boolean value */ public boolean equals(Object obj) { if (obj == null) { return false; } if (obj.getClass() != getClass()) { return false; } TemplateElement other = (TemplateElement) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return optional == other.optional; } } /** * equality method for macrodef, ignores project and * runtime info. * * @param obj an Object value * @return a boolean value */ public boolean equals(Object obj) { if (obj == null) { return false; } if (!obj.getClass().equals(getClass())) { return false; } MacroDef other = (MacroDef) obj; if (name == null) { return other.name == null; } if (!name.equals(other.name)) { return false; } if (getURI() == null || getURI().equals("") || getURI().equals(ProjectHelper.ANT_CORE_URI)) { if (!(other.getURI() == null || other.getURI().equals("") || other.getURI().equals(ProjectHelper.ANT_CORE_URI))) { return false; } } else { if (!getURI().equals(other.getURI())) { return false; } } if (!nestedSequential.similar(other.nestedSequential)) { return false; } if (!attributes.equals(other.attributes)) { return false; } if (!elements.equals(other.elements)) { return false; } return true; } /** * extends AntTypeDefinition, on create * of the object, the template macro definition * is given. */ private static class MyAntTypeDefinition extends AntTypeDefinition { private MacroDef macroDef; /** * Creates a new MyAntTypeDefinition instance. * * @param macroDef a MacroDef value */ public MyAntTypeDefinition(MacroDef macroDef) { this.macroDef = macroDef; } /** * create an instance of the definition. * The instance may be wrapped in a proxy class. * @param project the current project * @return the created object */ public Object create(Project project) { Object o = super.create(project); if (o == null) { return null; } ((MacroInstance) o).setMacroDef(macroDef); return o; } /** * Equality method for this definition * * @param other another definition * @param project the current project * @return true if the definitions are the same */ public boolean sameDefinition(AntTypeDefinition other, Project project) { if (!super.sameDefinition(other, project)) { return false; } MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other; return macroDef.equals(otherDef.macroDef); } /** * Similar method for this definition * * @param other another definition * @param project the current project * @return true if the definitions are the same */ public boolean similarDefinition( AntTypeDefinition other, Project project) { if (!super.similarDefinition(other, project)) { return false; } MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other; return macroDef.equals(otherDef.macroDef); } } }