bodewig 00/09/05 07:15:52
Modified: docs index.html
src/main/org/apache/tools/ant/types Path.java Reference.java
src/testcases/org/apache/tools/ant/types PathTest.java
Log:
Add a nested <path> element to <path>. This means one path can include
another path (by reference as well as literally, although I doubt the
latter is of any use). Circular references should be caught.
Suggested by: Barrie Treloar <[EMAIL PROTECTED]>
Revision Changes Path
1.90 +17 -0 jakarta-ant/docs/index.html
Index: index.html
===================================================================
RCS file: /home/cvs/jakarta-ant/docs/index.html,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -r1.89 -r1.90
--- index.html 2000/09/05 08:53:29 1.89
+++ index.html 2000/09/05 14:15:37 1.90
@@ -465,6 +465,23 @@
same level as <em>target</em>s and reference them via their
<em>id</em> attribute - see <a href="#references">References</a> for an
example.</p>
+<p>A PATH like structure can include a reference to another PATH like
+structure via a nested <code><path></code> elements.</p>
+<pre>
+ <path id="base.path">
+ <pathelement path="${classpath}" />
+ <fileset dir="lib">
+ <include name="**/*.jar" />
+ </fileset;>
+ <pathelement location="classes" />
+ </path>
+
+ <path id="tests.path">
+ <path refid="base.path" />
+ <pathelement location="testclasses" />
+ </path>
+</pre>
+
<h3><a name="arg">Command line arguments</a></h3>
<p>Several tasks take arguments that shall be passed to another
1.7 +104 -11 jakarta-ant/src/main/org/apache/tools/ant/types/Path.java
Index: Path.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/types/Path.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- Path.java 2000/08/09 14:09:37 1.6
+++ Path.java 2000/09/05 14:15:43 1.7
@@ -60,8 +60,10 @@
import org.apache.tools.ant.PathTokenizer;
import java.io.File;
-import java.util.Vector;
+import java.util.Enumeration;
import java.util.StringTokenizer;
+import java.util.Stack;
+import java.util.Vector;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
@@ -97,6 +99,11 @@
private Vector elements;
private Project project;
+ private boolean isReference = false;
+ /**
+ * Are we sure we don't hold circular references?
+ */
+ private boolean checked = true;
public static Path systemClasspath =
new Path(null, System.getProperty("java.class.path"));
@@ -140,7 +147,10 @@
* @param location the location of the element to add (must not be
* <code>null</code> nor empty.
*/
- public void setLocation(File location) {
+ public void setLocation(File location) throws BuildException {
+ if (isReference) {
+ throw tooManyAttributes();
+ }
createPathElement().setLocation(location);
}
@@ -149,15 +159,35 @@
* Parses a path definition and creates single PathElements.
* @param path the path definition.
*/
- public void setPath(String path) {
+ public void setPath(String path) throws BuildException {
+ if (isReference) {
+ throw tooManyAttributes();
+ }
createPathElement().setPath(path);
}
+ /**
+ * Makes this instance in effect a reference too another Path instance.
+ *
+ * <p>You must not set another attribute or nest elements inside
+ * this element if you make it a reference.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ isReference = true;
+ if (!elements.isEmpty()) {
+ throw tooManyAttributes();
+ }
+ elements.addElement(r);
+ checked = false;
+ }
/**
- * Created the nested <pathelement> element.
+ * Creates the nested <pathelement> element.
*/
- public PathElement createPathElement() {
+ public PathElement createPathElement() throws BuildException {
+ if (isReference) {
+ throw noChildrenAllowed();
+ }
PathElement pe = new PathElement();
elements.addElement(pe);
return pe;
@@ -166,18 +196,37 @@
/**
* Adds a nested <fileset> element.
*/
- public void addFileset(FileSet fs) {
+ public void addFileset(FileSet fs) throws BuildException {
+ if (isReference) {
+ throw noChildrenAllowed();
+ }
elements.addElement(fs);
}
/**
* Adds a nested <filesetref> element.
*/
- public void addFilesetRef(Reference r) {
+ public void addFilesetRef(Reference r) throws BuildException {
+ if (isReference) {
+ throw noChildrenAllowed();
+ }
elements.addElement(r);
}
/**
+ * Creates a nested <path> element.
+ */
+ public Path createPath() throws BuildException {
+ if (isReference) {
+ throw noChildrenAllowed();
+ }
+ Path p = new Path(project);
+ elements.add(p);
+ checked = false;
+ return p;
+ }
+
+ /**
* Append the contents of the other Path instance to this.
*/
public void append(Path other) {
@@ -214,19 +263,26 @@
}
/**
- * Returns all path elements defined by this and netsed path objects.
+ * Returns all path elements defined by this and nested path objects.
* @return list of path elements.
*/
public String[] list() {
+ if (!checked) {
+ // make sure we don't have a circular reference here
+ Stack stk = new Stack();
+ stk.push(this);
+ bailOnCircularReference(stk);
+ }
+
Vector result = new Vector(2*elements.size());
for (int i=0; i<elements.size(); i++) {
Object o = elements.elementAt(i);
if (o instanceof Reference) {
Reference r = (Reference) o;
o = r.getReferencedObject(project);
- // we only support references to filesets right now
- if (o == null || !(o instanceof FileSet)) {
- String msg = r.getRefId()+" doesn\'t denote a fileset";
+ // we only support references to filesets and paths right now
+ if (!(o instanceof FileSet) && !(o instanceof Path)) {
+ String msg = r.getRefId()+" doesn\'t denote a fileset or
path";
throw new BuildException(msg);
}
}
@@ -242,6 +298,11 @@
for (int j=0; j<parts.length; j++) {
addUnlessPresent(result, parts[j]);
}
+ } else if (o instanceof Path) {
+ String[] parts = ((Path) o).list();
+ for (int j=0; j<parts.length; j++) {
+ addUnlessPresent(result, parts[j]);
+ }
} else if (o instanceof FileSet) {
FileSet fs = (FileSet) o;
DirectoryScanner ds = fs.getDirectoryScanner(project);
@@ -338,6 +399,27 @@
return p;
}
+ protected void bailOnCircularReference(Stack stk) throws BuildException {
+ Enumeration enum = elements.elements();
+ while (enum.hasMoreElements()) {
+ Object o = enum.nextElement();
+ if (o instanceof Reference) {
+ o = ((Reference) o).getReferencedObject(project);
+ }
+
+ if (o instanceof Path) {
+ if (stk.contains(o)) {
+ throw circularReference();
+ } else {
+ stk.push(o);
+ ((Path) o).bailOnCircularReference(stk);
+ stk.pop();
+ }
+ }
+ }
+ checked = true;
+ }
+
private static String resolveFile(Project project, String relativeName) {
if (project != null) {
return project.resolveFile(relativeName).getAbsolutePath();
@@ -351,4 +433,15 @@
}
}
+ private BuildException tooManyAttributes() {
+ return new BuildException("You must not specify more than one
attribute when using refid");
+ }
+
+ private BuildException noChildrenAllowed() {
+ return new BuildException("You must not specify nested elements when
using refid");
+ }
+
+ private BuildException circularReference() {
+ return new BuildException("This path contains a circular
reference.");
+ }
}
1.3 +1 -1
jakarta-ant/src/main/org/apache/tools/ant/types/Reference.java
Index: Reference.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/types/Reference.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- Reference.java 2000/08/30 14:29:39 1.2
+++ Reference.java 2000/09/05 14:15:45 1.3
@@ -90,7 +90,7 @@
Object o = project.getReferences().get(refid);
if (o == null) {
- throw new BuildException("Refernce "+refid+" not found.");
+ throw new BuildException("Reference "+refid+" not found.");
}
return o;
}
1.4 +127 -0
jakarta-ant/src/testcases/org/apache/tools/ant/types/PathTest.java
Index: PathTest.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/testcases/org/apache/tools/ant/types/PathTest.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- PathTest.java 2000/08/10 08:42:30 1.3
+++ PathTest.java 2000/09/05 14:15:50 1.4
@@ -171,6 +171,9 @@
p.append(new Path(project, "\\f"));
l = p.list();
assertEquals("6 after append", 6, l.length);
+ p.createPath().setLocation(new File("/g"));
+ l = p.list();
+ assertEquals("7 after append", 7, l.length);
}
public void testEmpyPath() {
@@ -183,6 +186,9 @@
p.append(new Path(project));
l = p.list();
assertEquals("0 after append", 0, l.length);
+ p.createPath();
+ l = p.list();
+ assertEquals("0 after append", 0, l.length);
}
public void testUnique() {
@@ -198,6 +204,127 @@
p.append(new Path(project, "/a;\\a:\\a"));
l = p.list();
assertEquals("1 after append", 1, l.length);
+ p.createPath().setPath("\\a:/a");
+ l = p.list();
+ assertEquals("1 after append", 1, l.length);
+ }
+
+ public void testEmptyElementIfIsReference() {
+ Path p = new Path(project, "/a:/a");
+ try {
+ p.setRefid(new Reference("dummyref"));
+ fail("Can add reference to Path with elements from constructor");
+ } catch (BuildException be) {
+ assertEquals("You must not specify more than one attribute when
using refid",
+ be.getMessage());
+ }
+
+ p = new Path(project);
+ p.setLocation(new File("/a"));
+ try {
+ p.setRefid(new Reference("dummyref"));
+ fail("Can add reference to Path with elements from setLocation");
+ } catch (BuildException be) {
+ assertEquals("You must not specify more than one attribute when
using refid",
+ be.getMessage());
+ }
+
+ p = new Path(project);
+ p.setRefid(new Reference("dummyref"));
+ try {
+ p.setLocation(new File("/a"));
+ fail("Can set location in Path that is a reference.");
+ } catch (BuildException be) {
+ assertEquals("You must not specify more than one attribute when
using refid",
+ be.getMessage());
+ }
+
+ try {
+ p.setPath("/a;\\a");
+ fail("Can set path in Path that is a reference.");
+ } catch (BuildException be) {
+ assertEquals("You must not specify more than one attribute when
using refid",
+ be.getMessage());
+ }
+
+ try {
+ p.createPath();
+ fail("Can create nested Path in Path that is a reference.");
+ } catch (BuildException be) {
+ assertEquals("You must not specify nested elements when using
refid",
+ be.getMessage());
+ }
+
+ try {
+ p.createPathElement();
+ fail("Can create nested PathElement in Path that is a
reference.");
+ } catch (BuildException be) {
+ assertEquals("You must not specify nested elements when using
refid",
+ be.getMessage());
+ }
+
+ try {
+ p.addFileset(new FileSet());
+ fail("Can add nested FileSet in Path that is a reference.");
+ } catch (BuildException be) {
+ assertEquals("You must not specify nested elements when using
refid",
+ be.getMessage());
+ }
+
+ try {
+ p.addFilesetRef(new Reference("dummy2"));
+ fail("Can add nested FileSetRef in Path that is a reference.");
+ } catch (BuildException be) {
+ assertEquals("You must not specify nested elements when using
refid",
+ be.getMessage());
+ }
+
+ }
+
+ public void testCircularReferenceCheck() {
+ Path p = new Path(project);
+ project.addReference("dummy", p);
+ p.setRefid(new Reference("dummy"));
+ try {
+ p.list();
+ fail("Can make Path a Reference to itself.");
+ } catch (BuildException be) {
+ assertEquals("This path contains a circular reference.",
+ be.getMessage());
+ }
+
+ // dummy1 --> dummy2 --> dummy3 --> dummy1
+ Path p1 = new Path(project);
+ project.addReference("dummy1", p1);
+ Path p2 = p1.createPath();
+ project.addReference("dummy2", p2);
+ Path p3 = p2.createPath();
+ project.addReference("dummy3", p3);
+ p3.setRefid(new Reference("dummy1"));
+ try {
+ p1.list();
+ fail("Can make circular reference.");
+ } catch (BuildException be) {
+ assertEquals("This path contains a circular reference.",
+ be.getMessage());
+ }
+
+ // dummy1 --> dummy2 --> dummy3 (with Path "/a")
+ p1 = new Path(project);
+ project.addReference("dummy1", p1);
+ p2 = p1.createPath();
+ project.addReference("dummy2", p2);
+ p3 = p2.createPath();
+ project.addReference("dummy3", p3);
+ p3.setLocation(new File("/a"));
+ String[] l = p1.list();
+ assertEquals("One element burried deep inside a nested path
structure",
+ 1, l.length);
+ if (isUnixStyle) {
+ assertEquals("/a", l[0]);
+ } else {
+ assertEquals("\\a", l[0]);
+ }
}
}