Browse Source

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 <Barrie.Treloar@camtech.com.au>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267962 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 25 years ago
parent
commit
e8d2007cff
4 changed files with 249 additions and 12 deletions
  1. +17
    -0
      docs/index.html
  2. +104
    -11
      src/main/org/apache/tools/ant/types/Path.java
  3. +1
    -1
      src/main/org/apache/tools/ant/types/Reference.java
  4. +127
    -0
      src/testcases/org/apache/tools/ant/types/PathTest.java

+ 17
- 0
docs/index.html View File

@@ -465,6 +465,23 @@ you can define them with a <code>&lt;path&gt;</code> element at the
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>&lt;path&gt;</code> elements.</p>
<pre>
&lt;path id=&quot;base.path&quot;&gt;
&lt;pathelement path=&quot;${classpath}&quot; /&gt;
&lt;fileset dir=&quot;lib&quot;&gt;
&lt;include name=&quot;**/*.jar&quot; /&gt;
&lt;/fileset;&gt;
&lt;pathelement location=&quot;classes&quot; /&gt;
&lt;/path&gt;

&lt;path id=&quot;tests.path&quot;&gt;
&lt;path refid=&quot;base.path&quot; /&gt;
&lt;pathelement location=&quot;testclasses&quot; /&gt;
&lt;/path&gt;
</pre>

<h3><a name="arg">Command line arguments</a></h3>

<p>Several tasks take arguments that shall be passed to another


+ 104
- 11
src/main/org/apache/tools/ant/types/Path.java View File

@@ -60,8 +60,10 @@ import org.apache.tools.ant.Project;
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 @@ public class Path implements Cloneable {

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 @@ public class Path implements Cloneable {
* @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 @@ public class Path implements Cloneable {
* 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,17 +196,36 @@ public class Path implements Cloneable {
/**
* 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.
*/
@@ -214,19 +263,26 @@ public class Path implements Cloneable {
}

/**
* 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 @@ public class Path implements Cloneable {
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 @@ public class Path implements Cloneable {
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 @@ public class Path implements Cloneable {
}
}

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
- 1
src/main/org/apache/tools/ant/types/Reference.java View File

@@ -90,7 +90,7 @@ public class Reference {
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;
}


+ 127
- 0
src/testcases/org/apache/tools/ant/types/PathTest.java View File

@@ -171,6 +171,9 @@ public class PathTest extends TestCase {
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 @@ public class PathTest extends TestCase {
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 @@ public class PathTest extends TestCase {
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]);
}
}

}

Loading…
Cancel
Save