/*
* Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.antcontrib.logic;
import java.io.File;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
/***
* Task definition for the foreach task. The foreach task iterates
* over a list, a list of filesets, or both.
*
* <pre>
*
* Usage:
*
* Task declaration in the project:
* <code>
* <taskdef name="latesttimestamp" classname="net.sf.antcontrib.logic.TimestampSelector" />
* </code>
*
* Call Syntax:
* <code>
* <timestampselector
* [property="prop" | outputsetref="id"]
* [count="num"]
* [age="eldest|youngest"]
* [pathSep=","]
* [pathref="ref"] >
* <path>
* ...
* </path>
* </latesttimestamp>
* </code>
*
* Attributes:
* outputsetref --> The reference of the output Path set which will contain the
* files with the latest timestamps.
* property --> The name of the property to set with file having the latest
* timestamp. If you specify the "count" attribute, you will get
* the lastest N files. These will be the absolute pathnames
* count --> How many of the latest files do you wish to find
* pathSep --> What to use as the path separator when using the "property"
* attribute, in conjunction with the "count" attribute
* pathref --> The reference of the path which is the input set of files.
*
* </pre>
* @author <a href="mailto:mattinger@yahoo.com">Matthew Inger</a>
*/
public class TimestampSelector extends Task
{
private static final String AGE_ELDEST = "eldest";
private static final String AGE_YOUNGEST = "youngest";
private String property;
private Path path;
private String outputSetId;
private int count = 1;
private char pathSep = ',';
private String age = AGE_YOUNGEST;
/***
* Default Constructor
*/
public TimestampSelector()
{
super();
}
public void doFileSetExecute(String paths[])
throws BuildException
{
}
// Sorts entire array
public void sort(Vector array)
{
sort(array, 0, array.size() - 1);
}
// Sorts partial array
protected void sort(Vector array, int start, int end)
{
int p;
if (end > start)
{
p = partition(array, start, end);
sort(array, start, p-1);
sort(array, p+1, end);
}
}
protected int compare(File a, File b)
{
if (age.equalsIgnoreCase(AGE_ELDEST))
return new Long(a.lastModified()).compareTo(new Long(b.lastModified()));
else
return new Long(b.lastModified()).compareTo(new Long(a.lastModified()));
}
protected int partition(Vector array, int start, int end)
{
int left, right;
File partitionElement;
partitionElement = (File)array.elementAt(end);
left = start - 1;
right = end;
for (;;)
{
while (compare(partitionElement, (File)array.elementAt(++left)) == 1)
{
if (left == end) break;
}
while (compare(partitionElement, (File)array.elementAt(--right)) == -1)
{
if (right == start) break;
}
if (left >= right) break;
swap(array, left, right);
}
swap(array, left, end);
return left;
}
protected void swap(Vector array, int i, int j)
{
Object temp;
temp = array.elementAt(i);
array.setElementAt(array.elementAt(j), i);
array.setElementAt(temp, j);
}
public void execute()
throws BuildException
{
if (property == null && outputSetId == null)
throw new BuildException("Property or OutputSetId must be specified.");
if (path == null)
throw new BuildException("A path element or pathref attribute must be specified.");
// Figure out the list of existing file elements
// from the designated path
String s[] = path.list();
Vector v = new Vector();
for (int i=0;i<s.length;i++)
{
File f = new File(s[i]);
if (f.exists())
v.addElement(f);
}
// Sort the vector, need to make java 1.1 compliant
sort(v);
// Pull off the first N items
Vector v2 = new Vector();
int sz = v.size();
for (int i=0;i<sz && i<count;i++)
v2.add(v.elementAt(i));
// Build the resulting Path object
Path path = new Path(getProject());
sz = v2.size();
for (int i=0;i<sz;i++)
{
File f = (File)(v.elementAt(i));
Path p = new Path(getProject(), f.getAbsolutePath());
path.addExisting(p);
}
if (outputSetId != null)
{
// Add the reference to the project
getProject().addReference(outputSetId, path);
}
else
{
// Concat the paths, and put them in a property
// which is separated list of the files, using the
// "pathSep" attribute as the separator
String paths[] = path.list();
StringBuffer sb = new StringBuffer();
for (int i=0;i<paths.length;i++)
{
if (i != 0) sb.append(pathSep);
sb.append(paths[i]);
}
if (paths.length != 0)
getProject().setProperty(property, sb.toString());
}
}
public void setProperty(String property)
{
if (outputSetId != null)
throw new BuildException("Cannot set both Property and OutputSetId.");
this.property = property;
}
public void setCount(int count)
{
this.count = count;
}
public void setAge(String age)
{
if (age.equalsIgnoreCase(AGE_ELDEST) ||
age.equalsIgnoreCase(AGE_YOUNGEST))
this.age = age;
else
throw new BuildException("Invalid age: " + age);
}
public void setPathSep(char pathSep)
{
this.pathSep = pathSep;
}
public void setOutputSetId(String outputSetId)
{
if (property != null)
throw new BuildException("Cannot set both Property and OutputSetId.");
this.outputSetId = outputSetId;
}
public void setPathRef(Reference ref)
throws BuildException
{
if (path == null)
{
path = new Path(getProject());
path.setRefid(ref);
}
else
{
throw new BuildException("Path element already specified.");
}
}
public Path createPath()
throws BuildException
{
if (path == null)
path = new Path(getProject());
else
throw new BuildException("Path element already specified.");
return path;
}
}