summaryrefslogtreecommitdiffstats
path: root/src/main/java/net/sf/antcontrib/logic/ForTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/net/sf/antcontrib/logic/ForTask.java')
-rw-r--r--src/main/java/net/sf/antcontrib/logic/ForTask.java464
1 files changed, 464 insertions, 0 deletions
diff --git a/src/main/java/net/sf/antcontrib/logic/ForTask.java b/src/main/java/net/sf/antcontrib/logic/ForTask.java
new file mode 100644
index 0000000..4290c19
--- /dev/null
+++ b/src/main/java/net/sf/antcontrib/logic/ForTask.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2003-2005 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.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.MacroDef;
+import org.apache.tools.ant.taskdefs.MacroInstance;
+import org.apache.tools.ant.taskdefs.Parallel;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+
+/***
+ * Task definition for the for task. This is based on
+ * the foreach task but takes a sequential element
+ * instead of a target and only works for ant >= 1.6Beta3
+ * @author Peter Reilly
+ */
+public class ForTask extends Task {
+
+ private String list;
+ private String param;
+ private String delimiter = ",";
+ private Path currPath;
+ private boolean trim;
+ private boolean keepgoing = false;
+ private MacroDef macroDef;
+ private List hasIterators = new ArrayList();
+ private boolean parallel = false;
+ private Integer threadCount;
+ private Parallel parallelTasks;
+ private int begin = 0;
+ private Integer end = null;
+ private int step = 1;
+
+ private int taskCount = 0;
+ private int errorCount = 0;
+
+ /**
+ * Creates a new <code>For</code> instance.
+ */
+ public ForTask() {
+ }
+
+ /**
+ * Attribute whether to execute the loop in parallel or in sequence.
+ * @param parallel if true execute the tasks in parallel. Default is false.
+ */
+ public void setParallel(boolean parallel) {
+ this.parallel = parallel;
+ }
+
+ /***
+ * Set the maximum amount of threads we're going to allow
+ * to execute in parallel
+ * @param threadCount the number of threads to use
+ */
+ public void setThreadCount(int threadCount) {
+ if (threadCount < 1) {
+ throw new BuildException("Illegal value for threadCount " + threadCount
+ + " it should be > 0");
+ }
+ this.threadCount = new Integer(threadCount);
+ }
+
+ /**
+ * Set the trim attribute.
+ *
+ * @param trim if true, trim the value for each iterator.
+ */
+ public void setTrim(boolean trim) {
+ this.trim = trim;
+ }
+
+ /**
+ * Set the keepgoing attribute, indicating whether we
+ * should stop on errors or continue heedlessly onward.
+ *
+ * @param keepgoing a boolean, if <code>true</code> then we act in
+ * the keepgoing manner described.
+ */
+ public void setKeepgoing(boolean keepgoing) {
+ this.keepgoing = keepgoing;
+ }
+
+ /**
+ * Set the list attribute.
+ *
+ * @param list a list of delimiter separated tokens.
+ */
+ public void setList(String list) {
+ this.list = list;
+ }
+
+ /**
+ * Set the delimiter attribute.
+ *
+ * @param delimiter the delimiter used to separate the tokens in
+ * the list attribute. The default is ",".
+ */
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ /**
+ * Set the param attribute.
+ * This is the name of the macrodef attribute that
+ * gets set for each iterator of the sequential element.
+ *
+ * @param param the name of the macrodef attribute.
+ */
+ public void setParam(String param) {
+ this.param = param;
+ }
+
+ private Path getOrCreatePath() {
+ if (currPath == null) {
+ currPath = new Path(getProject());
+ }
+ return currPath;
+ }
+
+ /**
+ * This is a path that can be used instread of the list
+ * attribute to interate over. If this is set, each
+ * path element in the path is used for an interator of the
+ * sequential element.
+ *
+ * @param path the path to be set by the ant script.
+ */
+ public void addConfigured(Path path) {
+ getOrCreatePath().append(path);
+ }
+
+ /**
+ * This is a path that can be used instread of the list
+ * attribute to interate over. If this is set, each
+ * path element in the path is used for an interator of the
+ * sequential element.
+ *
+ * @param path the path to be set by the ant script.
+ */
+ public void addConfiguredPath(Path path) {
+ addConfigured(path);
+ }
+
+ /**
+ * @return a MacroDef#NestedSequential object to be configured
+ */
+ public Object createSequential() {
+ macroDef = new MacroDef();
+ macroDef.setProject(getProject());
+ return macroDef.createSequential();
+ }
+
+ /**
+ * Set begin attribute.
+ * @param begin the value to use.
+ */
+ public void setBegin(int begin) {
+ this.begin = begin;
+ }
+
+ /**
+ * Set end attribute.
+ * @param end the value to use.
+ */
+ public void setEnd(Integer end) {
+ this.end = end;
+ }
+
+ /**
+ * Set step attribute.
+ *
+ */
+ public void setStep(int step) {
+ this.step = step;
+ }
+
+
+ /**
+ * Run the for task.
+ * This checks the attributes and nested elements, and
+ * if there are ok, it calls doTheTasks()
+ * which constructes a macrodef task and a
+ * for each interation a macrodef instance.
+ */
+ public void execute() {
+ if (parallel) {
+ parallelTasks = (Parallel) getProject().createTask("parallel");
+ if (threadCount != null) {
+ parallelTasks.setThreadCount(threadCount.intValue());
+ }
+ }
+ if (list == null && currPath == null && hasIterators.size() == 0
+ && end == null) {
+ throw new BuildException(
+ "You must have a list or path or sequence to iterate through");
+ }
+ if (param == null) {
+ throw new BuildException(
+ "You must supply a property name to set on"
+ + " each iteration in param");
+ }
+ if (macroDef == null) {
+ throw new BuildException(
+ "You must supply an embedded sequential "
+ + "to perform");
+ }
+ if (end != null) {
+ int iEnd = end.intValue();
+ if (step == 0) {
+ throw new BuildException("step cannot be 0");
+ } else if (iEnd > begin && step < 0) {
+ throw new BuildException("end > begin, step needs to be > 0");
+ } else if (iEnd <= begin && step > 0) {
+ throw new BuildException("end <= begin, step needs to be < 0");
+ }
+ }
+ doTheTasks();
+ if (parallel) {
+ parallelTasks.perform();
+ }
+ }
+
+
+ private void doSequentialIteration(String val) {
+ MacroInstance instance = new MacroInstance();
+ instance.setProject(getProject());
+ instance.setOwningTarget(getOwningTarget());
+ instance.setMacroDef(macroDef);
+ instance.setDynamicAttribute(param.toLowerCase(),
+ val);
+ if (!parallel) {
+ instance.execute();
+ } else {
+ parallelTasks.addTask(instance);
+ }
+ }
+
+ private void doToken(String tok) {
+ try {
+ taskCount++;
+ doSequentialIteration(tok);
+ } catch (BuildException bx) {
+ if (keepgoing) {
+ log(tok + ": " + bx.getMessage(), Project.MSG_ERR);
+ errorCount++;
+ } else {
+ throw bx;
+ }
+ }
+ }
+
+ private void doTheTasks() {
+ errorCount = 0;
+ taskCount = 0;
+
+ // Create a macro attribute
+ if (macroDef.getAttributes().isEmpty()) {
+ MacroDef.Attribute attribute = new MacroDef.Attribute();
+ attribute.setName(param);
+ macroDef.addConfiguredAttribute(attribute);
+ }
+
+ // Take Care of the list attribute
+ if (list != null) {
+ StringTokenizer st = new StringTokenizer(list, delimiter);
+
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ if (trim) {
+ tok = tok.trim();
+ }
+ doToken(tok);
+ }
+ }
+
+ // Take care of the begin/end/step attributes
+ if (end != null) {
+ int iEnd = end.intValue();
+ if (step > 0) {
+ for (int i = begin; i < (iEnd + 1); i = i + step) {
+ doToken("" + i);
+ }
+ } else {
+ for (int i = begin; i > (iEnd - 1); i = i + step) {
+ doToken("" + i);
+ }
+ }
+ }
+
+ // Take Care of the path element
+ String[] pathElements = new String[0];
+ if (currPath != null) {
+ pathElements = currPath.list();
+ }
+ for (int i = 0; i < pathElements.length; i++) {
+ File nextFile = new File(pathElements[i]);
+ doToken(nextFile.getAbsolutePath());
+ }
+
+ // Take care of iterators
+ for (Iterator i = hasIterators.iterator(); i.hasNext();) {
+ Iterator it = ((HasIterator) i.next()).iterator();
+ while (it.hasNext()) {
+ doToken(it.next().toString());
+ }
+ }
+ if (keepgoing && (errorCount != 0)) {
+ throw new BuildException(
+ "Keepgoing execution: " + errorCount
+ + " of " + taskCount + " iterations failed.");
+ }
+ }
+
+ /**
+ * Add a Map, iterate over the values
+ *
+ * @param map a Map object - iterate over the values.
+ */
+ public void add(Map map) {
+ hasIterators.add(new MapIterator(map));
+ }
+
+ /**
+ * Add a fileset to be iterated over.
+ *
+ * @param fileset a <code>FileSet</code> value
+ */
+ public void add(FileSet fileset) {
+ getOrCreatePath().addFileset(fileset);
+ }
+
+ /**
+ * Add a fileset to be iterated over.
+ *
+ * @param fileset a <code>FileSet</code> value
+ */
+ public void addFileSet(FileSet fileset) {
+ add(fileset);
+ }
+
+ /**
+ * Add a dirset to be iterated over.
+ *
+ * @param dirset a <code>DirSet</code> value
+ */
+ public void add(DirSet dirset) {
+ getOrCreatePath().addDirset(dirset);
+ }
+
+ /**
+ * Add a dirset to be iterated over.
+ *
+ * @param dirset a <code>DirSet</code> value
+ */
+ public void addDirSet(DirSet dirset) {
+ add(dirset);
+ }
+
+ /**
+ * Add a collection that can be iterated over.
+ *
+ * @param collection a <code>Collection</code> value.
+ */
+ public void add(Collection collection) {
+ hasIterators.add(new ReflectIterator(collection));
+ }
+
+ /**
+ * Add an iterator to be iterated over.
+ *
+ * @param iterator an <code>Iterator</code> value
+ */
+ public void add(Iterator iterator) {
+ hasIterators.add(new IteratorIterator(iterator));
+ }
+
+ /**
+ * Add an object that has an Iterator iterator() method
+ * that can be iterated over.
+ *
+ * @param obj An object that can be iterated over.
+ */
+ public void add(Object obj) {
+ hasIterators.add(new ReflectIterator(obj));
+ }
+
+ /**
+ * Interface for the objects in the iterator collection.
+ */
+ private interface HasIterator {
+ Iterator iterator();
+ }
+
+ private static class IteratorIterator implements HasIterator {
+ private Iterator iterator;
+ public IteratorIterator(Iterator iterator) {
+ this.iterator = iterator;
+ }
+ public Iterator iterator() {
+ return this.iterator;
+ }
+ }
+
+ private static class MapIterator implements HasIterator {
+ private Map map;
+ public MapIterator(Map map) {
+ this.map = map;
+ }
+ public Iterator iterator() {
+ return map.values().iterator();
+ }
+ }
+
+ private static class ReflectIterator implements HasIterator {
+ private Object obj;
+ private Method method;
+ public ReflectIterator(Object obj) {
+ this.obj = obj;
+ try {
+ method = obj.getClass().getMethod(
+ "iterator", new Class[] {});
+ } catch (Throwable t) {
+ throw new BuildException(
+ "Invalid type " + obj.getClass() + " used in For task, it does"
+ + " not have a public iterator method");
+ }
+ }
+
+ public Iterator iterator() {
+ try {
+ return (Iterator) method.invoke(obj, new Object[] {});
+ } catch (Throwable t) {
+ throw new BuildException(t);
+ }
+ }
+ }
+}