001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant;
020:
021: import java.util.ArrayList;
022: import java.util.Collections;
023: import java.util.Enumeration;
024: import java.util.Hashtable;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.StringTokenizer;
028:
029: import org.apache.tools.ant.util.CollectionUtils;
030:
031: /**
032: * Class to implement a target object with required parameters.
033: *
034: */
035: public class Target implements TaskContainer {
036:
037: /** Name of this target. */
038: private String name;
039: /** The "if" condition to test on execution. */
040: private String ifCondition = "";
041: /** The "unless" condition to test on execution. */
042: private String unlessCondition = "";
043: /** List of targets this target is dependent on. */
044: private List dependencies = null;
045: /** Children of this target (tasks and data types). */
046: private List children = new ArrayList();
047: /** Since Ant 1.6.2 */
048: private Location location = Location.UNKNOWN_LOCATION;
049:
050: /** Project this target belongs to. */
051: private Project project;
052:
053: /** Description of this target, if any. */
054: private String description = null;
055:
056: /** Default constructor. */
057: public Target() {
058: //empty
059: }
060:
061: /**
062: * Cloning constructor.
063: * @param other the Target to clone.
064: */
065: public Target(Target other) {
066: this .name = other.name;
067: this .ifCondition = other.ifCondition;
068: this .unlessCondition = other.unlessCondition;
069: this .dependencies = other.dependencies;
070: this .location = other.location;
071: this .project = other.project;
072: this .description = other.description;
073: // The children are added to after this cloning
074: this .children = other.children;
075: }
076:
077: /**
078: * Sets the project this target belongs to.
079: *
080: * @param project The project this target belongs to.
081: * Must not be <code>null</code>.
082: */
083: public void setProject(Project project) {
084: this .project = project;
085: }
086:
087: /**
088: * Returns the project this target belongs to.
089: *
090: * @return The project this target belongs to, or <code>null</code> if
091: * the project has not been set yet.
092: */
093: public Project getProject() {
094: return project;
095: }
096:
097: /**
098: * Sets the location of this target's definition.
099: *
100: * @param location <code>Location</code>
101: * @since 1.6.2
102: */
103: public void setLocation(Location location) {
104: this .location = location;
105: }
106:
107: /**
108: * Get the location of this target's definition.
109: *
110: * @return <code>Location</code>
111: * @since 1.6.2
112: */
113: public Location getLocation() {
114: return location;
115: }
116:
117: /**
118: * Sets the list of targets this target is dependent on.
119: * The targets themselves are not resolved at this time.
120: *
121: * @param depS A comma-separated list of targets this target
122: * depends on. Must not be <code>null</code>.
123: */
124: public void setDepends(String depS) {
125: if (depS.length() > 0) {
126: StringTokenizer tok = new StringTokenizer(depS, ",", true);
127: while (tok.hasMoreTokens()) {
128: String token = tok.nextToken().trim();
129:
130: // Make sure the dependency is not empty string
131: if ("".equals(token) || ",".equals(token)) {
132: throw new BuildException("Syntax Error: depends "
133: + "attribute of target \"" + getName()
134: + "\" has an empty string as dependency.");
135: }
136:
137: addDependency(token);
138:
139: // Make sure that depends attribute does not
140: // end in a ,
141: if (tok.hasMoreTokens()) {
142: token = tok.nextToken();
143: if (!tok.hasMoreTokens() || !",".equals(token)) {
144: throw new BuildException(
145: "Syntax Error: Depend "
146: + "attribute for target \""
147: + getName()
148: + "\" ends with a , character");
149: }
150: }
151: }
152: }
153: }
154:
155: /**
156: * Sets the name of this target.
157: *
158: * @param name The name of this target. Should not be <code>null</code>.
159: */
160: public void setName(String name) {
161: this .name = name;
162: }
163:
164: /**
165: * Returns the name of this target.
166: *
167: * @return the name of this target, or <code>null</code> if the
168: * name has not been set yet.
169: */
170: public String getName() {
171: return name;
172: }
173:
174: /**
175: * Adds a task to this target.
176: *
177: * @param task The task to be added. Must not be <code>null</code>.
178: */
179: public void addTask(Task task) {
180: children.add(task);
181: }
182:
183: /**
184: * Adds the wrapper for a data type element to this target.
185: *
186: * @param r The wrapper for the data type element to be added.
187: * Must not be <code>null</code>.
188: */
189: public void addDataType(RuntimeConfigurable r) {
190: children.add(r);
191: }
192:
193: /**
194: * Returns the current set of tasks to be executed by this target.
195: *
196: * @return an array of the tasks currently within this target
197: */
198: public Task[] getTasks() {
199: List tasks = new ArrayList(children.size());
200: Iterator it = children.iterator();
201: while (it.hasNext()) {
202: Object o = it.next();
203: if (o instanceof Task) {
204: tasks.add(o);
205: }
206: }
207:
208: return (Task[]) tasks.toArray(new Task[tasks.size()]);
209: }
210:
211: /**
212: * Adds a dependency to this target.
213: *
214: * @param dependency The name of a target this target is dependent on.
215: * Must not be <code>null</code>.
216: */
217: public void addDependency(String dependency) {
218: if (dependencies == null) {
219: dependencies = new ArrayList(2);
220: }
221: dependencies.add(dependency);
222: }
223:
224: /**
225: * Returns an enumeration of the dependencies of this target.
226: *
227: * @return an enumeration of the dependencies of this target
228: */
229: public Enumeration getDependencies() {
230: return (dependencies != null ? Collections
231: .enumeration(dependencies)
232: : new CollectionUtils.EmptyEnumeration());
233: }
234:
235: /**
236: * Does this target depend on the named target?
237: * @param other the other named target.
238: * @return true if the target does depend on the named target
239: * @since Ant 1.6
240: */
241: public boolean dependsOn(String other) {
242: Project p = getProject();
243: Hashtable t = (p == null) ? null : p.getTargets();
244: return (p != null && p.topoSort(getName(), t, false).contains(
245: t.get(other)));
246: }
247:
248: /**
249: * Sets the "if" condition to test on execution. This is the
250: * name of a property to test for existence - if the property
251: * is not set, the task will not execute. The property goes
252: * through property substitution once before testing, so if
253: * property <code>foo</code> has value <code>bar</code>, setting
254: * the "if" condition to <code>${foo}_x</code> will mean that the
255: * task will only execute if property <code>bar_x</code> is set.
256: *
257: * @param property The property condition to test on execution.
258: * May be <code>null</code>, in which case
259: * no "if" test is performed.
260: */
261: public void setIf(String property) {
262: ifCondition = (property == null) ? "" : property;
263: }
264:
265: /**
266: * Returns the "if" property condition of this target.
267: *
268: * @return the "if" property condition or <code>null</code> if no
269: * "if" condition had been defined.
270: * @since 1.6.2
271: */
272: public String getIf() {
273: return ("".equals(ifCondition) ? null : ifCondition);
274: }
275:
276: /**
277: * Sets the "unless" condition to test on execution. This is the
278: * name of a property to test for existence - if the property
279: * is set, the task will not execute. The property goes
280: * through property substitution once before testing, so if
281: * property <code>foo</code> has value <code>bar</code>, setting
282: * the "unless" condition to <code>${foo}_x</code> will mean that the
283: * task will only execute if property <code>bar_x</code> isn't set.
284: *
285: * @param property The property condition to test on execution.
286: * May be <code>null</code>, in which case
287: * no "unless" test is performed.
288: */
289: public void setUnless(String property) {
290: unlessCondition = (property == null) ? "" : property;
291: }
292:
293: /**
294: * Returns the "unless" property condition of this target.
295: *
296: * @return the "unless" property condition or <code>null</code>
297: * if no "unless" condition had been defined.
298: * @since 1.6.2
299: */
300: public String getUnless() {
301: return ("".equals(unlessCondition) ? null : unlessCondition);
302: }
303:
304: /**
305: * Sets the description of this target.
306: *
307: * @param description The description for this target.
308: * May be <code>null</code>, indicating that no
309: * description is available.
310: */
311: public void setDescription(String description) {
312: this .description = description;
313: }
314:
315: /**
316: * Returns the description of this target.
317: *
318: * @return the description of this target, or <code>null</code> if no
319: * description is available.
320: */
321: public String getDescription() {
322: return description;
323: }
324:
325: /**
326: * Returns the name of this target.
327: *
328: * @return the name of this target, or <code>null</code> if the
329: * name has not been set yet.
330: */
331: public String toString() {
332: return name;
333: }
334:
335: /**
336: * Executes the target if the "if" and "unless" conditions are
337: * satisfied. Dependency checking should be done before calling this
338: * method, as it does no checking of its own. If either the "if"
339: * or "unless" test prevents this target from being executed, a verbose
340: * message is logged giving the reason. It is recommended that clients
341: * of this class call performTasks rather than this method so that
342: * appropriate build events are fired.
343: *
344: * @exception BuildException if any of the tasks fail or if a data type
345: * configuration fails.
346: *
347: * @see #performTasks()
348: * @see #setIf(String)
349: * @see #setUnless(String)
350: */
351: public void execute() throws BuildException {
352: if (testIfCondition() && testUnlessCondition()) {
353: for (int taskPosition = 0; taskPosition < children.size(); ++taskPosition) {
354: Object o = children.get(taskPosition);
355: if (o instanceof Task) {
356: Task task = (Task) o;
357: task.perform();
358: } else {
359: RuntimeConfigurable r = (RuntimeConfigurable) o;
360: r.maybeConfigure(project);
361: }
362: }
363: } else if (!testIfCondition()) {
364: project.log(this , "Skipped because property '"
365: + project.replaceProperties(ifCondition)
366: + "' not set.", Project.MSG_VERBOSE);
367: } else {
368: project.log(this , "Skipped because property '"
369: + project.replaceProperties(unlessCondition)
370: + "' set.", Project.MSG_VERBOSE);
371: }
372: }
373:
374: /**
375: * Performs the tasks within this target (if the conditions are met),
376: * firing target started/target finished messages around a call to
377: * execute.
378: *
379: * @see #execute()
380: */
381: public final void performTasks() {
382: RuntimeException thrown = null;
383: project.fireTargetStarted(this );
384: try {
385: execute();
386: } catch (RuntimeException exc) {
387: thrown = exc;
388: throw exc;
389: } finally {
390: project.fireTargetFinished(this , thrown);
391: }
392: }
393:
394: /**
395: * Replaces all occurrences of the given task in the list
396: * of children with the replacement data type wrapper.
397: *
398: * @param el The task to replace.
399: * Must not be <code>null</code>.
400: * @param o The data type wrapper to replace <code>el</code> with.
401: */
402: void replaceChild(Task el, RuntimeConfigurable o) {
403: int index;
404: while ((index = children.indexOf(el)) >= 0) {
405: children.set(index, o);
406: }
407: }
408:
409: /**
410: * Replaces all occurrences of the given task in the list
411: * of children with the replacement task.
412: *
413: * @param el The task to replace.
414: * Must not be <code>null</code>.
415: * @param o The task to replace <code>el</code> with.
416: */
417: void replaceChild(Task el, Task o) {
418: int index;
419: while ((index = children.indexOf(el)) >= 0) {
420: children.set(index, o);
421: }
422: }
423:
424: /**
425: * Tests whether or not the "if" condition is satisfied.
426: *
427: * @return whether or not the "if" condition is satisfied. If no
428: * condition (or an empty condition) has been set,
429: * <code>true</code> is returned.
430: *
431: * @see #setIf(String)
432: */
433: private boolean testIfCondition() {
434: if ("".equals(ifCondition)) {
435: return true;
436: }
437:
438: String test = project.replaceProperties(ifCondition);
439: return project.getProperty(test) != null;
440: }
441:
442: /**
443: * Tests whether or not the "unless" condition is satisfied.
444: *
445: * @return whether or not the "unless" condition is satisfied. If no
446: * condition (or an empty condition) has been set,
447: * <code>true</code> is returned.
448: *
449: * @see #setUnless(String)
450: */
451: private boolean testUnlessCondition() {
452: if ("".equals(unlessCondition)) {
453: return true;
454: }
455: String test = project.replaceProperties(unlessCondition);
456: return project.getProperty(test) == null;
457: }
458: }
|