From 0ffd5612f57b4b785af3455b800fa189718299de Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 28 Sep 2005 18:34:04 +0000 Subject: [PATCH] lay grounds for Schema or Relax NG description generators as plugins in antstructure git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@292244 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/CoreTasks/antstructure.html | 33 +++++ src/etc/testcases/taskdefs/antstructure.xml | 16 +++ .../tools/ant/taskdefs/AntStructure.java | 130 +++++++++++++++--- .../tools/ant/taskdefs/AntStructureTest.java | 60 ++++++++ 4 files changed, 219 insertions(+), 20 deletions(-) diff --git a/docs/manual/CoreTasks/antstructure.html b/docs/manual/CoreTasks/antstructure.html index b1ca29302..19226598c 100644 --- a/docs/manual/CoreTasks/antstructure.html +++ b/docs/manual/CoreTasks/antstructure.html @@ -30,6 +30,14 @@ href="http://www.sdv.fr/pages/casa/html/ant-dtd.en.html" target="_top">here for a way to get around this problem.

This task doesn't know about required attributes, all will be listed as #IMPLIED.

+ +

Since Ant 1.7 custom structure printers can be used +instead of the one that emits a DTD. In order to plug in your own +structure, you have to implement the interface +org.apache.tools.ant.taskdefs.AntStructure.StructurePrinter +and <typedef> your class and use the new type as a nested +element of this task - see the example below. +

Parameters

@@ -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); + } + } }