|
|
@@ -1,397 +0,0 @@ |
|
|
|
/* |
|
|
|
* The Apache Software License, Version 1.1 |
|
|
|
* |
|
|
|
* Copyright (c) 1999, 2000 The Apache Software Foundation. All rights |
|
|
|
* reserved. |
|
|
|
* |
|
|
|
* Redistribution and use in source and binary forms, with or without |
|
|
|
* modification, are permitted provided that the following conditions |
|
|
|
* are met: |
|
|
|
* |
|
|
|
* 1. Redistributions of source code must retain the above copyright |
|
|
|
* notice, this list of conditions and the following disclaimer. |
|
|
|
* |
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
|
|
* notice, this list of conditions and the following disclaimer in |
|
|
|
* the documentation and/or other materials provided with the |
|
|
|
* distribution. |
|
|
|
* |
|
|
|
* 3. The end-user documentation included with the redistribution, if |
|
|
|
* any, must include the following acknowlegement: |
|
|
|
* "This product includes software developed by the |
|
|
|
* Apache Software Foundation (http://www.apache.org/)." |
|
|
|
* Alternately, this acknowlegement may appear in the software itself, |
|
|
|
* if and wherever such third-party acknowlegements normally appear. |
|
|
|
* |
|
|
|
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software |
|
|
|
* Foundation" must not be used to endorse or promote products derived |
|
|
|
* from this software without prior written permission. For written |
|
|
|
* permission, please contact apache@apache.org. |
|
|
|
* |
|
|
|
* 5. Products derived from this software may not be called "Apache" |
|
|
|
* nor may "Apache" appear in their names without prior written |
|
|
|
* permission of the Apache Group. |
|
|
|
* |
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
|
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
|
|
* SUCH DAMAGE. |
|
|
|
* ==================================================================== |
|
|
|
* |
|
|
|
* This software consists of voluntary contributions made by many |
|
|
|
* individuals on behalf of the Apache Software Foundation. For more |
|
|
|
* information on the Apache Software Foundation, please see |
|
|
|
* <http://www.apache.org/>. |
|
|
|
*/ |
|
|
|
package org.apache.ant.engine; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.*; |
|
|
|
import org.apache.ant.AntException; |
|
|
|
import org.apache.ant.tasks.Task; |
|
|
|
|
|
|
|
/** |
|
|
|
* The engine that actually invokes each Task. In addition to specifying a Task |
|
|
|
* to execute, it may be desirable to specify the root Task that will define |
|
|
|
* an execution cycle. |
|
|
|
*/ |
|
|
|
public class TaskEngineImpl implements TaskEngine { |
|
|
|
|
|
|
|
/** |
|
|
|
* Analagous to a call stack, but with Tasks. |
|
|
|
*/ |
|
|
|
protected Stack taskStack = new Stack();; |
|
|
|
|
|
|
|
/** |
|
|
|
* As the task stack is built, a mirror representation will also be |
|
|
|
* contructed that will hold property values. |
|
|
|
*/ |
|
|
|
protected Stack propertyStack = new Stack(); |
|
|
|
|
|
|
|
/** |
|
|
|
* Keeps track of AntEngineListeners. We don't have to use Vector because we |
|
|
|
* take care of synchronization on the add, remove, and iteration operations. |
|
|
|
*/ |
|
|
|
protected ArrayList listenerList = new ArrayList(); |
|
|
|
|
|
|
|
private int engineLevel = 0; |
|
|
|
|
|
|
|
/** |
|
|
|
* Constructor is private so it cannot be instantiated. Users of this class |
|
|
|
* will get an instance by using the getTaskEngine() method. This will allow |
|
|
|
* us to have a simple Factory implementation. We may use a Singleton |
|
|
|
* implementation, or a collection pool. The choice is up to us. |
|
|
|
*/ |
|
|
|
private TaskEngineImpl() { |
|
|
|
super(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Return a usable instance of a TaskEngine to the requestor. Nothing |
|
|
|
* sophisticated yet, simple doles out a new instance each time. |
|
|
|
*/ |
|
|
|
public static TaskEngine getTaskEngine() { |
|
|
|
return new TaskEngineImpl(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Walk the list of Tasks backwards until the root is reached. Keep track of |
|
|
|
* the Tasks along the way in a Stack. Return null if the root Task is not a |
|
|
|
* parent of the provided Task. |
|
|
|
*/ |
|
|
|
protected Stack getTaskStack(Task root, Task task) { |
|
|
|
Stack stack = new Stack(); |
|
|
|
while (task != null) { |
|
|
|
stack.push(task); |
|
|
|
if (task == root) { |
|
|
|
return stack; |
|
|
|
} |
|
|
|
task = task.getParent(); |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Returns the next Task to be executed from the taskStack. The task is not |
|
|
|
* removed from the Stack. |
|
|
|
*/ |
|
|
|
public Task getNextExecuteTask() { |
|
|
|
try { |
|
|
|
return (Task)taskStack.peek(); |
|
|
|
} catch (EmptyStackException esx) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* If no root is specified, we will assume that the user wants to execute |
|
|
|
* the Task with no root. This is accomplished by using the Task parameter |
|
|
|
* as its own root. |
|
|
|
*/ |
|
|
|
public void execute(Task task) throws AntException { |
|
|
|
execute(task, task); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* This is the workhorse, however it has been made to be very simple. Given |
|
|
|
* the ability to specify a path between root and the target Task, we build |
|
|
|
* a trail of Tasks to connect the two. Next we execute each Task on the way |
|
|
|
* between the two Tasks. Once we arrive at the Task to execute, we execute |
|
|
|
* all of its chlidren. |
|
|
|
*/ |
|
|
|
public void execute(Task root, Task task) throws AntException { |
|
|
|
fireEngineStart(); |
|
|
|
try { |
|
|
|
taskStack = getTaskStack(root, task);; |
|
|
|
if (taskStack == null) { |
|
|
|
throw new AntException( |
|
|
|
"The execution root Task is not an ancestor of the execution Task."); |
|
|
|
} |
|
|
|
|
|
|
|
// Pop thru the stack and execute each Task we come across. |
|
|
|
while (!taskStack.isEmpty()) { |
|
|
|
executeTask(taskStack); |
|
|
|
} |
|
|
|
} finally { |
|
|
|
fireEngineFinish(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* A recursive routine that allows all Tasks in the stack to be executed. At |
|
|
|
* the same time, the stack may grow to include new Tasks. |
|
|
|
*/ |
|
|
|
protected void executeTask(Stack taskStack) throws AntException { |
|
|
|
Task task = (Task)taskStack.pop(); |
|
|
|
|
|
|
|
fireTaskStart(task); |
|
|
|
try { |
|
|
|
// Add a new property holder for this task to the property stack. Note |
|
|
|
// that the parent of the new holder is the current stack head. |
|
|
|
if (task.isPropertyContainer()) { |
|
|
|
if (propertyStack.isEmpty()) { |
|
|
|
propertyStack.push(new HierarchicalHashtable()); |
|
|
|
} else { |
|
|
|
propertyStack.push(new HierarchicalHashtable( |
|
|
|
(HierarchicalHashtable)propertyStack.peek())); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Allow Task to do whatever it may need to do before touching its |
|
|
|
// children. |
|
|
|
task.init(this); |
|
|
|
|
|
|
|
// Iterate the Task's children and execute any priority Tasks. |
|
|
|
Task[] tasks = task.getChildren(); |
|
|
|
for (int i = 0, c = tasks.length; i < c; i++) { |
|
|
|
if (tasks[i].getExecutionMode() == Task.EXECUTION_MODE_PRIORITY) { |
|
|
|
taskStack.push(tasks[i]); |
|
|
|
executeTask(taskStack); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Allow the Task to validate. |
|
|
|
task.validate(); |
|
|
|
|
|
|
|
// Finally, execute the Task. |
|
|
|
fireTaskExecute(task); |
|
|
|
task.execute(this); |
|
|
|
|
|
|
|
// We can discard the no londer needed property holder. |
|
|
|
if (task.isPropertyContainer()) { |
|
|
|
propertyStack.pop(); |
|
|
|
} |
|
|
|
|
|
|
|
} catch (AntException ax) { |
|
|
|
fireTaskException(task, ax); |
|
|
|
} finally { |
|
|
|
fireTaskFinish(task); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Causes an AntEvent to be generated and fired to all listeners. |
|
|
|
*/ |
|
|
|
public void message(Task task, String message) { |
|
|
|
fireTaskMessage(task, message); |
|
|
|
} |
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////// |
|
|
|
// Listener Support // |
|
|
|
//////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
public synchronized void addAntEngineListener(AntEngineListener listener) { |
|
|
|
if (!listenerList.contains(listener)) { |
|
|
|
listenerList.add(listener); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public synchronized void removeAntEngineListener(AntEngineListener listener) { |
|
|
|
if (listenerList.contains(listener)) { |
|
|
|
listenerList.remove(listener); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireEngineStart() { |
|
|
|
if (engineLevel++ > 0) return; |
|
|
|
AntEvent e = new AntEvent(this); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).engineStart(e); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireEngineFinish() { |
|
|
|
if (--engineLevel > 0) return; |
|
|
|
AntEvent e = new AntEvent(this); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).engineFinish(e); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireTaskStart(Task task) { |
|
|
|
AntEvent e = new AntEvent(this, task); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).taskStart(e); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireTaskExecute(Task task) { |
|
|
|
AntEvent e = new AntEvent(this, task); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).taskExecute(e); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireTaskFinish(Task task) { |
|
|
|
AntEvent e = new AntEvent(this, task); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).taskFinish(e); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireTaskMessage(Task task, String message) { |
|
|
|
AntEvent e = new AntEvent(this, task); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).taskMessage(e, message); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
protected synchronized void fireTaskException(Task task, AntException exception) { |
|
|
|
AntEvent e = new AntEvent(this, task); |
|
|
|
for (int i = 0; i < listenerList.size(); i++) { |
|
|
|
((AntEngineListener)listenerList.get(i)).taskException(e, exception); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////// |
|
|
|
// Property Support Methods // |
|
|
|
//////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
/** |
|
|
|
* This is the routine that will perform key substitution. Phrase will come |
|
|
|
* in as "src/${someparam}" and be converted to the appropriate "normalized" |
|
|
|
* string. I suppose while I'm doing this we should support phrases with |
|
|
|
* nested keys, such as "src/${build${token}}". Also, we should properly |
|
|
|
* handle cases where ${someparam} will evaluate to ${anotherparam}. |
|
|
|
* <p></p> |
|
|
|
* One thing that will be different from the Ant 1.2 mechanismoccurs when a |
|
|
|
* parameter value is not found. The substitution routine inserts it back in |
|
|
|
* the phrase unchanged. I have opted to insert a zero-length string |
|
|
|
* instead. |
|
|
|
* <p></p> |
|
|
|
* I should add a switch to the engine that will give the user the ability |
|
|
|
* to throw an exception if a key is not found. Pretty easy, except this |
|
|
|
* method is a strange place for an AntException to be thrown. Perhaps I |
|
|
|
* should use a RuntimeException instead... |
|
|
|
* <p></p> |
|
|
|
* A brief rundown on the logic here: |
|
|
|
* I check for the first instances of a key prefix. |
|
|
|
* If none found we return the phrase as is. |
|
|
|
* If key prefix is found get location of next key prefix and suffix. |
|
|
|
* If suffix is found first, we have found a key. |
|
|
|
* If there is no suffix, we return the phrase. |
|
|
|
*/ |
|
|
|
static final String KEY_PREFIX = "${"; |
|
|
|
static final String KEY_SUFFIX = "}"; |
|
|
|
protected String substitute(String phrase) { |
|
|
|
StringBuffer sb = new StringBuffer(phrase); |
|
|
|
int startPoint = 0; |
|
|
|
while (startPoint >= 0 && startPoint < phrase.length()) { |
|
|
|
int pre1 = startPoint + phrase.substring(startPoint).indexOf(KEY_PREFIX); |
|
|
|
if (pre1 < 0) break; |
|
|
|
int suf1 = phrase.substring(pre1 + KEY_PREFIX.length()).indexOf(KEY_SUFFIX); |
|
|
|
if (suf1 < 0) break; |
|
|
|
suf1 = suf1 + pre1 + KEY_PREFIX.length(); |
|
|
|
int pre2 = phrase.substring(pre1 + KEY_PREFIX.length()).indexOf(KEY_PREFIX); |
|
|
|
if (pre2 < 0) { |
|
|
|
pre2 = phrase.length() + 1; |
|
|
|
} else { |
|
|
|
pre2 = pre2 + pre1 + KEY_PREFIX.length(); |
|
|
|
} |
|
|
|
|
|
|
|
if (suf1 < pre2) { |
|
|
|
// we have found a token |
|
|
|
String key = sb.substring(pre1 + KEY_PREFIX.length(), suf1); |
|
|
|
sb.delete(pre1, suf1 + 1); |
|
|
|
Object value = getPropertyValueNoSubstitution(key); |
|
|
|
if (value != null) { |
|
|
|
sb.insert(pre1, value.toString()); |
|
|
|
} |
|
|
|
return substitute(sb.toString()); |
|
|
|
} |
|
|
|
startPoint = pre2; |
|
|
|
} |
|
|
|
return sb.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
public List getPropertyNames() { |
|
|
|
if (propertyStack.isEmpty()) return new ArrayList(); |
|
|
|
HierarchicalHashtable hash = (HierarchicalHashtable)propertyStack.peek(); |
|
|
|
return hash.getPropertyNames(); |
|
|
|
} |
|
|
|
|
|
|
|
public Object getPropertyValue(String name) { |
|
|
|
if (propertyStack.isEmpty()) return null; |
|
|
|
HierarchicalHashtable hash = (HierarchicalHashtable)propertyStack.peek(); |
|
|
|
Object result = hash.getPropertyValue(name); |
|
|
|
if (result instanceof String) { |
|
|
|
return substitute((String)result); |
|
|
|
} else { |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected Object getPropertyValueNoSubstitution(String name) { |
|
|
|
if (propertyStack.isEmpty()) return null; |
|
|
|
HierarchicalHashtable hash = (HierarchicalHashtable)propertyStack.peek(); |
|
|
|
return hash.getPropertyValue(name); |
|
|
|
} |
|
|
|
|
|
|
|
public void setPropertyValue(String name, Object value) { |
|
|
|
if (propertyStack.isEmpty()) return; |
|
|
|
HierarchicalHashtable hash = (HierarchicalHashtable)propertyStack.peek(); |
|
|
|
hash.setPropertyValue(name, value); |
|
|
|
} |
|
|
|
|
|
|
|
public void removePropertyValue(String name) { |
|
|
|
if (propertyStack.isEmpty()) return; |
|
|
|
HierarchicalHashtable hash = (HierarchicalHashtable)propertyStack.peek(); |
|
|
|
hash.remove(name); |
|
|
|
} |
|
|
|
} |