diff options
Diffstat (limited to 'src/main/java/net/sf/antcontrib/logic/ForTask.java')
-rw-r--r-- | src/main/java/net/sf/antcontrib/logic/ForTask.java | 464 |
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); + } + } + } +} |