@@ -47,6 +55,31 @@ listed as #IMPLIED
.
<antstructure output="project.dtd"/>
+
+Emitting your own structure instead of a DTD
+
+First you need to implement the interface
+
+
+package org.example;
+import org.apache.tools.ant.taskdefs.AntStructure;
+public class MyPrinter implements AntStructure.StructurePrinter {
+ ...
+}
+
+
+and then use it via typedef
+
+
+ <typedef name="myprinter" classname="org.example.MyPrinter"/>
+ <antstructure output="project.my">
+ <myprinter/>
+ <antstructure>
+
+
+Your own StructurePrinter can accept attributes and nested elements
+just like any other Ant type or task.
+
Copyright © 2000-2002,2004-2005 The Apache Software Foundation. All rights
Reserved.
diff --git a/src/etc/testcases/taskdefs/antstructure.xml b/src/etc/testcases/taskdefs/antstructure.xml
index 8d49ad923..74888b61e 100644
--- a/src/etc/testcases/taskdefs/antstructure.xml
+++ b/src/etc/testcases/taskdefs/antstructure.xml
@@ -6,4 +6,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/org/apache/tools/ant/taskdefs/AntStructure.java b/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
index 41c07a4c7..6a48650d9 100644
--- a/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
+++ b/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
@@ -29,6 +29,7 @@ import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
import org.apache.tools.ant.types.EnumeratedAttribute;
@@ -44,15 +45,10 @@ import org.apache.tools.ant.types.Reference;
*/
public class AntStructure extends Task {
- private final String lSep = System.getProperty("line.separator");
-
- private static final String BOOLEAN = "%boolean;";
- private static final String TASKS = "%tasks;";
- private static final String TYPES = "%types;";
-
- private Hashtable visited = new Hashtable();
+ private static final String lSep = System.getProperty("line.separator");
private File output;
+ private StructurePrinter printer = new DTDPrinter();
/**
* The output file.
@@ -62,6 +58,14 @@ public class AntStructure extends Task {
this.output = output;
}
+ /**
+ * The StructurePrinter to use.
+ * @since Ant 1.7
+ */
+ public void add(StructurePrinter p) {
+ printer = p;
+ }
+
/**
* Build the antstructure DTD.
*
@@ -87,25 +91,28 @@ public class AntStructure extends Task {
out = new PrintWriter(new FileWriter(output));
}
- printHead(out, getProject().getTaskDefinitions().keys(),
- getProject().getDataTypeDefinitions().keys());
+ printer.printHead(out, getProject(),
+ getProject().getTaskDefinitions(),
+ getProject().getDataTypeDefinitions());
- printTargetDecl(out);
+ printer.printTargetDecl(out);
Enumeration dataTypes = getProject().getDataTypeDefinitions().keys();
while (dataTypes.hasMoreElements()) {
String typeName = (String) dataTypes.nextElement();
- printElementDecl(out, typeName,
+ printer.printElementDecl(out, getProject(), typeName,
(Class) getProject().getDataTypeDefinitions().get(typeName));
}
Enumeration tasks = getProject().getTaskDefinitions().keys();
while (tasks.hasMoreElements()) {
String tName = (String) tasks.nextElement();
- printElementDecl(out, tName,
+ printer.printElementDecl(out, getProject(), tName,
(Class) getProject().getTaskDefinitions().get(tName));
}
+ printer.printTail(out);
+
} catch (IOException ioe) {
throw new BuildException("Error writing "
+ output.getAbsolutePath(), ioe, getLocation());
@@ -113,10 +120,72 @@ public class AntStructure extends Task {
if (out != null) {
out.close();
}
- visited.clear();
}
}
+ /**
+ * Writes the actual structure information.
+ *
+ * {@link StructurePrinter#printHead printHead}, {@link
+ * StructurePrinter#printTargetDecl printTargetDecl} and {@link
+ * StructurePrinter#printTail printTail} are called exactly once,
+ * {@link StructurePrinter#printElement printElement} once for
+ * each declared task and type.
+ */
+ public static interface StructurePrinter {
+ /**
+ * Prints the header of the generated output.
+ *
+ * @param out PrintWriter to write to.
+ * @param p Project instance for the current task
+ * @param tasks map (name to implementing class)
+ * @param types map (name to implementing class)
+ * data types.
+ */
+ void printHead(PrintWriter out, Project p, Hashtable tasks,
+ Hashtable types);
+
+ /**
+ * Prints the definition for the target element.
+ * @param out PrintWriter to write to.
+ */
+ void printTargetDecl(PrintWriter out);
+
+ /**
+ * Print the definition for a given element.
+ *
+ * @param out PrintWriter to write to.
+ * @param p Project instance for the current task
+ * @param name element name.
+ * @param name class of the defined element.
+ */
+ void printElementDecl(PrintWriter out, Project p, String name,
+ Class element);
+
+ /**
+ * Prints the trailer.
+ * @param out PrintWriter to write to.
+ */
+ void printTail(PrintWriter out);
+ }
+
+ private static class DTDPrinter implements StructurePrinter {
+
+ private static final String BOOLEAN = "%boolean;";
+ private static final String TASKS = "%tasks;";
+ private static final String TYPES = "%types;";
+
+ private Hashtable visited = new Hashtable();
+
+ public void printTail(PrintWriter out) {
+ visited.clear();
+ }
+
+ public void printHead(PrintWriter out, Project p, Hashtable tasks, Hashtable types) {
+ printHead(out, tasks.keys(), types.keys());
+ }
+
+
/**
* Prints the header of the generated output.
*
@@ -169,7 +238,7 @@ public class AntStructure extends Task {
/**
* Prints the definition for the target element.
*/
- private void printTargetDecl(PrintWriter out) {
+ public void printTargetDecl(PrintWriter out) {
out.print("Otherwise they are not suitable as an enumerated attribute,
+ * for example.
+ * @param s the array of string to test
+ * @return true if all the strings in the array math XML-NMTOKEN
+ */
+ protected boolean areNmtokens(String[] s) {
+ return DTDPrinter.areNmtokens(s);
+ }
}
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java b/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java
index 80de293b0..3f14433ba 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java
@@ -17,7 +17,12 @@
package org.apache.tools.ant.taskdefs;
+import java.io.PrintWriter;
+import java.util.Hashtable;
+import junit.framework.Assert;
import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+
/**
*/
public class AntStructureTest extends BuildFileTest {
@@ -30,7 +35,62 @@ public class AntStructureTest extends BuildFileTest {
configureProject("src/etc/testcases/taskdefs/antstructure.xml");
}
+ public void tearDown() {
+ executeTarget("tearDown");
+ }
+
public void test1() {
expectBuildException("test1", "required argument not specified");
}
+
+ public void testCustomPrinter() {
+ executeTarget("testCustomPrinter");
+ // can't access the booleans in MyPrinter here (even if they
+ // were static) since the MyPrinter instance that was used in
+ // the test has likely been loaded via a different classloader
+ // than this class. Therefore we make the printer assert its
+ // state and only check for the tail invocation.
+ assertLogContaining(MyPrinter.TAIL_CALLED);
+ }
+
+ public static class MyPrinter implements AntStructure.StructurePrinter {
+ private static final String TAIL_CALLED = "tail has been called";
+ private boolean headCalled = false;
+ private boolean targetCalled = false;
+ private boolean tailCalled = false;
+ private int elementCalled = 0;
+ private Project p;
+
+ public void printHead(PrintWriter out, Project p, Hashtable tasks,
+ Hashtable types) {
+ Assert.assertTrue(!headCalled);
+ Assert.assertTrue(!targetCalled);
+ Assert.assertTrue(!tailCalled);
+ Assert.assertEquals(0, elementCalled);
+ headCalled = true;
+ }
+ public void printTargetDecl(PrintWriter out) {
+ Assert.assertTrue(headCalled);
+ Assert.assertTrue(!targetCalled);
+ Assert.assertTrue(!tailCalled);
+ Assert.assertEquals(0, elementCalled);
+ targetCalled = true;
+ }
+ public void printElementDecl(PrintWriter out, Project p, String name,
+ Class element) {
+ Assert.assertTrue(headCalled);
+ Assert.assertTrue(targetCalled);
+ Assert.assertTrue(!tailCalled);
+ elementCalled++;
+ this.p = p;
+ }
+ public void printTail(PrintWriter out) {
+ Assert.assertTrue(headCalled);
+ Assert.assertTrue(targetCalled);
+ Assert.assertTrue(!tailCalled);
+ Assert.assertTrue(elementCalled > 0);
+ tailCalled = true;
+ p.log(TAIL_CALLED);
+ }
+ }
}