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
19 package org.apache.tools.ant;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Enumeration;
24 import java.util.Hashtable;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.StringTokenizer;
28
29 import org.apache.tools.ant.util.CollectionUtils;
30
31 /**
32 * Class to implement a target object with required parameters.
33 *
34 */
35 public class Target implements TaskContainer {
36
37 /** Name of this target. */
38 private String name;
39 /** The "if" condition to test on execution. */
40 private String ifCondition = "";
41 /** The "unless" condition to test on execution. */
42 private String unlessCondition = "";
43 /** List of targets this target is dependent on. */
44 private List dependencies = null;
45 /** Children of this target (tasks and data types). */
46 private List children = new ArrayList();
47 /** Since Ant 1.6.2 */
48 private Location location = Location.UNKNOWN_LOCATION;
49
50 /** Project this target belongs to. */
51 private Project project;
52
53 /** Description of this target, if any. */
54 private String description = null;
55
56 /** Default constructor. */
57 public Target() {
58 //empty
59 }
60
61 /**
62 * Cloning constructor.
63 * @param other the Target to clone.
64 */
65 public Target(Target other) {
66 this.name = other.name;
67 this.ifCondition = other.ifCondition;
68 this.unlessCondition = other.unlessCondition;
69 this.dependencies = other.dependencies;
70 this.location = other.location;
71 this.project = other.project;
72 this.description = other.description;
73 // The children are added to after this cloning
74 this.children = other.children;
75 }
76
77 /**
78 * Sets the project this target belongs to.
79 *
80 * @param project The project this target belongs to.
81 * Must not be <code>null</code>.
82 */
83 public void setProject(Project project) {
84 this.project = project;
85 }
86
87 /**
88 * Returns the project this target belongs to.
89 *
90 * @return The project this target belongs to, or <code>null</code> if
91 * the project has not been set yet.
92 */
93 public Project getProject() {
94 return project;
95 }
96
97 /**
98 * Sets the location of this target's definition.
99 *
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 =
127 new StringTokenizer(depS, ",", true);
128 while (tok.hasMoreTokens()) {
129 String token = tok.nextToken().trim();
130
131 // Make sure the dependency is not empty string
132 if ("".equals(token) || ",".equals(token)) {
133 throw new BuildException("Syntax Error: depends "
134 + "attribute of target \"" + getName()
135 + "\" has an empty string as dependency.");
136 }
137
138 addDependency(token);
139
140 // Make sure that depends attribute does not
141 // end in a ,
142 if (tok.hasMoreTokens()) {
143 token = tok.nextToken();
144 if (!tok.hasMoreTokens() || !",".equals(token)) {
145 throw new BuildException("Syntax Error: Depend "
146 + "attribute for target \"" + getName()
147 + "\" ends with a , character");
148 }
149 }
150 }
151 }
152 }
153
154 /**
155 * Sets the name of this target.
156 *
157 * @param name The name of this target. Should not be <code>null</code>.
158 */
159 public void setName(String name) {
160 this.name = name;
161 }
162
163 /**
164 * Returns the name of this target.
165 *
166 * @return the name of this target, or <code>null</code> if the
167 * name has not been set yet.
168 */
169 public String getName() {
170 return name;
171 }
172
173 /**
174 * Adds a task to this target.
175 *
176 * @param task The task to be added. Must not be <code>null</code>.
177 */
178 public void addTask(Task task) {
179 children.add(task);
180 }
181
182 /**
183 * Adds the wrapper for a data type element to this target.
184 *
185 * @param r The wrapper for the data type element to be added.
186 * Must not be <code>null</code>.
187 */
188 public void addDataType(RuntimeConfigurable r) {
189 children.add(r);
190 }
191
192 /**
193 * Returns the current set of tasks to be executed by this target.
194 *
195 * @return an array of the tasks currently within this target
196 */
197 public Task[] getTasks() {
198 List tasks = new ArrayList(children.size());
199 Iterator it = children.iterator();
200 while (it.hasNext()) {
201 Object o = it.next();
202 if (o instanceof Task) {
203 tasks.add(o);
204 }
205 }
206
207 return (Task[]) tasks.toArray(new Task[tasks.size()]);
208 }
209
210 /**
211 * Adds a dependency to this target.
212 *
213 * @param dependency The name of a target this target is dependent on.
214 * Must not be <code>null</code>.
215 */
216 public void addDependency(String dependency) {
217 if (dependencies == null) {
218 dependencies = new ArrayList(2);
219 }
220 dependencies.add(dependency);
221 }
222
223 /**
224 * Returns an enumeration of the dependencies of this target.
225 *
226 * @return an enumeration of the dependencies of this target
227 */
228 public Enumeration getDependencies() {
229 return (dependencies != null ? Collections.enumeration(dependencies)
230 : new CollectionUtils.EmptyEnumeration());
231 }
232
233 /**
234 * Does this target depend on the named target?
235 * @param other the other named target.
236 * @return true if the target does depend on the named target
237 * @since Ant 1.6
238 */
239 public boolean dependsOn(String other) {
240 Project p = getProject();
241 Hashtable t = (p == null) ? null : p.getTargets();
242 return (p != null
243 && p.topoSort(getName(), t, false).contains(t.get(other)));
244 }
245
246 /**
247 * Sets the "if" condition to test on execution. This is the
248 * name of a property to test for existence - if the property
249 * is not set, the task will not execute. The property goes
250 * through property substitution once before testing, so if
251 * property <code>foo</code> has value <code>bar</code>, setting
252 * the "if" condition to <code>${foo}_x</code> will mean that the
253 * task will only execute if property <code>bar_x</code> is set.
254 *
255 * @param property The property condition to test on execution.
256 * May be <code>null</code>, in which case
257 * no "if" test is performed.
258 */
259 public void setIf(String property) {
260 ifCondition = (property == null) ? "" : property;
261 }
262
263 /**
264 * Returns the "if" property condition of this target.
265 *
266 * @return the "if" property condition or <code>null</code> if no
267 * "if" condition had been defined.
268 * @since 1.6.2
269 */
270 public String getIf() {
271 return ("".equals(ifCondition) ? null : ifCondition);
272 }
273
274 /**
275 * Sets the "unless" condition to test on execution. This is the
276 * name of a property to test for existence - if the property
277 * is set, the task will not execute. The property goes
278 * through property substitution once before testing, so if
279 * property <code>foo</code> has value <code>bar</code>, setting
280 * the "unless" condition to <code>${foo}_x</code> will mean that the
281 * task will only execute if property <code>bar_x</code> isn't set.
282 *
283 * @param property The property condition to test on execution.
284 * May be <code>null</code>, in which case
285 * no "unless" test is performed.
286 */
287 public void setUnless(String property) {
288 unlessCondition = (property == null) ? "" : property;
289 }
290
291 /**
292 * Returns the "unless" property condition of this target.
293 *
294 * @return the "unless" property condition or <code>null</code>
295 * if no "unless" condition had been defined.
296 * @since 1.6.2
297 */
298 public String getUnless() {
299 return ("".equals(unlessCondition) ? null : unlessCondition);
300 }
301
302 /**
303 * Sets the description of this target.
304 *
305 * @param description The description for this target.
306 * May be <code>null</code>, indicating that no
307 * description is available.
308 */
309 public void setDescription(String description) {
310 this.description = description;
311 }
312
313 /**
314 * Returns the description of this target.
315 *
316 * @return the description of this target, or <code>null</code> if no
317 * description is available.
318 */
319 public String getDescription() {
320 return description;
321 }
322
323 /**
324 * Returns the name of this target.
325 *
326 * @return the name of this target, or <code>null</code> if the
327 * name has not been set yet.
328 */
329 public String toString() {
330 return name;
331 }
332
333 /**
334 * Executes the target if the "if" and "unless" conditions are
335 * satisfied. Dependency checking should be done before calling this
336 * method, as it does no checking of its own. If either the "if"
337 * or "unless" test prevents this target from being executed, a verbose
338 * message is logged giving the reason. It is recommended that clients
339 * of this class call performTasks rather than this method so that
340 * appropriate build events are fired.
341 *
342 * @exception BuildException if any of the tasks fail or if a data type
343 * configuration fails.
344 *
345 * @see #performTasks()
346 * @see #setIf(String)
347 * @see #setUnless(String)
348 */
349 public void execute() throws BuildException {
350 if (testIfCondition() && testUnlessCondition()) {
351 for (int taskPosition = 0;
352 taskPosition < children.size();
353 ++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 }