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