1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.tools.ant;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Enumeration;
23 import java.util.Hashtable;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.StringTokenizer;
27
28 import org.apache.tools.ant.property.LocalProperties;
29
30 /**
31 * Class to implement a target object with required parameters.
32 *
33 * <p>If you are creating Targets programmatically, make sure you set
34 * the Location to a useful value. In particular all targets should
35 * have different location values.</p>
36 */
37 public class Target implements TaskContainer {
38
39 /** Name of this target. */
40 private String name;
41
42 /** The "if" condition to test on execution. */
43 private String ifCondition = "";
44
45 /** The "unless" condition to test on execution. */
46 private String unlessCondition = "";
47
48 /** List of targets this target is dependent on. */
49 private List dependencies = null;
50
51 /** Children of this target (tasks and data types). */
52 private List children = new ArrayList();
53
54 /** Since Ant 1.6.2 */
55 private Location location = Location.UNKNOWN_LOCATION;
56
57 /** Project this target belongs to. */
58 private Project project;
59
60 /** Description of this target, if any. */
61 private String description = null;
62
63 /** Default constructor. */
64 public Target() {
65 //empty
66 }
67
68 /**
69 * Cloning constructor.
70 * @param other the Target to clone.
71 */
72 public Target(Target other) {
73 this.name = other.name;
74 this.ifCondition = other.ifCondition;
75 this.unlessCondition = other.unlessCondition;
76 this.dependencies = other.dependencies;
77 this.location = other.location;
78 this.project = other.project;
79 this.description = other.description;
80 // The children are added to after this cloning
81 this.children = other.children;
82 }
83
84 /**
85 * Sets the project this target belongs to.
86 *
87 * @param project The project this target belongs to.
88 * Must not be <code>null</code>.
89 */
90 public void setProject(Project project) {
91 this.project = project;
92 }
93
94 /**
95 * Returns the project this target belongs to.
96 *
97 * @return The project this target belongs to, or <code>null</code> if
98 * the project has not been set yet.
99 */
100 public Project getProject() {
101 return project;
102 }
103
104 /**
105 * Sets the location of this target's definition.
106 *
107 * @param location <code>Location</code>
108 * @since 1.6.2
109 */
110 public void setLocation(Location location) {
111 this.location = location;
112 }
113
114 /**
115 * Get the location of this target's definition.
116 *
117 * @return <code>Location</code>
118 * @since 1.6.2
119 */
120 public Location getLocation() {
121 return location;
122 }
123
124 /**
125 * Sets the list of targets this target is dependent on.
126 * The targets themselves are not resolved at this time.
127 *
128 * @param depS A comma-separated list of targets this target
129 * depends on. Must not be <code>null</code>.
130 */
131 public void setDepends(String depS) {
132 for (Iterator iter = parseDepends(depS, getName(), "depends").iterator();
133 iter.hasNext(); ) {
134 addDependency((String) iter.next());
135 }
136 }
137
138 public static List/*<String>*/ parseDepends(String depends,
139 String targetName,
140 String attributeName) {
141 ArrayList list = new ArrayList();
142 if (depends.length() > 0) {
143 StringTokenizer tok =
144 new StringTokenizer(depends, ",", true);
145 while (tok.hasMoreTokens()) {
146 String token = tok.nextToken().trim();
147
148 // Make sure the dependency is not empty string
149 if ("".equals(token) || ",".equals(token)) {
150 throw new BuildException("Syntax Error: "
151 + attributeName
152 + " attribute of target \""
153 + targetName
154 + "\" contains an empty string.");
155 }
156
157 list.add(token);
158
159 // Make sure that depends attribute does not
160 // end in a ,
161 if (tok.hasMoreTokens()) {
162 token = tok.nextToken();
163 if (!tok.hasMoreTokens() || !",".equals(token)) {
164 throw new BuildException("Syntax Error: "
165 + attributeName
166 + " attribute for target \""
167 + targetName
168 + "\" ends with a \",\" "
169 + "character");
170 }
171 }
172 }
173 }
174 return list;
175 }
176
177 /**
178 * Sets the name of this target.
179 *
180 * @param name The name of this target. Should not be <code>null</code>.
181 */
182 public void setName(String name) {
183 this.name = name;
184 }
185
186 /**
187 * Returns the name of this target.
188 *
189 * @return the name of this target, or <code>null</code> if the
190 * name has not been set yet.
191 */
192 public String getName() {
193 return name;
194 }
195
196 /**
197 * Adds a task to this target.
198 *
199 * @param task The task to be added. Must not be <code>null</code>.
200 */
201 public void addTask(Task task) {
202 children.add(task);
203 }
204
205 /**
206 * Adds the wrapper for a data type element to this target.
207 *
208 * @param r The wrapper for the data type element to be added.
209 * Must not be <code>null</code>.
210 */
211 public void addDataType(RuntimeConfigurable r) {
212 children.add(r);
213 }
214
215 /**
216 * Returns the current set of tasks to be executed by this target.
217 *
218 * @return an array of the tasks currently within this target
219 */
220 public Task[] getTasks() {
221 List tasks = new ArrayList(children.size());
222 Iterator it = children.iterator();
223 while (it.hasNext()) {
224 Object o = it.next();
225 if (o instanceof Task) {
226 tasks.add(o);
227 }
228 }
229 return (Task[]) tasks.toArray(new Task[tasks.size()]);
230 }
231
232 /**
233 * Adds a dependency to this target.
234 *
235 * @param dependency The name of a target this target is dependent on.
236 * Must not be <code>null</code>.
237 */
238 public void addDependency(String dependency) {
239 if (dependencies == null) {
240 dependencies = new ArrayList(2);
241 }
242 dependencies.add(dependency);
243 }
244
245 /**
246 * Returns an enumeration of the dependencies of this target.
247 *
248 * @return an enumeration of the dependencies of this target
249 */
250 public Enumeration getDependencies() {
251 return Collections
252 .enumeration(dependencies == null ? Collections.EMPTY_LIST : dependencies);
253 }
254
255 /**
256 * Does this target depend on the named target?
257 * @param other the other named target.
258 * @return true if the target does depend on the named target
259 * @since Ant 1.6
260 */
261 public boolean dependsOn(String other) {
262 Project p = getProject();
263 Hashtable t = p == null ? null : p.getTargets();
264 return p != null && p.topoSort(getName(), t, false).contains(t.get(other));
265 }
266
267 /**
268 * Sets the "if" condition to test on execution. This is the
269 * name of a property to test for existence - if the property
270 * is not set, the task will not execute. The property goes
271 * through property substitution once before testing, so if
272 * property <code>foo</code> has value <code>bar</code>, setting
273 * the "if" condition to <code>${foo}_x</code> will mean that the
274 * task will only execute if property <code>bar_x</code> is set.
275 *
276 * @param property The property condition to test on execution.
277 * May be <code>null</code>, in which case
278 * no "if" test is performed.
279 */
280 public void setIf(String property) {
281 ifCondition = property == null ? "" : property;
282 }
283
284 /**
285 * Returns the "if" property condition of this target.
286 *
287 * @return the "if" property condition or <code>null</code> if no
288 * "if" condition had been defined.
289 * @since 1.6.2
290 */
291 public String getIf() {
292 return "".equals(ifCondition) ? null : ifCondition;
293 }
294
295 /**
296 * Sets the "unless" condition to test on execution. This is the
297 * name of a property to test for existence - if the property
298 * is set, the task will not execute. The property goes
299 * through property substitution once before testing, so if
300 * property <code>foo</code> has value <code>bar</code>, setting
301 * the "unless" condition to <code>${foo}_x</code> will mean that the
302 * task will only execute if property <code>bar_x</code> isn't set.
303 *
304 * @param property The property condition to test on execution.
305 * May be <code>null</code>, in which case
306 * no "unless" test is performed.
307 */
308 public void setUnless(String property) {
309 unlessCondition = property == null ? "" : property;
310 }
311
312 /**
313 * Returns the "unless" property condition of this target.
314 *
315 * @return the "unless" property condition or <code>null</code>
316 * if no "unless" condition had been defined.
317 * @since 1.6.2
318 */
319 public String getUnless() {
320 return "".equals(unlessCondition) ? null : unlessCondition;
321 }
322
323 /**
324 * Sets the description of this target.
325 *
326 * @param description The description for this target.
327 * May be <code>null</code>, indicating that no
328 * description is available.
329 */
330 public void setDescription(String description) {
331 this.description = description;
332 }
333
334 /**
335 * Returns the description of this target.
336 *
337 * @return the description of this target, or <code>null</code> if no
338 * description is available.
339 */
340 public String getDescription() {
341 return description;
342 }
343
344 /**
345 * Returns the name of this target.
346 *
347 * @return the name of this target, or <code>null</code> if the
348 * name has not been set yet.
349 */
350 public String toString() {
351 return name;
352 }
353
354 /**
355 * Executes the target if the "if" and "unless" conditions are
356 * satisfied. Dependency checking should be done before calling this
357 * method, as it does no checking of its own. If either the "if"
358 * or "unless" test prevents this target from being executed, a verbose
359 * message is logged giving the reason. It is recommended that clients
360 * of this class call performTasks rather than this method so that
361 * appropriate build events are fired.
362 *
363 * @exception BuildException if any of the tasks fail or if a data type
364 * configuration fails.
365 *
366 * @see #performTasks()
367 * @see #setIf(String)
368 * @see #setUnless(String)
369 */
370 public void execute() throws BuildException {
371 if (!testIfAllows()) {
372 project.log(this, "Skipped because property '" + project.replaceProperties(ifCondition)
373 + "' not set.", Project.MSG_VERBOSE);
374 return;
375 }
376 if (!testUnlessAllows()) {
377 project.log(this, "Skipped because property '"
378 + project.replaceProperties(unlessCondition) + "' set.", Project.MSG_VERBOSE);
379 return;
380 }
381 LocalProperties localProperties = LocalProperties.get(getProject());
382 localProperties.enterScope();
383 try {
384 // use index-based approach to avoid ConcurrentModificationExceptions;
385 // also account for growing target children
386 for (int i = 0; i < children.size(); i++) {
387 Object o = children.get(i);
388 if (o instanceof Task) {
389 Task task = (Task) o;
390 task.perform();
391 } else {
392 ((RuntimeConfigurable) o).maybeConfigure(project);
393 }
394 }
395 } finally {
396 localProperties.exitScope();
397 }
398 }
399
400 /**
401 * Performs the tasks within this target (if the conditions are met),
402 * firing target started/target finished messages around a call to
403 * execute.
404 *
405 * @see #execute()
406 */
407 public final void performTasks() {
408 RuntimeException thrown = null;
409 project.fireTargetStarted(this);
410 try {
411 execute();
412 } catch (RuntimeException exc) {
413 thrown = exc;
414 throw exc;
415 } finally {
416 project.fireTargetFinished(this, thrown);
417 }
418 }
419
420 /**
421 * Replaces all occurrences of the given task in the list
422 * of children with the replacement data type wrapper.
423 *
424 * @param el The task to replace.
425 * Must not be <code>null</code>.
426 * @param o The data type wrapper to replace <code>el</code> with.
427 */
428 void replaceChild(Task el, RuntimeConfigurable o) {
429 int index;
430 while ((index = children.indexOf(el)) >= 0) {
431 children.set(index, o);
432 }
433 }
434
435 /**
436 * Replaces all occurrences of the given task in the list
437 * of children with the replacement task.
438 *
439 * @param el The task to replace.
440 * Must not be <code>null</code>.
441 * @param o The task to replace <code>el</code> with.
442 */
443 void replaceChild(Task el, Task o) {
444 int index;
445 while ((index = children.indexOf(el)) >= 0) {
446 children.set(index, o);
447 }
448 }
449
450 /**
451 * Tests whether or not the "if" condition allows the execution of this target.
452 *
453 * @return whether or not the "if" condition is satisfied. If no
454 * condition (or an empty condition) has been set,
455 * <code>true</code> is returned.
456 *
457 * @see #setIf(String)
458 */
459 private boolean testIfAllows() {
460 PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(getProject());
461 Object o = propertyHelper.parseProperties(ifCondition);
462 return propertyHelper.testIfCondition(o);
463 }
464
465 /**
466 * Tests whether or not the "unless" condition allows the execution of this target.
467 *
468 * @return whether or not the "unless" condition is satisfied. If no
469 * condition (or an empty condition) has been set,
470 * <code>true</code> is returned.
471 *
472 * @see #setUnless(String)
473 */
474 private boolean testUnlessAllows() {
475 PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(getProject());
476 Object o = propertyHelper.parseProperties(unlessCondition);
477 return propertyHelper.testUnlessCondition(o);
478 }
479 }