Browse Source

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
master
Stefan Bodewig 19 years ago
parent
commit
0ffd5612f5
4 changed files with 219 additions and 20 deletions
  1. +33
    -0
      docs/manual/CoreTasks/antstructure.html
  2. +16
    -0
      src/etc/testcases/taskdefs/antstructure.xml
  3. +110
    -20
      src/main/org/apache/tools/ant/taskdefs/AntStructure.java
  4. +60
    -0
      src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java

+ 33
- 0
docs/manual/CoreTasks/antstructure.html View File

@@ -30,6 +30,14 @@ href="http://www.sdv.fr/pages/casa/html/ant-dtd.en.html"
target="_top">here</a> for a way to get around this problem.</p>
<p>This task doesn't know about required attributes, all will be
listed as <code>#IMPLIED</code>.</p>

<p><em>Since Ant 1.7</em> 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
<code>org.apache.tools.ant.taskdefs.AntStructure.StructurePrinter</code>
and <code>&lt;typedef&gt; your class and use the new type as a nested
element of this task - see the example below.</code>

<h3>Parameters</h3>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
@@ -47,6 +55,31 @@ listed as <code>#IMPLIED</code>.</p>
<blockquote><pre>
&lt;antstructure output=&quot;project.dtd&quot;/&gt;
</pre></blockquote>

<p><b>Emitting your own structure instead of a DTD</b></p>

<p>First you need to implement the interface</p>

<pre>
package org.example;
import org.apache.tools.ant.taskdefs.AntStructure;
public class MyPrinter implements AntStructure.StructurePrinter {
...
}
</pre>

<p>and then use it via typedef</p>

<pre>
&lt;typedef name="myprinter" classname="org.example.MyPrinter"/&gt;
&lt;antstructure output="project.my"&gt;
&lt;myprinter/&gt;
&lt;antstructure&gt;
</pre>

<p>Your own StructurePrinter can accept attributes and nested elements
just like any other Ant type or task.</p>

<hr><p align="center">Copyright &copy; 2000-2002,2004-2005 The Apache Software Foundation. All rights
Reserved.</p>



+ 16
- 0
src/etc/testcases/taskdefs/antstructure.xml View File

@@ -6,4 +6,20 @@
<antstructure/>
</target>

<target name="testCustomPrinter">
<typedef name="myprinter"
classname="org.apache.tools.ant.taskdefs.AntStructureTest$MyPrinter">
<classpath>
<pathelement path="${tests-classpath.value}"/>
</classpath>
</typedef>
<antstructure output="foo.dtd">
<myprinter/>
</antstructure>
</target>

<target name="tearDown">
<delete file="foo.dtd" quiet="true"/>
</target>

</project>

+ 110
- 20
src/main/org/apache/tools/ant/taskdefs/AntStructure.java View File

@@ -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.
*
* <p>{@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.</p>
*/
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("<!ELEMENT target (");
out.print(TASKS);
out.print(" | ");
@@ -190,8 +259,8 @@ public class AntStructure extends Task {
/**
* Print the definition for a given element.
*/
private void printElementDecl(PrintWriter out, String name, Class element)
throws BuildException {
public void printElementDecl(PrintWriter out, Project p,
String name, Class element) {

if (visited.containsKey(name)) {
return;
@@ -200,7 +269,7 @@ public class AntStructure extends Task {

IntrospectionHelper ih = null;
try {
ih = IntrospectionHelper.getHelper(getProject(), element);
ih = IntrospectionHelper.getHelper(p, element);
} catch (Throwable t) {
/*
* XXX - failed to load the class properly.
@@ -312,7 +381,7 @@ public class AntStructure extends Task {
if (!"#PCDATA".equals(nestedName)
&& !TASKS.equals(nestedName)
&& !TYPES.equals(nestedName)) {
printElementDecl(out, nestedName, ih.getElementType(nestedName));
printElementDecl(out, p, nestedName, ih.getElementType(nestedName));
}
}
}
@@ -322,7 +391,7 @@ public class AntStructure extends Task {
* @param s the string to test
* @return true if the string matches the XML-NMTOKEN
*/
protected boolean isNmtoken(String s) {
public static final boolean isNmtoken(String s) {
final int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
@@ -343,7 +412,7 @@ public class AntStructure extends Task {
* @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) {
public static final boolean areNmtokens(String[] s) {
for (int i = 0; i < s.length; i++) {
if (!isNmtoken(s[i])) {
return false;
@@ -351,5 +420,26 @@ public class AntStructure extends Task {
}
return true;
}
}

/**
* Does this String match the XML-NMTOKEN production?
* @param s the string to test
* @return true if the string matches the XML-NMTOKEN
*/
protected boolean isNmtoken(String s) {
return DTDPrinter.isNmtoken(s);
}

/**
* Do the Strings all match the XML-NMTOKEN production?
*
* <p>Otherwise they are not suitable as an enumerated attribute,
* for example.</p>
* @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);
}
}

+ 60
- 0
src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java View File

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

Loading…
Cancel
Save