diff --git a/src/main/org/apache/tools/ant/types/DataType.java b/src/main/org/apache/tools/ant/types/DataType.java index 65a4e8b99..78192c5c5 100644 --- a/src/main/org/apache/tools/ant/types/DataType.java +++ b/src/main/org/apache/tools/ant/types/DataType.java @@ -17,11 +17,13 @@ package org.apache.tools.ant.types; - import java.util.Stack; -import org.apache.tools.ant.BuildException; + import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.ComponentHelper; import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.util.IdentityStack; /** * Base class for those classes that can appear inside the build file @@ -35,6 +37,7 @@ import org.apache.tools.ant.ProjectComponent; * */ public abstract class DataType extends ProjectComponent { + /** * The description the user has set. * @@ -105,6 +108,40 @@ public abstract class DataType extends ProjectComponent { checked = false; } + /** + * Gets as descriptive as possible a name used for this datatype instance. + * @return String name. + */ + protected String getDataTypeName() { + Project p = getProject(); + if (p != null) { + return ComponentHelper.getComponentHelper(p) + .getElementName(this, true); + } + String classname = getClass().getName(); + return classname.substring(classname.lastIndexOf('.') + 1); + } + + /** + * Convenience method. + * @since Ant 1.7 + */ + protected void dieOnCircularReference() { + dieOnCircularReference(getProject()); + } + + /** + * Convenience method. + * @param p the Ant Project instance against which to resolve references. + * @since Ant 1.7 + */ + protected void dieOnCircularReference(Project p) { + if (checked || !isReference()) { + return; + } + dieOnCircularReference(new IdentityStack(this), p); + } + /** * Check to see whether any DataType we hold references to is * included in the Stack (which holds all DataType instances that @@ -120,9 +157,9 @@ public abstract class DataType extends ProjectComponent { *

The general contract of this method is that it shouldn't do * anything if {@link #checked checked} is true and * set it to true on exit.

- * @param stack the stack of references to check - * @param project the project to use to dereference the references - * @throws BuildException on error + * @param stack the stack of references to check. + * @param project the project to use to dereference the references. + * @throws BuildException on error. */ protected void dieOnCircularReference(final Stack stack, final Project project) @@ -134,43 +171,96 @@ public abstract class DataType extends ProjectComponent { Object o = ref.getReferencedObject(project); if (o instanceof DataType) { - if (stack.contains(o)) { + IdentityStack id = IdentityStack.getInstance(stack); + + if (id.contains(o)) { throw circularReference(); } else { - stack.push(o); - ((DataType) o).dieOnCircularReference(stack, project); - stack.pop(); + id.push(o); + ((DataType) o).dieOnCircularReference(id, project); + id.pop(); } } checked = true; } + /** + * Allow DataTypes outside org.apache.tools.ant.types to indirectly call + * dieOnCircularReference on nested DataTypes. + * @param dt the DataType to check. + * @param stack the stack of references to check. + * @param project the project to use to dereference the references. + * @throws BuildException on error. + * @since Ant 1.7 + */ + public static void invokeCircularReferenceCheck(DataType dt, Stack stk, + Project p) { + dt.dieOnCircularReference(stk, p); + } + + /** + * Performs the check for circular references and returns the + * referenced object. + * @return the dereferenced object. + * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). + * @since Ant 1.7 + */ + protected Object getCheckedRef() { + return getCheckedRef(getProject()); + } + + /** + * Performs the check for circular references and returns the + * referenced object. + * @param project the Ant Project instance against which to resolve references. + * @return the dereferenced object. + * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). + * @since Ant 1.7 + */ + protected Object getCheckedRef(Project p) { + return getCheckedRef(getClass(), getDataTypeName(), p); + } + /** * Performs the check for circular references and returns the * referenced object. - * @param requiredClass the class that this reference should be a subclass of - * @param dataTypeName the name of the datatype that the reference should be (error message - * use only) - * @return the derefenced object + * @param requiredClass the class that this reference should be a subclass of. + * @param dataTypeName the name of the datatype that the reference should be + * (error message use only). + * @return the dereferenced object. * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). */ protected Object getCheckedRef(final Class requiredClass, final String dataTypeName) { - if (!checked) { - Stack stk = new Stack(); - stk.push(this); - dieOnCircularReference(stk, getProject()); - } + return getCheckedRef(requiredClass, dataTypeName, getProject()); + } - Object o = ref.getReferencedObject(getProject()); + /** + * Performs the check for circular references and returns the + * referenced object. This version allows the fallback Project instance to be specified. + * @param requiredClass the class that this reference should be a subclass of. + * @param dataTypeName the name of the datatype that the reference should be + * (error message use only). + * @param project the fallback Project instance for dereferencing. + * @return the dereferenced object. + * @throws BuildException if the reference is invalid (circular ref, wrong class, etc), + * or if project is null. + * @since Ant 1.7 + */ + protected Object getCheckedRef(final Class requiredClass, + final String dataTypeName, final Project project) { + dieOnCircularReference(project); + if (project == null) { + throw new BuildException("No Project specified"); + } + Object o = ref.getReferencedObject(project); if (!(requiredClass.isAssignableFrom(o.getClass()))) { log("Class " + o.getClass() + " is not a subclass of " + requiredClass, Project.MSG_VERBOSE); String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName; throw new BuildException(msg); - } else { - return o; } + return o; } /** @@ -223,7 +313,7 @@ public abstract class DataType extends ProjectComponent { * get the reference set on this object * @return the reference or null */ - protected Reference getRefid() { + public Reference getRefid() { return ref; } diff --git a/src/main/org/apache/tools/ant/util/IdentityStack.java b/src/main/org/apache/tools/ant/util/IdentityStack.java new file mode 100755 index 000000000..f0a024b61 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/IdentityStack.java @@ -0,0 +1,99 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.util; + +import java.util.Stack; + +/** + * Identity Stack. + * @since Ant 1.7 + */ +public class IdentityStack extends Stack { + + /** + * Get an IdentityStack containing the contents of the specified Stack. + * @param s the Stack to copy. + * @return an IdentityStack instance. + */ + public static IdentityStack getInstance(Stack s) { + if (s instanceof IdentityStack) { + return (IdentityStack) s; + } + IdentityStack result = new IdentityStack(); + result.addAll(s); + return result; + } + + /** + * Default constructor. + */ + public IdentityStack() { + } + + /** + * Construct a new IdentityStack with the specified Object + * as the bottom element. + * @param o the bottom element. + */ + public IdentityStack(Object o) { + super(); + push(o); + } + + /** + * Override methods that use .equals() comparisons on elements. + * @param o the Object to search for. + * @see Vector#contains(Object) + */ + public synchronized boolean contains(Object o) { + return indexOf(o) >= 0; + } + + /** + * Override methods that use .equals() comparisons on elements. + * @param o the Object to search for. + * @param pos the position from which to search. + * @see Vector#indexOf(Object, int) + */ + public synchronized int indexOf(Object o, int pos) { + for (int i = pos; i < size(); i++) + { + if (get(i) == o) { + return i; + } + } + return -1; + } + + /** + * Override methods that use .equals() comparisons on elements. + * @param o the Object to search for. + * @param pos the position from which to search (backward). + * @see Vector#indexOf(Object, int) + */ + public synchronized int lastIndexOf(Object o, int pos) { + for (int i = pos; i >= 0; i--) + { + if (get(i) == o) { + return i; + } + } + return -1; + } + +} +