source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/Project.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 80.2 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant;
19
20import java.io.File;
21import java.io.IOException;
22import java.io.EOFException;
23import java.io.InputStream;
24import java.lang.reflect.Method;
25import java.lang.reflect.Modifier;
26import java.util.Enumeration;
27import java.util.Hashtable;
28import java.util.Iterator;
29import java.util.Properties;
30import java.util.Stack;
31import java.util.Vector;
32import java.util.Set;
33import java.util.HashSet;
34import org.apache.tools.ant.input.DefaultInputHandler;
35import org.apache.tools.ant.input.InputHandler;
36import org.apache.tools.ant.loader.AntClassLoader2;
37import org.apache.tools.ant.helper.DefaultExecutor;
38import org.apache.tools.ant.types.FilterSet;
39import org.apache.tools.ant.types.FilterSetCollection;
40import org.apache.tools.ant.types.Description;
41import org.apache.tools.ant.types.Path;
42import org.apache.tools.ant.util.FileUtils;
43import org.apache.tools.ant.util.JavaEnvUtils;
44import org.apache.tools.ant.util.StringUtils;
45
46
47/**
48 * Central representation of an Ant project. This class defines an
49 * Ant project with all of its targets, tasks and various other
50 * properties. It also provides the mechanism to kick off a build using
51 * a particular target name.
52 * <p>
53 * This class also encapsulates methods which allow files to be referred
54 * to using abstract path names which are translated to native system
55 * file paths at runtime.
56 *
57 */
58
59public class Project {
60 /** Message priority of &quot;error&quot;. */
61 public static final int MSG_ERR = 0;
62 /** Message priority of &quot;warning&quot;. */
63 public static final int MSG_WARN = 1;
64 /** Message priority of &quot;information&quot;. */
65 public static final int MSG_INFO = 2;
66 /** Message priority of &quot;verbose&quot;. */
67 public static final int MSG_VERBOSE = 3;
68 /** Message priority of &quot;debug&quot;. */
69 public static final int MSG_DEBUG = 4;
70
71 /**
72 * Constant for the &quot;visiting&quot; state, used when
73 * traversing a DFS of target dependencies.
74 */
75 private static final String VISITING = "VISITING";
76 /**
77 * Constant for the &quot;visited&quot; state, used when
78 * traversing a DFS of target dependencies.
79 */
80 private static final String VISITED = "VISITED";
81
82 /**
83 * Version constant for Java 1.0 .
84 *
85 * @deprecated Use {@link JavaEnvUtils#JAVA_1_0} instead.
86 */
87 public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
88 /**
89 * Version constant for Java 1.1 .
90 *
91 * @deprecated Use {@link JavaEnvUtils#JAVA_1_1} instead.
92 */
93 public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
94 /**
95 * Version constant for Java 1.2 .
96 *
97 * @deprecated Use {@link JavaEnvUtils#JAVA_1_2} instead.
98 */
99 public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
100 /**
101 * Version constant for Java 1.3 .
102 *
103 * @deprecated Use {@link JavaEnvUtils#JAVA_1_3} instead.
104 */
105 public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
106 /**
107 * Version constant for Java 1.4 .
108 *
109 * @deprecated Use {@link JavaEnvUtils#JAVA_1_4} instead.
110 */
111 public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
112
113 /** Default filter start token. */
114 public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
115 /** Default filter end token. */
116 public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
117
118 /** Instance of a utility class to use for file operations. */
119 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
120
121 /** Name of this project. */
122 private String name;
123 /** Description for this project (if any). */
124 private String description;
125
126
127 /** Map of references within the project (paths etc) (String to Object). */
128 private Hashtable references = new AntRefTable(this);
129
130 /** Name of the project's default target. */
131 private String defaultTarget;
132
133 /** Map from target names to targets (String to Target). */
134 private Hashtable targets = new Hashtable();
135 /** Set of global filters. */
136 private FilterSet globalFilterSet = new FilterSet();
137 {
138 // Initialize the globalFileSet's project
139 globalFilterSet.setProject(this);
140 }
141
142 /**
143 * Wrapper around globalFilterSet. This collection only ever
144 * contains one FilterSet, but the wrapper is needed in order to
145 * make it easier to use the FileUtils interface.
146 */
147 private FilterSetCollection globalFilters
148 = new FilterSetCollection(globalFilterSet);
149
150 /** Project base directory. */
151 private File baseDir;
152
153 /** List of listeners to notify of build events. */
154 private Vector listeners = new Vector();
155
156 /**
157 * The Ant core classloader--may be <code>null</code> if using
158 * parent classloader.
159 */
160 private ClassLoader coreLoader = null;
161
162 /** Records the latest task to be executed on a thread (Thread to Task). */
163 private Hashtable threadTasks = new Hashtable();
164
165 /** Records the latest task to be executed on a thread Group. */
166 private Hashtable threadGroupTasks = new Hashtable();
167
168 /**
169 * Called to handle any input requests.
170 */
171 private InputHandler inputHandler = null;
172
173 /**
174 * The default input stream used to read any input.
175 */
176 private InputStream defaultInputStream = null;
177
178 /**
179 * Keep going flag.
180 */
181 private boolean keepGoingMode = false;
182
183 /**
184 * Flag which catches Listeners which try to use System.out or System.err .
185 */
186 private boolean loggingMessage = false;
187
188 /**
189 * Set the input handler.
190 *
191 * @param handler the InputHandler instance to use for gathering input.
192 */
193 public void setInputHandler(InputHandler handler) {
194 inputHandler = handler;
195 }
196
197 /**
198 * Set the default System input stream. Normally this stream is set to
199 * System.in. This inputStream is used when no task input redirection is
200 * being performed.
201 *
202 * @param defaultInputStream the default input stream to use when input
203 * is requested.
204 * @since Ant 1.6
205 */
206 public void setDefaultInputStream(InputStream defaultInputStream) {
207 this.defaultInputStream = defaultInputStream;
208 }
209
210 /**
211 * Get this project's input stream.
212 *
213 * @return the InputStream instance in use by this Project instance to
214 * read input.
215 */
216 public InputStream getDefaultInputStream() {
217 return defaultInputStream;
218 }
219
220 /**
221 * Retrieve the current input handler.
222 *
223 * @return the InputHandler instance currently in place for the project
224 * instance.
225 */
226 public InputHandler getInputHandler() {
227 return inputHandler;
228 }
229
230 /**
231 * Create a new Ant project.
232 */
233 public Project() {
234 inputHandler = new DefaultInputHandler();
235 }
236
237 /**
238 * Init a sub project--used by taskdefs.Ant .
239 * @param subProject the subproject to initialize.
240 */
241 public void initSubProject(Project subProject) {
242 ComponentHelper.getComponentHelper(subProject)
243 .initSubProject(ComponentHelper.getComponentHelper(this));
244 subProject.setKeepGoingMode(this.isKeepGoingMode());
245 subProject.setExecutor(getExecutor().getSubProjectExecutor());
246 }
247
248 /**
249 * Initialise the project.
250 *
251 * This involves setting the default task definitions and loading the
252 * system properties.
253 *
254 * @exception BuildException if the default task list cannot be loaded.
255 */
256 public void init() throws BuildException {
257 setJavaVersionProperty();
258
259 ComponentHelper.getComponentHelper(this).initDefaultDefinitions();
260
261 setSystemProperties();
262 }
263
264 /**
265 * Factory method to create a class loader for loading classes from
266 * a given path.
267 *
268 * @param path the path from which classes are to be loaded.
269 *
270 * @return an appropriate classloader.
271 */
272 public AntClassLoader createClassLoader(Path path) {
273 AntClassLoader loader = new AntClassLoader2();
274 loader.setProject(this);
275 loader.setClassPath(path);
276 return loader;
277 }
278
279 /**
280 * Set the core classloader for the project. If a <code>null</code>
281 * classloader is specified, the parent classloader should be used.
282 *
283 * @param coreLoader The classloader to use for the project.
284 * May be <code>null</code>.
285 */
286 public void setCoreLoader(ClassLoader coreLoader) {
287 this.coreLoader = coreLoader;
288 }
289
290 /**
291 * Return the core classloader to use for this project.
292 * This may be <code>null</code>, indicating that
293 * the parent classloader should be used.
294 *
295 * @return the core classloader to use for this project.
296 *
297 */
298 public ClassLoader getCoreLoader() {
299 return coreLoader;
300 }
301
302 /**
303 * Add a build listener to the list. This listener will
304 * be notified of build events for this project.
305 *
306 * @param listener The listener to add to the list.
307 * Must not be <code>null</code>.
308 */
309 public synchronized void addBuildListener(BuildListener listener) {
310 // create a new Vector to avoid ConcurrentModificationExc when
311 // the listeners get added/removed while we are in fire
312 Vector newListeners = getBuildListeners();
313 newListeners.addElement(listener);
314 listeners = newListeners;
315 }
316
317 /**
318 * Remove a build listener from the list. This listener
319 * will no longer be notified of build events for this project.
320 *
321 * @param listener The listener to remove from the list.
322 * Should not be <code>null</code>.
323 */
324 public synchronized void removeBuildListener(BuildListener listener) {
325 // create a new Vector to avoid ConcurrentModificationExc when
326 // the listeners get added/removed while we are in fire
327 Vector newListeners = getBuildListeners();
328 newListeners.removeElement(listener);
329 listeners = newListeners;
330 }
331
332 /**
333 * Return a copy of the list of build listeners for the project.
334 *
335 * @return a list of build listeners for the project
336 */
337 public Vector getBuildListeners() {
338 return (Vector) listeners.clone();
339 }
340
341 /**
342 * Write a message to the log with the default log level
343 * of MSG_INFO .
344 * @param message The text to log. Should not be <code>null</code>.
345 */
346
347 public void log(String message) {
348 log(message, MSG_INFO);
349 }
350
351 /**
352 * Write a project level message to the log with the given log level.
353 * @param message The text to log. Should not be <code>null</code>.
354 * @param msgLevel The log priority level to use.
355 */
356 public void log(String message, int msgLevel) {
357 fireMessageLogged(this, message, msgLevel);
358 }
359
360 /**
361 * Write a task level message to the log with the given log level.
362 * @param task The task to use in the log. Must not be <code>null</code>.
363 * @param message The text to log. Should not be <code>null</code>.
364 * @param msgLevel The log priority level to use.
365 */
366 public void log(Task task, String message, int msgLevel) {
367 fireMessageLogged(task, message, msgLevel);
368 }
369
370 /**
371 * Write a target level message to the log with the given log level.
372 * @param target The target to use in the log.
373 * Must not be <code>null</code>.
374 * @param message The text to log. Should not be <code>null</code>.
375 * @param msgLevel The log priority level to use.
376 */
377 public void log(Target target, String message, int msgLevel) {
378 fireMessageLogged(target, message, msgLevel);
379 }
380
381 /**
382 * Return the set of global filters.
383 *
384 * @return the set of global filters.
385 */
386 public FilterSet getGlobalFilterSet() {
387 return globalFilterSet;
388 }
389
390 /**
391 * Set a property. Any existing property of the same name
392 * is overwritten, unless it is a user property.
393 * @param name The name of property to set.
394 * Must not be <code>null</code>.
395 * @param value The new value of the property.
396 * Must not be <code>null</code>.
397 */
398 public void setProperty(String name, String value) {
399 PropertyHelper.getPropertyHelper(this).
400 setProperty(null, name, value, true);
401 }
402
403 /**
404 * Set a property if no value currently exists. If the property
405 * exists already, a message is logged and the method returns with
406 * no other effect.
407 *
408 * @param name The name of property to set.
409 * Must not be <code>null</code>.
410 * @param value The new value of the property.
411 * Must not be <code>null</code>.
412 * @since 1.5
413 */
414 public void setNewProperty(String name, String value) {
415 PropertyHelper.getPropertyHelper(this).setNewProperty(null, name,
416 value);
417 }
418
419 /**
420 * Set a user property, which cannot be overwritten by
421 * set/unset property calls. Any previous value is overwritten.
422 * @param name The name of property to set.
423 * Must not be <code>null</code>.
424 * @param value The new value of the property.
425 * Must not be <code>null</code>.
426 * @see #setProperty(String,String)
427 */
428 public void setUserProperty(String name, String value) {
429 PropertyHelper.getPropertyHelper(this).setUserProperty(null, name,
430 value);
431 }
432
433 /**
434 * Set a user property, which cannot be overwritten by set/unset
435 * property calls. Any previous value is overwritten. Also marks
436 * these properties as properties that have not come from the
437 * command line.
438 *
439 * @param name The name of property to set.
440 * Must not be <code>null</code>.
441 * @param value The new value of the property.
442 * Must not be <code>null</code>.
443 * @see #setProperty(String,String)
444 */
445 public void setInheritedProperty(String name, String value) {
446 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
447 ph.setInheritedProperty(null, name, value);
448 }
449
450 /**
451 * Set a property unless it is already defined as a user property
452 * (in which case the method returns silently).
453 *
454 * @param name The name of the property.
455 * Must not be <code>null</code>.
456 * @param value The property value. Must not be <code>null</code>.
457 */
458 private void setPropertyInternal(String name, String value) {
459 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
460 ph.setProperty(null, name, value, false);
461 }
462
463 /**
464 * Return the value of a property, if it is set.
465 *
466 * @param name The name of the property.
467 * May be <code>null</code>, in which case
468 * the return value is also <code>null</code>.
469 * @return the property value, or <code>null</code> for no match
470 * or if a <code>null</code> name is provided.
471 */
472 public String getProperty(String name) {
473 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
474 return (String) ph.getProperty(null, name);
475 }
476
477 /**
478 * Replace ${} style constructions in the given value with the
479 * string value of the corresponding data types.
480 *
481 * @param value The string to be scanned for property references.
482 * May be <code>null</code>.
483 *
484 * @return the given string with embedded property names replaced
485 * by values, or <code>null</code> if the given string is
486 * <code>null</code>.
487 *
488 * @exception BuildException if the given value has an unclosed
489 * property name, e.g. <code>${xxx</code>.
490 */
491 public String replaceProperties(String value)
492 throws BuildException {
493 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
494 return ph.replaceProperties(null, value, null);
495 }
496
497 /**
498 * Return the value of a user property, if it is set.
499 *
500 * @param name The name of the property.
501 * May be <code>null</code>, in which case
502 * the return value is also <code>null</code>.
503 * @return the property value, or <code>null</code> for no match
504 * or if a <code>null</code> name is provided.
505 */
506 public String getUserProperty(String name) {
507 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
508 return (String) ph.getUserProperty(null, name);
509 }
510
511 /**
512 * Return a copy of the properties table.
513 * @return a hashtable containing all properties
514 * (including user properties).
515 */
516 public Hashtable getProperties() {
517 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
518 return ph.getProperties();
519 }
520
521 /**
522 * Return a copy of the user property hashtable.
523 * @return a hashtable containing just the user properties.
524 */
525 public Hashtable getUserProperties() {
526 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
527 return ph.getUserProperties();
528 }
529
530 /**
531 * Copy all user properties that have been set on the command
532 * line or a GUI tool from this instance to the Project instance
533 * given as the argument.
534 *
535 * <p>To copy all &quot;user&quot; properties, you will also have to call
536 * {@link #copyInheritedProperties copyInheritedProperties}.</p>
537 *
538 * @param other the project to copy the properties to. Must not be null.
539 *
540 * @since Ant 1.5
541 */
542 public void copyUserProperties(Project other) {
543 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
544 ph.copyUserProperties(other);
545 }
546
547 /**
548 * Copy all user properties that have not been set on the
549 * command line or a GUI tool from this instance to the Project
550 * instance given as the argument.
551 *
552 * <p>To copy all &quot;user&quot; properties, you will also have to call
553 * {@link #copyUserProperties copyUserProperties}.</p>
554 *
555 * @param other the project to copy the properties to. Must not be null.
556 *
557 * @since Ant 1.5
558 */
559 public void copyInheritedProperties(Project other) {
560 PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
561 ph.copyInheritedProperties(other);
562 }
563
564 /**
565 * Set the default target of the project.
566 *
567 * @param defaultTarget The name of the default target for this project.
568 * May be <code>null</code>, indicating that there is
569 * no default target.
570 *
571 * @deprecated use setDefault
572 * @see #setDefault(String)
573 */
574 public void setDefaultTarget(String defaultTarget) {
575 this.defaultTarget = defaultTarget;
576 }
577
578 /**
579 * Return the name of the default target of the project.
580 * @return name of the default target or
581 * <code>null</code> if no default has been set.
582 */
583 public String getDefaultTarget() {
584 return defaultTarget;
585 }
586
587 /**
588 * Set the default target of the project.
589 *
590 * @param defaultTarget The name of the default target for this project.
591 * May be <code>null</code>, indicating that there is
592 * no default target.
593 */
594 public void setDefault(String defaultTarget) {
595 this.defaultTarget = defaultTarget;
596 }
597
598 /**
599 * Set the name of the project, also setting the user
600 * property <code>ant.project.name</code>.
601 *
602 * @param name The name of the project.
603 * Must not be <code>null</code>.
604 */
605 public void setName(String name) {
606 setUserProperty("ant.project.name", name);
607 this.name = name;
608 }
609
610 /**
611 * Return the project name, if one has been set.
612 *
613 * @return the project name, or <code>null</code> if it hasn't been set.
614 */
615 public String getName() {
616 return name;
617 }
618
619 /**
620 * Set the project description.
621 *
622 * @param description The description of the project.
623 * May be <code>null</code>.
624 */
625 public void setDescription(String description) {
626 this.description = description;
627 }
628
629 /**
630 * Return the project description, if one has been set.
631 *
632 * @return the project description, or <code>null</code> if it hasn't
633 * been set.
634 */
635 public String getDescription() {
636 if (description == null) {
637 description = Description.getDescription(this);
638 }
639 return description;
640 }
641
642 /**
643 * Add a filter to the set of global filters.
644 *
645 * @param token The token to filter.
646 * Must not be <code>null</code>.
647 * @param value The replacement value.
648 * Must not be <code>null</code>.
649 * @deprecated Use getGlobalFilterSet().addFilter(token,value)
650 *
651 * @see #getGlobalFilterSet()
652 * @see FilterSet#addFilter(String,String)
653 */
654 public void addFilter(String token, String value) {
655 if (token == null) {
656 return;
657 }
658 globalFilterSet.addFilter(new FilterSet.Filter(token, value));
659 }
660
661 /**
662 * Return a hashtable of global filters, mapping tokens to values.
663 *
664 * @return a hashtable of global filters, mapping tokens to values
665 * (String to String).
666 *
667 * @deprecated Use getGlobalFilterSet().getFilterHash()
668 *
669 * @see #getGlobalFilterSet()
670 * @see FilterSet#getFilterHash()
671 */
672 public Hashtable getFilters() {
673 // we need to build the hashtable dynamically
674 return globalFilterSet.getFilterHash();
675 }
676
677 /**
678 * Set the base directory for the project, checking that
679 * the given filename exists and is a directory.
680 *
681 * @param baseD The project base directory.
682 * Must not be <code>null</code>.
683 *
684 * @exception BuildException if the directory if invalid.
685 */
686 public void setBasedir(String baseD) throws BuildException {
687 setBaseDir(new File(baseD));
688 }
689
690 /**
691 * Set the base directory for the project, checking that
692 * the given file exists and is a directory.
693 *
694 * @param baseDir The project base directory.
695 * Must not be <code>null</code>.
696 * @exception BuildException if the specified file doesn't exist or
697 * isn't a directory.
698 */
699 public void setBaseDir(File baseDir) throws BuildException {
700 baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
701 if (!baseDir.exists()) {
702 throw new BuildException("Basedir " + baseDir.getAbsolutePath()
703 + " does not exist");
704 }
705 if (!baseDir.isDirectory()) {
706 throw new BuildException("Basedir " + baseDir.getAbsolutePath()
707 + " is not a directory");
708 }
709 this.baseDir = baseDir;
710 setPropertyInternal("basedir", this.baseDir.getPath());
711 String msg = "Project base dir set to: " + this.baseDir;
712 log(msg, MSG_VERBOSE);
713 }
714
715 /**
716 * Return the base directory of the project as a file object.
717 *
718 * @return the project base directory, or <code>null</code> if the
719 * base directory has not been successfully set to a valid value.
720 */
721 public File getBaseDir() {
722 if (baseDir == null) {
723 try {
724 setBasedir(".");
725 } catch (BuildException ex) {
726 ex.printStackTrace();
727 }
728 }
729 return baseDir;
730 }
731
732 /**
733 * Set &quot;keep-going&quot; mode. In this mode Ant will try to execute
734 * as many targets as possible. All targets that do not depend
735 * on failed target(s) will be executed. If the keepGoing settor/getter
736 * methods are used in conjunction with the <code>ant.executor.class</code>
737 * property, they will have no effect.
738 * @param keepGoingMode &quot;keep-going&quot; mode
739 * @since Ant 1.6
740 */
741 public void setKeepGoingMode(boolean keepGoingMode) {
742 this.keepGoingMode = keepGoingMode;
743 }
744
745 /**
746 * Return the keep-going mode. If the keepGoing settor/getter
747 * methods are used in conjunction with the <code>ant.executor.class</code>
748 * property, they will have no effect.
749 * @return &quot;keep-going&quot; mode
750 * @since Ant 1.6
751 */
752 public boolean isKeepGoingMode() {
753 return this.keepGoingMode;
754 }
755
756 /**
757 * Return the version of Java this class is running under.
758 * @return the version of Java as a String, e.g. "1.1" .
759 * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
760 * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
761 */
762 public static String getJavaVersion() {
763 return JavaEnvUtils.getJavaVersion();
764 }
765
766 /**
767 * Set the <code>ant.java.version</code> property and tests for
768 * unsupported JVM versions. If the version is supported,
769 * verbose log messages are generated to record the Java version
770 * and operating system name.
771 *
772 * @exception BuildException if this Java version is not supported.
773 *
774 * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
775 */
776 public void setJavaVersionProperty() throws BuildException {
777 String javaVersion = JavaEnvUtils.getJavaVersion();
778 setPropertyInternal("ant.java.version", javaVersion);
779
780 // sanity check
781 if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_0)) {
782 throw new BuildException("Ant cannot work on Java 1.0");
783 }
784 log("Detected Java version: " + javaVersion + " in: "
785 + System.getProperty("java.home"), MSG_VERBOSE);
786
787 log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
788 }
789
790 /**
791 * Add all system properties which aren't already defined as
792 * user properties to the project properties.
793 */
794 public void setSystemProperties() {
795 Properties systemP = System.getProperties();
796 Enumeration e = systemP.propertyNames();
797 while (e.hasMoreElements()) {
798 String propertyName = (String) e.nextElement();
799 String value = systemP.getProperty(propertyName);
800 this.setPropertyInternal(propertyName, value);
801 }
802 }
803
804 /**
805 * Add a new task definition to the project.
806 * Attempting to override an existing definition with an
807 * equivalent one (i.e. with the same classname) results in
808 * a verbose log message. Attempting to override an existing definition
809 * with a different one results in a warning log message and
810 * invalidates any tasks which have already been created with the
811 * old definition.
812 *
813 * @param taskName The name of the task to add.
814 * Must not be <code>null</code>.
815 * @param taskClass The full name of the class implementing the task.
816 * Must not be <code>null</code>.
817 *
818 * @exception BuildException if the class is unsuitable for being an Ant
819 * task. An error level message is logged before
820 * this exception is thrown.
821 *
822 * @see #checkTaskClass(Class)
823 */
824 public void addTaskDefinition(String taskName, Class taskClass)
825 throws BuildException {
826 ComponentHelper.getComponentHelper(this).addTaskDefinition(taskName,
827 taskClass);
828 }
829
830 /**
831 * Check whether or not a class is suitable for serving as Ant task.
832 * Ant task implementation classes must be public, concrete, and have
833 * a no-arg constructor.
834 *
835 * @param taskClass The class to be checked.
836 * Must not be <code>null</code>.
837 *
838 * @exception BuildException if the class is unsuitable for being an Ant
839 * task. An error level message is logged before
840 * this exception is thrown.
841 */
842 public void checkTaskClass(final Class taskClass) throws BuildException {
843 ComponentHelper.getComponentHelper(this).checkTaskClass(taskClass);
844
845 if (!Modifier.isPublic(taskClass.getModifiers())) {
846 final String message = taskClass + " is not public";
847 log(message, Project.MSG_ERR);
848 throw new BuildException(message);
849 }
850 if (Modifier.isAbstract(taskClass.getModifiers())) {
851 final String message = taskClass + " is abstract";
852 log(message, Project.MSG_ERR);
853 throw new BuildException(message);
854 }
855 try {
856 taskClass.getConstructor((Class[]) null);
857 // don't have to check for public, since
858 // getConstructor finds public constructors only.
859 } catch (NoSuchMethodException e) {
860 final String message = "No public no-arg constructor in "
861 + taskClass;
862 log(message, Project.MSG_ERR);
863 throw new BuildException(message);
864 } catch (LinkageError e) {
865 String message = "Could not load " + taskClass + ": " + e;
866 log(message, Project.MSG_ERR);
867 throw new BuildException(message, e);
868 }
869 if (!Task.class.isAssignableFrom(taskClass)) {
870 TaskAdapter.checkTaskClass(taskClass, this);
871 }
872 }
873
874 /**
875 * Return the current task definition hashtable. The returned hashtable is
876 * &quot;live&quot; and so should not be modified.
877 *
878 * @return a map of from task name to implementing class
879 * (String to Class).
880 */
881 public Hashtable getTaskDefinitions() {
882 return ComponentHelper.getComponentHelper(this).getTaskDefinitions();
883 }
884
885 /**
886 * Add a new datatype definition.
887 * Attempting to override an existing definition with an
888 * equivalent one (i.e. with the same classname) results in
889 * a verbose log message. Attempting to override an existing definition
890 * with a different one results in a warning log message, but the
891 * definition is changed.
892 *
893 * @param typeName The name of the datatype.
894 * Must not be <code>null</code>.
895 * @param typeClass The full name of the class implementing the datatype.
896 * Must not be <code>null</code>.
897 */
898 public void addDataTypeDefinition(String typeName, Class typeClass) {
899 ComponentHelper.getComponentHelper(this).addDataTypeDefinition(typeName,
900 typeClass);
901 }
902
903 /**
904 * Return the current datatype definition hashtable. The returned
905 * hashtable is &quot;live&quot; and so should not be modified.
906 *
907 * @return a map of from datatype name to implementing class
908 * (String to Class).
909 */
910 public Hashtable getDataTypeDefinitions() {
911 return ComponentHelper.getComponentHelper(this).getDataTypeDefinitions();
912 }
913
914 /**
915 * Add a <em>new</em> target to the project.
916 *
917 * @param target The target to be added to the project.
918 * Must not be <code>null</code>.
919 *
920 * @exception BuildException if the target already exists in the project
921 *
922 * @see Project#addOrReplaceTarget
923 */
924 public void addTarget(Target target) throws BuildException {
925 addTarget(target.getName(), target);
926 }
927
928 /**
929 * Add a <em>new</em> target to the project.
930 *
931 * @param targetName The name to use for the target.
932 * Must not be <code>null</code>.
933 * @param target The target to be added to the project.
934 * Must not be <code>null</code>.
935 *
936 * @exception BuildException if the target already exists in the project.
937 *
938 * @see Project#addOrReplaceTarget
939 */
940 public void addTarget(String targetName, Target target)
941 throws BuildException {
942 if (targets.get(targetName) != null) {
943 throw new BuildException("Duplicate target: `" + targetName + "'");
944 }
945 addOrReplaceTarget(targetName, target);
946 }
947
948 /**
949 * Add a target to the project, or replaces one with the same
950 * name.
951 *
952 * @param target The target to be added or replaced in the project.
953 * Must not be <code>null</code>.
954 */
955 public void addOrReplaceTarget(Target target) {
956 addOrReplaceTarget(target.getName(), target);
957 }
958
959 /**
960 * Add a target to the project, or replaces one with the same
961 * name.
962 *
963 * @param targetName The name to use for the target.
964 * Must not be <code>null</code>.
965 * @param target The target to be added or replaced in the project.
966 * Must not be <code>null</code>.
967 */
968 public void addOrReplaceTarget(String targetName, Target target) {
969 String msg = " +Target: " + targetName;
970 log(msg, MSG_DEBUG);
971 target.setProject(this);
972 targets.put(targetName, target);
973 }
974
975 /**
976 * Return the hashtable of targets. The returned hashtable
977 * is &quot;live&quot; and so should not be modified.
978 * @return a map from name to target (String to Target).
979 */
980 public Hashtable getTargets() {
981 return targets;
982 }
983
984 /**
985 * Create a new instance of a task, adding it to a list of
986 * created tasks for later invalidation. This causes all tasks
987 * to be remembered until the containing project is removed
988 * @param taskType The name of the task to create an instance of.
989 * Must not be <code>null</code>.
990 *
991 * @return an instance of the specified task, or <code>null</code> if
992 * the task name is not recognised.
993 *
994 * @exception BuildException if the task name is recognised but task
995 * creation fails.
996 */
997 public Task createTask(String taskType) throws BuildException {
998 return ComponentHelper.getComponentHelper(this).createTask(taskType);
999 }
1000
1001 /**
1002 * Create a new instance of a data type.
1003 *
1004 * @param typeName The name of the data type to create an instance of.
1005 * Must not be <code>null</code>.
1006 *
1007 * @return an instance of the specified data type, or <code>null</code> if
1008 * the data type name is not recognised.
1009 *
1010 * @exception BuildException if the data type name is recognised but
1011 * instance creation fails.
1012 */
1013 public Object createDataType(String typeName) throws BuildException {
1014 return ComponentHelper.getComponentHelper(this).createDataType(typeName);
1015 }
1016
1017 /**
1018 * Set the Executor instance for this Project.
1019 * @param e the Executor to use.
1020 */
1021 public void setExecutor(Executor e) {
1022 addReference("ant.executor", e);
1023 }
1024
1025 /**
1026 * Get this Project's Executor (setting it if necessary).
1027 * @return an Executor instance.
1028 */
1029 public Executor getExecutor() {
1030 Object o = getReference("ant.executor");
1031 if (o == null) {
1032 String classname = getProperty("ant.executor.class");
1033 if (classname == null) {
1034 classname = DefaultExecutor.class.getName();
1035 }
1036 log("Attempting to create object of type " + classname, MSG_DEBUG);
1037 try {
1038 o = Class.forName(classname, true, coreLoader).newInstance();
1039 } catch (ClassNotFoundException seaEnEfEx) {
1040 //try the current classloader
1041 try {
1042 o = Class.forName(classname).newInstance();
1043 } catch (Exception ex) {
1044 log(ex.toString(), MSG_ERR);
1045 }
1046 } catch (Exception ex) {
1047 log(ex.toString(), MSG_ERR);
1048 }
1049 if (o == null) {
1050 throw new BuildException(
1051 "Unable to obtain a Target Executor instance.");
1052 }
1053 setExecutor((Executor) o);
1054 }
1055 return (Executor) o;
1056 }
1057
1058 /**
1059 * Execute the specified sequence of targets, and the targets
1060 * they depend on.
1061 *
1062 * @param names A vector of target name strings to execute.
1063 * Must not be <code>null</code>.
1064 *
1065 * @exception BuildException if the build failed.
1066 */
1067 public void executeTargets(Vector names) throws BuildException {
1068 getExecutor().executeTargets(this,
1069 (String[]) (names.toArray(new String[names.size()])));
1070 }
1071
1072 /**
1073 * Demultiplex output so that each task receives the appropriate
1074 * messages. If the current thread is not currently executing a task,
1075 * the message is logged directly.
1076 *
1077 * @param output Message to handle. Should not be <code>null</code>.
1078 * @param isWarning Whether the text represents an warning (<code>true</code>)
1079 * or information (<code>false</code>).
1080 */
1081 public void demuxOutput(String output, boolean isWarning) {
1082 Task task = getThreadTask(Thread.currentThread());
1083 if (task == null) {
1084 log(output, isWarning ? MSG_WARN : MSG_INFO);
1085 } else {
1086 if (isWarning) {
1087 task.handleErrorOutput(output);
1088 } else {
1089 task.handleOutput(output);
1090 }
1091 }
1092 }
1093
1094 /**
1095 * Read data from the default input stream. If no default has been
1096 * specified, System.in is used.
1097 *
1098 * @param buffer the buffer into which data is to be read.
1099 * @param offset the offset into the buffer at which data is stored.
1100 * @param length the amount of data to read.
1101 *
1102 * @return the number of bytes read.
1103 *
1104 * @exception IOException if the data cannot be read.
1105 * @since Ant 1.6
1106 */
1107 public int defaultInput(byte[] buffer, int offset, int length)
1108 throws IOException {
1109 if (defaultInputStream != null) {
1110 System.out.flush();
1111 return defaultInputStream.read(buffer, offset, length);
1112 } else {
1113 throw new EOFException("No input provided for project");
1114 }
1115 }
1116
1117 /**
1118 * Demux an input request to the correct task.
1119 *
1120 * @param buffer the buffer into which data is to be read.
1121 * @param offset the offset into the buffer at which data is stored.
1122 * @param length the amount of data to read.
1123 *
1124 * @return the number of bytes read.
1125 *
1126 * @exception IOException if the data cannot be read.
1127 * @since Ant 1.6
1128 */
1129 public int demuxInput(byte[] buffer, int offset, int length)
1130 throws IOException {
1131 Task task = getThreadTask(Thread.currentThread());
1132 if (task == null) {
1133 return defaultInput(buffer, offset, length);
1134 } else {
1135 return task.handleInput(buffer, offset, length);
1136 }
1137 }
1138
1139 /**
1140 * Demultiplex flush operations so that each task receives the appropriate
1141 * messages. If the current thread is not currently executing a task,
1142 * the message is logged directly.
1143 *
1144 * @since Ant 1.5.2
1145 *
1146 * @param output Message to handle. Should not be <code>null</code>.
1147 * @param isError Whether the text represents an error (<code>true</code>)
1148 * or information (<code>false</code>).
1149 */
1150 public void demuxFlush(String output, boolean isError) {
1151 Task task = getThreadTask(Thread.currentThread());
1152 if (task == null) {
1153 fireMessageLogged(this, output, isError ? MSG_ERR : MSG_INFO);
1154 } else {
1155 if (isError) {
1156 task.handleErrorFlush(output);
1157 } else {
1158 task.handleFlush(output);
1159 }
1160 }
1161 }
1162
1163 /**
1164 * Execute the specified target and any targets it depends on.
1165 *
1166 * @param targetName The name of the target to execute.
1167 * Must not be <code>null</code>.
1168 *
1169 * @exception BuildException if the build failed.
1170 */
1171 public void executeTarget(String targetName) throws BuildException {
1172
1173 // sanity check ourselves, if we've been asked to build nothing
1174 // then we should complain
1175
1176 if (targetName == null) {
1177 String msg = "No target specified";
1178 throw new BuildException(msg);
1179 }
1180
1181 // Sort and run the dependency tree.
1182 // Sorting checks if all the targets (and dependencies)
1183 // exist, and if there is any cycle in the dependency
1184 // graph.
1185 executeSortedTargets(topoSort(targetName, targets, false));
1186 }
1187
1188 /**
1189 * Execute a <code>Vector</code> of sorted targets.
1190 * @param sortedTargets the aforementioned <code>Vector</code>.
1191 * @throws BuildException on error.
1192 */
1193 public void executeSortedTargets(Vector sortedTargets)
1194 throws BuildException {
1195 Set succeededTargets = new HashSet();
1196 BuildException buildException = null; // first build exception
1197 for (Enumeration iter = sortedTargets.elements();
1198 iter.hasMoreElements();) {
1199 Target curtarget = (Target) iter.nextElement();
1200 boolean canExecute = true;
1201 for (Enumeration depIter = curtarget.getDependencies();
1202 depIter.hasMoreElements();) {
1203 String dependencyName = ((String) depIter.nextElement());
1204 if (!succeededTargets.contains(dependencyName)) {
1205 canExecute = false;
1206 log(curtarget,
1207 "Cannot execute '" + curtarget.getName() + "' - '"
1208 + dependencyName + "' failed or was not executed.",
1209 MSG_ERR);
1210 break;
1211 }
1212 }
1213 if (canExecute) {
1214 Throwable thrownException = null;
1215 try {
1216 curtarget.performTasks();
1217 succeededTargets.add(curtarget.getName());
1218 } catch (RuntimeException ex) {
1219 if (!(keepGoingMode)) {
1220 throw ex; // throw further
1221 }
1222 thrownException = ex;
1223 } catch (Throwable ex) {
1224 if (!(keepGoingMode)) {
1225 throw new BuildException(ex);
1226 }
1227 thrownException = ex;
1228 }
1229 if (thrownException != null) {
1230 if (thrownException instanceof BuildException) {
1231 log(curtarget,
1232 "Target '" + curtarget.getName()
1233 + "' failed with message '"
1234 + thrownException.getMessage() + "'.", MSG_ERR);
1235 // only the first build exception is reported
1236 if (buildException == null) {
1237 buildException = (BuildException) thrownException;
1238 }
1239 } else {
1240 log(curtarget,
1241 "Target '" + curtarget.getName()
1242 + "' failed with message '"
1243 + thrownException.getMessage() + "'.", MSG_ERR);
1244 thrownException.printStackTrace(System.err);
1245 if (buildException == null) {
1246 buildException =
1247 new BuildException(thrownException);
1248 }
1249 }
1250 }
1251 }
1252 }
1253 if (buildException != null) {
1254 throw buildException;
1255 }
1256 }
1257
1258 /**
1259 * Return the canonical form of a filename.
1260 * <p>
1261 * If the specified file name is relative it is resolved
1262 * with respect to the given root directory.
1263 *
1264 * @param fileName The name of the file to resolve.
1265 * Must not be <code>null</code>.
1266 *
1267 * @param rootDir The directory respective to which relative file names
1268 * are resolved. May be <code>null</code>, in which case
1269 * the current directory is used.
1270 *
1271 * @return the resolved File.
1272 *
1273 * @deprecated
1274 */
1275 public File resolveFile(String fileName, File rootDir) {
1276 return FILE_UTILS.resolveFile(rootDir, fileName);
1277 }
1278
1279 /**
1280 * Return the canonical form of a filename.
1281 * <p>
1282 * If the specified file name is relative it is resolved
1283 * with respect to the project's base directory.
1284 *
1285 * @param fileName The name of the file to resolve.
1286 * Must not be <code>null</code>.
1287 *
1288 * @return the resolved File.
1289 *
1290 */
1291 public File resolveFile(String fileName) {
1292 return FILE_UTILS.resolveFile(baseDir, fileName);
1293 }
1294
1295 /**
1296 * Translate a path into its native (platform specific) format.
1297 * <p>
1298 * This method uses PathTokenizer to separate the input path
1299 * into its components. This handles DOS style paths in a relatively
1300 * sensible way. The file separators are then converted to their platform
1301 * specific versions.
1302 *
1303 * @param toProcess The path to be translated.
1304 * May be <code>null</code>.
1305 *
1306 * @return the native version of the specified path or
1307 * an empty string if the path is <code>null</code> or empty.
1308 *
1309 * @see PathTokenizer
1310 */
1311 public static String translatePath(String toProcess) {
1312 if (toProcess == null || toProcess.length() == 0) {
1313 return "";
1314 }
1315 StringBuffer path = new StringBuffer(toProcess.length() + 50);
1316 PathTokenizer tokenizer = new PathTokenizer(toProcess);
1317 while (tokenizer.hasMoreTokens()) {
1318 String pathComponent = tokenizer.nextToken();
1319 pathComponent = pathComponent.replace('/', File.separatorChar);
1320 pathComponent = pathComponent.replace('\\', File.separatorChar);
1321 if (path.length() != 0) {
1322 path.append(File.pathSeparatorChar);
1323 }
1324 path.append(pathComponent);
1325 }
1326 return path.toString();
1327 }
1328
1329 /**
1330 * Convenience method to copy a file from a source to a destination.
1331 * No filtering is performed.
1332 *
1333 * @param sourceFile Name of file to copy from.
1334 * Must not be <code>null</code>.
1335 * @param destFile Name of file to copy to.
1336 * Must not be <code>null</code>.
1337 *
1338 * @exception IOException if the copying fails.
1339 *
1340 * @deprecated
1341 */
1342 public void copyFile(String sourceFile, String destFile)
1343 throws IOException {
1344 FILE_UTILS.copyFile(sourceFile, destFile);
1345 }
1346
1347 /**
1348 * Convenience method to copy a file from a source to a destination
1349 * specifying if token filtering should be used.
1350 *
1351 * @param sourceFile Name of file to copy from.
1352 * Must not be <code>null</code>.
1353 * @param destFile Name of file to copy to.
1354 * Must not be <code>null</code>.
1355 * @param filtering Whether or not token filtering should be used during
1356 * the copy.
1357 *
1358 * @exception IOException if the copying fails.
1359 *
1360 * @deprecated
1361 */
1362 public void copyFile(String sourceFile, String destFile, boolean filtering)
1363 throws IOException {
1364 FILE_UTILS.copyFile(sourceFile, destFile,
1365 filtering ? globalFilters : null);
1366 }
1367
1368 /**
1369 * Convenience method to copy a file from a source to a
1370 * destination specifying if token filtering should be used and if
1371 * source files may overwrite newer destination files.
1372 *
1373 * @param sourceFile Name of file to copy from.
1374 * Must not be <code>null</code>.
1375 * @param destFile Name of file to copy to.
1376 * Must not be <code>null</code>.
1377 * @param filtering Whether or not token filtering should be used during
1378 * the copy.
1379 * @param overwrite Whether or not the destination file should be
1380 * overwritten if it already exists.
1381 *
1382 * @exception IOException if the copying fails.
1383 *
1384 * @deprecated
1385 */
1386 public void copyFile(String sourceFile, String destFile, boolean filtering,
1387 boolean overwrite) throws IOException {
1388 FILE_UTILS.copyFile(sourceFile, destFile,
1389 filtering ? globalFilters : null, overwrite);
1390 }
1391
1392 /**
1393 * Convenience method to copy a file from a source to a
1394 * destination specifying if token filtering should be used, if
1395 * source files may overwrite newer destination files, and if the
1396 * last modified time of the resulting file should be set to
1397 * that of the source file.
1398 *
1399 * @param sourceFile Name of file to copy from.
1400 * Must not be <code>null</code>.
1401 * @param destFile Name of file to copy to.
1402 * Must not be <code>null</code>.
1403 * @param filtering Whether or not token filtering should be used during
1404 * the copy.
1405 * @param overwrite Whether or not the destination file should be
1406 * overwritten if it already exists.
1407 * @param preserveLastModified Whether or not the last modified time of
1408 * the resulting file should be set to that
1409 * of the source file.
1410 *
1411 * @exception IOException if the copying fails.
1412 *
1413 * @deprecated
1414 */
1415 public void copyFile(String sourceFile, String destFile, boolean filtering,
1416 boolean overwrite, boolean preserveLastModified)
1417 throws IOException {
1418 FILE_UTILS.copyFile(sourceFile, destFile,
1419 filtering ? globalFilters : null, overwrite, preserveLastModified);
1420 }
1421
1422 /**
1423 * Convenience method to copy a file from a source to a destination.
1424 * No filtering is performed.
1425 *
1426 * @param sourceFile File to copy from.
1427 * Must not be <code>null</code>.
1428 * @param destFile File to copy to.
1429 * Must not be <code>null</code>.
1430 *
1431 * @exception IOException if the copying fails.
1432 *
1433 * @deprecated
1434 */
1435 public void copyFile(File sourceFile, File destFile) throws IOException {
1436 FILE_UTILS.copyFile(sourceFile, destFile);
1437 }
1438
1439 /**
1440 * Convenience method to copy a file from a source to a destination
1441 * specifying if token filtering should be used.
1442 *
1443 * @param sourceFile File to copy from.
1444 * Must not be <code>null</code>.
1445 * @param destFile File to copy to.
1446 * Must not be <code>null</code>.
1447 * @param filtering Whether or not token filtering should be used during
1448 * the copy.
1449 *
1450 * @exception IOException if the copying fails.
1451 *
1452 * @deprecated
1453 */
1454 public void copyFile(File sourceFile, File destFile, boolean filtering)
1455 throws IOException {
1456 FILE_UTILS.copyFile(sourceFile, destFile,
1457 filtering ? globalFilters : null);
1458 }
1459
1460 /**
1461 * Convenience method to copy a file from a source to a
1462 * destination specifying if token filtering should be used and if
1463 * source files may overwrite newer destination files.
1464 *
1465 * @param sourceFile File to copy from.
1466 * Must not be <code>null</code>.
1467 * @param destFile File to copy to.
1468 * Must not be <code>null</code>.
1469 * @param filtering Whether or not token filtering should be used during
1470 * the copy.
1471 * @param overwrite Whether or not the destination file should be
1472 * overwritten if it already exists.
1473 *
1474 * @exception IOException if the file cannot be copied.
1475 *
1476 * @deprecated
1477 */
1478 public void copyFile(File sourceFile, File destFile, boolean filtering,
1479 boolean overwrite) throws IOException {
1480 FILE_UTILS.copyFile(sourceFile, destFile,
1481 filtering ? globalFilters : null, overwrite);
1482 }
1483
1484 /**
1485 * Convenience method to copy a file from a source to a
1486 * destination specifying if token filtering should be used, if
1487 * source files may overwrite newer destination files, and if the
1488 * last modified time of the resulting file should be set to
1489 * that of the source file.
1490 *
1491 * @param sourceFile File to copy from.
1492 * Must not be <code>null</code>.
1493 * @param destFile File to copy to.
1494 * Must not be <code>null</code>.
1495 * @param filtering Whether or not token filtering should be used during
1496 * the copy.
1497 * @param overwrite Whether or not the destination file should be
1498 * overwritten if it already exists.
1499 * @param preserveLastModified Whether or not the last modified time of
1500 * the resulting file should be set to that
1501 * of the source file.
1502 *
1503 * @exception IOException if the file cannot be copied.
1504 *
1505 * @deprecated
1506 */
1507 public void copyFile(File sourceFile, File destFile, boolean filtering,
1508 boolean overwrite, boolean preserveLastModified)
1509 throws IOException {
1510 FILE_UTILS.copyFile(sourceFile, destFile,
1511 filtering ? globalFilters : null, overwrite, preserveLastModified);
1512 }
1513
1514 /**
1515 * Call File.setLastModified(long time) on Java above 1.1, and logs
1516 * a warning on Java 1.1.
1517 *
1518 * @param file The file to set the last modified time on.
1519 * Must not be <code>null</code>.
1520 *
1521 * @param time the required modification time.
1522 *
1523 * @deprecated
1524 *
1525 * @exception BuildException if the last modified time cannot be set
1526 * despite running on a platform with a version
1527 * above 1.1.
1528 */
1529 public void setFileLastModified(File file, long time)
1530 throws BuildException {
1531 FILE_UTILS.setFileLastModified(file, time);
1532 log("Setting modification time for " + file, MSG_VERBOSE);
1533 }
1534
1535 /**
1536 * Return the boolean equivalent of a string, which is considered
1537 * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
1538 * or <code>"yes"</code> is found, ignoring case.
1539 *
1540 * @param s The string to convert to a boolean value.
1541 *
1542 * @return <code>true</code> if the given string is <code>"on"</code>,
1543 * <code>"true"</code> or <code>"yes"</code>, or
1544 * <code>false</code> otherwise.
1545 */
1546 public static boolean toBoolean(String s) {
1547 return ("on".equalsIgnoreCase(s)
1548 || "true".equalsIgnoreCase(s)
1549 || "yes".equalsIgnoreCase(s));
1550 }
1551
1552 /**
1553 * Topologically sort a set of targets. Equivalent to calling
1554 * <code>topoSort(new String[] {root}, targets, true)</code>.
1555 *
1556 * @param root The name of the root target. The sort is created in such
1557 * a way that the sequence of Targets up to the root
1558 * target is the minimum possible such sequence.
1559 * Must not be <code>null</code>.
1560 * @param targets A Hashtable mapping names to Targets.
1561 * Must not be <code>null</code>.
1562 * @return a Vector of ALL Target objects in sorted order.
1563 * @exception BuildException if there is a cyclic dependency among the
1564 * targets, or if a named target does not exist.
1565 */
1566 public final Vector topoSort(String root, Hashtable targets)
1567 throws BuildException {
1568 return topoSort(new String[] {root}, targets, true);
1569 }
1570
1571 /**
1572 * Topologically sort a set of targets. Equivalent to calling
1573 * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
1574 *
1575 * @param root The name of the root target. The sort is created in such
1576 * a way that the sequence of Targets up to the root
1577 * target is the minimum possible such sequence.
1578 * Must not be <code>null</code>.
1579 * @param targets A Hashtable mapping names to Targets.
1580 * Must not be <code>null</code>.
1581 * @param returnAll <code>boolean</code> indicating whether to return all
1582 * targets, or the execution sequence only.
1583 * @return a Vector of Target objects in sorted order.
1584 * @exception BuildException if there is a cyclic dependency among the
1585 * targets, or if a named target does not exist.
1586 * @since Ant 1.6.3
1587 */
1588 public final Vector topoSort(String root, Hashtable targets,
1589 boolean returnAll) throws BuildException {
1590 return topoSort(new String[] {root}, targets, returnAll);
1591 }
1592
1593 /**
1594 * Topologically sort a set of targets.
1595 *
1596 * @param root <code>String[]</code> containing the names of the root targets.
1597 * The sort is created in such a way that the ordered sequence of
1598 * Targets is the minimum possible such sequence to the specified
1599 * root targets.
1600 * Must not be <code>null</code>.
1601 * @param targets A map of names to targets (String to Target).
1602 * Must not be <code>null</code>.
1603 * @param returnAll <code>boolean</code> indicating whether to return all
1604 * targets, or the execution sequence only.
1605 * @return a Vector of Target objects in sorted order.
1606 * @exception BuildException if there is a cyclic dependency among the
1607 * targets, or if a named target does not exist.
1608 * @since Ant 1.6.3
1609 */
1610 public final Vector topoSort(String[] root, Hashtable targets,
1611 boolean returnAll) throws BuildException {
1612 Vector ret = new Vector();
1613 Hashtable state = new Hashtable();
1614 Stack visiting = new Stack();
1615
1616 // We first run a DFS based sort using each root as a starting node.
1617 // This creates the minimum sequence of Targets to the root node(s).
1618 // We then do a sort on any remaining unVISITED targets.
1619 // This is unnecessary for doing our build, but it catches
1620 // circular dependencies or missing Targets on the entire
1621 // dependency tree, not just on the Targets that depend on the
1622 // build Target.
1623
1624 for (int i = 0; i < root.length; i++) {
1625 String st = (String) (state.get(root[i]));
1626 if (st == null) {
1627 tsort(root[i], targets, state, visiting, ret);
1628 } else if (st == VISITING) {
1629 throw new RuntimeException("Unexpected node in visiting state: "
1630 + root[i]);
1631 }
1632 }
1633 StringBuffer buf = new StringBuffer("Build sequence for target(s)");
1634
1635 for (int j = 0; j < root.length; j++) {
1636 buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
1637 }
1638 buf.append(" is " + ret);
1639 log(buf.toString(), MSG_VERBOSE);
1640
1641 Vector complete = (returnAll) ? ret : new Vector(ret);
1642 for (Enumeration en = targets.keys(); en.hasMoreElements();) {
1643 String curTarget = (String) en.nextElement();
1644 String st = (String) state.get(curTarget);
1645 if (st == null) {
1646 tsort(curTarget, targets, state, visiting, complete);
1647 } else if (st == VISITING) {
1648 throw new RuntimeException("Unexpected node in visiting state: "
1649 + curTarget);
1650 }
1651 }
1652 log("Complete build sequence is " + complete, MSG_VERBOSE);
1653 return ret;
1654 }
1655
1656 /**
1657 * Perform a single step in a recursive depth-first-search traversal of
1658 * the target dependency tree.
1659 * <p>
1660 * The current target is first set to the &quot;visiting&quot; state, and
1661 * pushed onto the &quot;visiting&quot; stack.
1662 * <p>
1663 * An exception is then thrown if any child of the current node is in the
1664 * visiting state, as that implies a circular dependency. The exception
1665 * contains details of the cycle, using elements of the &quot;visiting&quot;
1666 * stack.
1667 * <p>
1668 * If any child has not already been &quot;visited&quot;, this method is
1669 * called recursively on it.
1670 * <p>
1671 * The current target is then added to the ordered list of targets. Note
1672 * that this is performed after the children have been visited in order
1673 * to get the correct order. The current target is set to the
1674 * &quot;visited&quot; state.
1675 * <p>
1676 * By the time this method returns, the ordered list contains the sequence
1677 * of targets up to and including the current target.
1678 *
1679 * @param root The current target to inspect.
1680 * Must not be <code>null</code>.
1681 * @param targets A mapping from names to targets (String to Target).
1682 * Must not be <code>null</code>.
1683 * @param state A mapping from target names to states (String to String).
1684 * The states in question are &quot;VISITING&quot; and
1685 * &quot;VISITED&quot;. Must not be <code>null</code>.
1686 * @param visiting A stack of targets which are currently being visited.
1687 * Must not be <code>null</code>.
1688 * @param ret The list to add target names to. This will end up
1689 * containing the complete list of dependencies in
1690 * dependency order.
1691 * Must not be <code>null</code>.
1692 *
1693 * @exception BuildException if a non-existent target is specified or if
1694 * a circular dependency is detected.
1695 */
1696 private void tsort(String root, Hashtable targets,
1697 Hashtable state, Stack visiting,
1698 Vector ret)
1699 throws BuildException {
1700 state.put(root, VISITING);
1701 visiting.push(root);
1702
1703 Target target = (Target) targets.get(root);
1704
1705 // Make sure we exist
1706 if (target == null) {
1707 StringBuffer sb = new StringBuffer("Target `");
1708 sb.append(root);
1709 sb.append("' does not exist in this project. ");
1710 visiting.pop();
1711 if (!visiting.empty()) {
1712 String parent = (String) visiting.peek();
1713 sb.append("It is used from target `");
1714 sb.append(parent);
1715 sb.append("'.");
1716 }
1717 throw new BuildException(new String(sb));
1718 }
1719 for (Enumeration en = target.getDependencies(); en.hasMoreElements();) {
1720 String cur = (String) en.nextElement();
1721 String m = (String) state.get(cur);
1722 if (m == null) {
1723 // Not been visited
1724 tsort(cur, targets, state, visiting, ret);
1725 } else if (m == VISITING) {
1726 // Currently visiting this node, so have a cycle
1727 throw makeCircularException(cur, visiting);
1728 }
1729 }
1730 String p = (String) visiting.pop();
1731 if (root != p) {
1732 throw new RuntimeException("Unexpected internal error: expected to "
1733 + "pop " + root + " but got " + p);
1734 }
1735 state.put(root, VISITED);
1736 ret.addElement(target);
1737 }
1738
1739 /**
1740 * Build an appropriate exception detailing a specified circular
1741 * dependency.
1742 *
1743 * @param end The dependency to stop at. Must not be <code>null</code>.
1744 * @param stk A stack of dependencies. Must not be <code>null</code>.
1745 *
1746 * @return a BuildException detailing the specified circular dependency.
1747 */
1748 private static BuildException makeCircularException(String end, Stack stk) {
1749 StringBuffer sb = new StringBuffer("Circular dependency: ");
1750 sb.append(end);
1751 String c;
1752 do {
1753 c = (String) stk.pop();
1754 sb.append(" <- ");
1755 sb.append(c);
1756 } while (!c.equals(end));
1757 return new BuildException(new String(sb));
1758 }
1759
1760 /**
1761 * Add a reference to the project.
1762 *
1763 * @param name The name of the reference. Must not be <code>null</code>.
1764 * @param value The value of the reference. Must not be <code>null</code>.
1765 */
1766 public void addReference(String name, Object value) {
1767 synchronized (references) {
1768 Object old = ((AntRefTable) references).getReal(name);
1769 if (old == value) {
1770 // no warning, this is not changing anything
1771 return;
1772 }
1773 if (old != null && !(old instanceof UnknownElement)) {
1774 log("Overriding previous definition of reference to " + name,
1775 MSG_WARN);
1776 }
1777 log("Adding reference: " + name, MSG_DEBUG);
1778 references.put(name, value);
1779 }
1780 }
1781
1782 /**
1783 * Return a map of the references in the project (String to Object).
1784 * The returned hashtable is &quot;live&quot; and so must not be modified.
1785 *
1786 * @return a map of the references in the project (String to Object).
1787 */
1788 public Hashtable getReferences() {
1789 return references;
1790 }
1791
1792 /**
1793 * Look up a reference by its key (ID).
1794 *
1795 * @param key The key for the desired reference.
1796 * Must not be <code>null</code>.
1797 *
1798 * @return the reference with the specified ID, or <code>null</code> if
1799 * there is no such reference in the project.
1800 */
1801 public Object getReference(String key) {
1802 return references.get(key);
1803 }
1804
1805 /**
1806 * Return a description of the type of the given element, with
1807 * special handling for instances of tasks and data types.
1808 * <p>
1809 * This is useful for logging purposes.
1810 *
1811 * @param element The element to describe.
1812 * Must not be <code>null</code>.
1813 *
1814 * @return a description of the element type.
1815 *
1816 * @since 1.95, Ant 1.5
1817 */
1818 public String getElementName(Object element) {
1819 return ComponentHelper.getComponentHelper(this).getElementName(element);
1820 }
1821
1822 /**
1823 * Send a &quot;build started&quot; event
1824 * to the build listeners for this project.
1825 */
1826 public void fireBuildStarted() {
1827 BuildEvent event = new BuildEvent(this);
1828 Iterator iter = listeners.iterator();
1829 while (iter.hasNext()) {
1830 BuildListener listener = (BuildListener) iter.next();
1831 listener.buildStarted(event);
1832 }
1833 }
1834
1835 /**
1836 * Send a &quot;build finished&quot; event to the build listeners
1837 * for this project.
1838 * @param exception an exception indicating a reason for a build
1839 * failure. May be <code>null</code>, indicating
1840 * a successful build.
1841 */
1842 public void fireBuildFinished(Throwable exception) {
1843 BuildEvent event = new BuildEvent(this);
1844 event.setException(exception);
1845 Iterator iter = listeners.iterator();
1846 while (iter.hasNext()) {
1847 BuildListener listener = (BuildListener) iter.next();
1848 listener.buildFinished(event);
1849 }
1850 }
1851
1852 /**
1853 * Send a &quot;subbuild started&quot; event to the build listeners for
1854 * this project.
1855 *
1856 * @since Ant 1.6.2
1857 */
1858 public void fireSubBuildStarted() {
1859 BuildEvent event = new BuildEvent(this);
1860 Iterator iter = listeners.iterator();
1861 while (iter.hasNext()) {
1862 Object listener = iter.next();
1863 if (listener instanceof SubBuildListener) {
1864 ((SubBuildListener) listener).subBuildStarted(event);
1865 }
1866 }
1867 }
1868
1869 /**
1870 * Send a &quot;subbuild finished&quot; event to the build listeners for
1871 * this project.
1872 * @param exception an exception indicating a reason for a build
1873 * failure. May be <code>null</code>, indicating
1874 * a successful build.
1875 *
1876 * @since Ant 1.6.2
1877 */
1878 public void fireSubBuildFinished(Throwable exception) {
1879 BuildEvent event = new BuildEvent(this);
1880 event.setException(exception);
1881 Iterator iter = listeners.iterator();
1882 while (iter.hasNext()) {
1883 Object listener = iter.next();
1884 if (listener instanceof SubBuildListener) {
1885 ((SubBuildListener) listener).subBuildFinished(event);
1886 }
1887 }
1888 }
1889
1890 /**
1891 * Send a &quot;target started&quot; event to the build listeners
1892 * for this project.
1893 *
1894 * @param target The target which is starting to build.
1895 * Must not be <code>null</code>.
1896 */
1897 protected void fireTargetStarted(Target target) {
1898 BuildEvent event = new BuildEvent(target);
1899 Iterator iter = listeners.iterator();
1900 while (iter.hasNext()) {
1901 BuildListener listener = (BuildListener) iter.next();
1902 listener.targetStarted(event);
1903 }
1904 }
1905
1906 /**
1907 * Send a &quot;target finished&quot; event to the build listeners
1908 * for this project.
1909 *
1910 * @param target The target which has finished building.
1911 * Must not be <code>null</code>.
1912 * @param exception an exception indicating a reason for a build
1913 * failure. May be <code>null</code>, indicating
1914 * a successful build.
1915 */
1916 protected void fireTargetFinished(Target target, Throwable exception) {
1917 BuildEvent event = new BuildEvent(target);
1918 event.setException(exception);
1919 Iterator iter = listeners.iterator();
1920 while (iter.hasNext()) {
1921 BuildListener listener = (BuildListener) iter.next();
1922 listener.targetFinished(event);
1923 }
1924 }
1925
1926 /**
1927 * Send a &quot;task started&quot; event to the build listeners
1928 * for this project.
1929 *
1930 * @param task The target which is starting to execute.
1931 * Must not be <code>null</code>.
1932 */
1933 protected void fireTaskStarted(Task task) {
1934 // register this as the current task on the current thread.
1935 registerThreadTask(Thread.currentThread(), task);
1936 BuildEvent event = new BuildEvent(task);
1937 Iterator iter = listeners.iterator();
1938 while (iter.hasNext()) {
1939 BuildListener listener = (BuildListener) iter.next();
1940 listener.taskStarted(event);
1941 }
1942 }
1943
1944 /**
1945 * Send a &quot;task finished&quot; event to the build listeners for this
1946 * project.
1947 *
1948 * @param task The task which has finished executing.
1949 * Must not be <code>null</code>.
1950 * @param exception an exception indicating a reason for a build
1951 * failure. May be <code>null</code>, indicating
1952 * a successful build.
1953 */
1954 protected void fireTaskFinished(Task task, Throwable exception) {
1955 registerThreadTask(Thread.currentThread(), null);
1956 System.out.flush();
1957 System.err.flush();
1958 BuildEvent event = new BuildEvent(task);
1959 event.setException(exception);
1960 Iterator iter = listeners.iterator();
1961 while (iter.hasNext()) {
1962 BuildListener listener = (BuildListener) iter.next();
1963 listener.taskFinished(event);
1964 }
1965 }
1966
1967 /**
1968 * Send a &quot;message logged&quot; event to the build listeners
1969 * for this project.
1970 *
1971 * @param event The event to send. This should be built up with the
1972 * appropriate task/target/project by the caller, so that
1973 * this method can set the message and priority, then send
1974 * the event. Must not be <code>null</code>.
1975 * @param message The message to send. Should not be <code>null</code>.
1976 * @param priority The priority of the message.
1977 */
1978 private void fireMessageLoggedEvent(BuildEvent event, String message,
1979 int priority) {
1980
1981 if (message.endsWith(StringUtils.LINE_SEP)) {
1982 int endIndex = message.length() - StringUtils.LINE_SEP.length();
1983 event.setMessage(message.substring(0, endIndex), priority);
1984 } else {
1985 event.setMessage(message, priority);
1986 }
1987 synchronized (this) {
1988 if (loggingMessage) {
1989 /*
1990 * One of the Listeners has attempted to access
1991 * System.err or System.out.
1992 *
1993 * We used to throw an exception in this case, but
1994 * sometimes Listeners can't prevent it(like our own
1995 * Log4jListener which invokes getLogger() which in
1996 * turn wants to write to the console).
1997 *
1998 * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
1999 *
2000 * We now (Ant 1.7 and 1.6.3) simply swallow the message.
2001 */
2002 return;
2003 }
2004 try {
2005 loggingMessage = true;
2006 Iterator iter = listeners.iterator();
2007 while (iter.hasNext()) {
2008 BuildListener listener = (BuildListener) iter.next();
2009 listener.messageLogged(event);
2010 }
2011 } finally {
2012 loggingMessage = false;
2013 }
2014 }
2015 }
2016
2017 /**
2018 * Send a &quot;message logged&quot; project level event
2019 * to the build listeners for this project.
2020 *
2021 * @param project The project generating the event.
2022 * Should not be <code>null</code>.
2023 * @param message The message to send. Should not be <code>null</code>.
2024 * @param priority The priority of the message.
2025 */
2026 protected void fireMessageLogged(Project project, String message,
2027 int priority) {
2028 BuildEvent event = new BuildEvent(project);
2029 fireMessageLoggedEvent(event, message, priority);
2030 }
2031
2032 /**
2033 * Send a &quot;message logged&quot; target level event
2034 * to the build listeners for this project.
2035 *
2036 * @param target The target generating the event.
2037 * Must not be <code>null</code>.
2038 * @param message The message to send. Should not be <code>null</code>.
2039 * @param priority The priority of the message.
2040 */
2041 protected void fireMessageLogged(Target target, String message,
2042 int priority) {
2043 BuildEvent event = new BuildEvent(target);
2044 fireMessageLoggedEvent(event, message, priority);
2045 }
2046
2047 /**
2048 * Send a &quot;message logged&quot; task level event
2049 * to the build listeners for this project.
2050 *
2051 * @param task The task generating the event.
2052 * Must not be <code>null</code>.
2053 * @param message The message to send. Should not be <code>null</code>.
2054 * @param priority The priority of the message.
2055 */
2056 protected void fireMessageLogged(Task task, String message, int priority) {
2057 BuildEvent event = new BuildEvent(task);
2058 fireMessageLoggedEvent(event, message, priority);
2059 }
2060
2061 /**
2062 * Register a task as the current task for a thread.
2063 * If the task is null, the thread's entry is removed.
2064 *
2065 * @param thread the thread on which the task is registered.
2066 * @param task the task to be registered.
2067 * @since Ant 1.5
2068 */
2069 public synchronized void registerThreadTask(Thread thread, Task task) {
2070 if (task != null) {
2071 threadTasks.put(thread, task);
2072 threadGroupTasks.put(thread.getThreadGroup(), task);
2073 } else {
2074 threadTasks.remove(thread);
2075 threadGroupTasks.remove(thread.getThreadGroup());
2076 }
2077 }
2078
2079 /**
2080 * Get the current task associated with a thread, if any.
2081 *
2082 * @param thread the thread for which the task is required.
2083 * @return the task which is currently registered for the given thread or
2084 * null if no task is registered.
2085 */
2086 public Task getThreadTask(Thread thread) {
2087 Task task = (Task) threadTasks.get(thread);
2088 if (task == null) {
2089 ThreadGroup group = thread.getThreadGroup();
2090 while (task == null && group != null) {
2091 task = (Task) threadGroupTasks.get(group);
2092 group = group.getParent();
2093 }
2094 }
2095 return task;
2096 }
2097
2098
2099 // Should move to a separate public class - and have API to add
2100 // listeners, etc.
2101 private static class AntRefTable extends Hashtable {
2102 private Project project;
2103
2104 public AntRefTable(Project project) {
2105 super();
2106 this.project = project;
2107 }
2108
2109 /** Returns the unmodified original object.
2110 * This method should be called internally to
2111 * get the &quot;real&quot; object.
2112 * The normal get method will do the replacement
2113 * of UnknownElement (this is similar with the JDNI
2114 * refs behavior).
2115 */
2116 public Object getReal(Object key) {
2117 return super.get(key);
2118 }
2119
2120 /** Get method for the reference table.
2121 * It can be used to hook dynamic references and to modify
2122 * some references on the fly--for example for delayed
2123 * evaluation.
2124 *
2125 * It is important to make sure that the processing that is
2126 * done inside is not calling get indirectly.
2127 *
2128 * @param key lookup key.
2129 * @return mapped value.
2130 */
2131 public Object get(Object key) {
2132 //System.out.println("AntRefTable.get " + key);
2133 Object o = getReal(key);
2134 if (o instanceof UnknownElement) {
2135 // Make sure that
2136 UnknownElement ue = (UnknownElement) o;
2137 ue.maybeConfigure();
2138 o = ue.getRealThing();
2139 }
2140 return o;
2141 }
2142 }
2143
2144 /**
2145 * Set a reference to this Project on the parameterized object.
2146 * Need to set the project before other set/add elements
2147 * are called.
2148 * @param obj the object to invoke setProject(this) on.
2149 */
2150 public final void setProjectReference(final Object obj) {
2151 if (obj instanceof ProjectComponent) {
2152 ((ProjectComponent) obj).setProject(this);
2153 return;
2154 }
2155 try {
2156 Method method =
2157 obj.getClass().getMethod(
2158 "setProject", new Class[] {Project.class});
2159 if (method != null) {
2160 method.invoke(obj, new Object[] {this});
2161 }
2162 } catch (Throwable e) {
2163 // ignore this if the object does not have
2164 // a set project method or the method
2165 // is private/protected.
2166 }
2167 }
2168}
Note: See TracBrowser for help on using the repository browser.