001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.taskdefs;
019:
020: import java.util.Hashtable;
021: import org.apache.tools.ant.BuildEvent;
022: import org.apache.tools.ant.BuildException;
023: import org.apache.tools.ant.Project;
024: import org.apache.tools.ant.SubBuildListener;
025: import org.apache.tools.ant.Task;
026: import org.apache.tools.ant.types.EnumeratedAttribute;
027: import org.apache.tools.ant.types.LogLevel;
028:
029: /**
030: * Adds a listener to the current build process that records the
031: * output to a file.
032: * <p>Several recorders can exist at the same time. Each recorder is
033: * associated with a file. The filename is used as a unique identifier for
034: * the recorders. The first call to the recorder task with an unused filename
035: * will create a recorder (using the parameters provided) and add it to the
036: * listeners of the build. All subsequent calls to the recorder task using
037: * this filename will modify that recorders state (recording or not) or other
038: * properties (like logging level).</p>
039: * <p>Some technical issues: the file's print stream is flushed for "finished"
040: * events (buildFinished, targetFinished and taskFinished), and is closed on
041: * a buildFinished event.</p>
042: * @see RecorderEntry
043: * @version 0.5
044: * @since Ant 1.4
045: * @ant.task name="record" category="utility"
046: */
047: public class Recorder extends Task implements SubBuildListener {
048:
049: //////////////////////////////////////////////////////////////////////
050: // ATTRIBUTES
051:
052: /** The name of the file to record to. */
053: private String filename = null;
054: /**
055: * Whether or not to append. Need Boolean to record an unset state (null).
056: */
057: private Boolean append = null;
058: /**
059: * Whether to start or stop recording. Need Boolean to record an unset
060: * state (null).
061: */
062: private Boolean start = null;
063: /** The level to log at. A level of -1 means not initialized yet. */
064: private int loglevel = -1;
065: /** Strip task banners if true. */
066: private boolean emacsMode = false;
067: /** The list of recorder entries. */
068: private static Hashtable recorderEntries = new Hashtable();
069:
070: //////////////////////////////////////////////////////////////////////
071: // CONSTRUCTORS / INITIALIZERS
072:
073: /**
074: * Overridden so we can add the task as build listener.
075: *
076: * @since Ant 1.7
077: */
078: public void init() {
079: getProject().addBuildListener(this );
080: }
081:
082: //////////////////////////////////////////////////////////////////////
083: // ACCESSOR METHODS
084:
085: /**
086: * Sets the name of the file to log to, and the name of the recorder
087: * entry.
088: *
089: * @param fname File name of logfile.
090: */
091: public void setName(String fname) {
092: filename = fname;
093: }
094:
095: /**
096: * Sets the action for the associated recorder entry.
097: *
098: * @param action The action for the entry to take: start or stop.
099: */
100: public void setAction(ActionChoices action) {
101: if (action.getValue().equalsIgnoreCase("start")) {
102: start = Boolean.TRUE;
103: } else {
104: start = Boolean.FALSE;
105: }
106: }
107:
108: /**
109: * Whether or not the logger should append to a previous file.
110: * @param append if true, append to a previous file.
111: */
112: public void setAppend(boolean append) {
113: this .append = (append ? Boolean.TRUE : Boolean.FALSE);
114: }
115:
116: /**
117: * Set emacs mode.
118: * @param emacsMode if true use emacs mode
119: */
120: public void setEmacsMode(boolean emacsMode) {
121: this .emacsMode = emacsMode;
122: }
123:
124: /**
125: * Sets the level to which this recorder entry should log to.
126: * @param level the level to set.
127: * @see VerbosityLevelChoices
128: */
129: public void setLoglevel(VerbosityLevelChoices level) {
130: loglevel = level.getLevel();
131: }
132:
133: //////////////////////////////////////////////////////////////////////
134: // CORE / MAIN BODY
135:
136: /**
137: * The main execution.
138: * @throws BuildException on error
139: */
140: public void execute() throws BuildException {
141: if (filename == null) {
142: throw new BuildException("No filename specified");
143: }
144:
145: getProject().log("setting a recorder for name " + filename,
146: Project.MSG_DEBUG);
147:
148: // get the recorder entry
149: RecorderEntry recorder = getRecorder(filename, getProject());
150: // set the values on the recorder
151: recorder.setMessageOutputLevel(loglevel);
152: recorder.setEmacsMode(emacsMode);
153: if (start != null) {
154: if (start.booleanValue()) {
155: recorder.reopenFile();
156: recorder.setRecordState(start);
157: } else {
158: recorder.setRecordState(start);
159: recorder.closeFile();
160: }
161: }
162: }
163:
164: //////////////////////////////////////////////////////////////////////
165: // INNER CLASSES
166:
167: /**
168: * A list of possible values for the <code>setAction()</code> method.
169: * Possible values include: start and stop.
170: */
171: public static class ActionChoices extends EnumeratedAttribute {
172: private static final String[] VALUES = { "start", "stop" };
173:
174: /**
175: * @see EnumeratedAttribute#getValues()
176: */
177: /** {@inheritDoc}. */
178: public String[] getValues() {
179: return VALUES;
180: }
181: }
182:
183: /**
184: * A list of possible values for the <code>setLoglevel()</code> method.
185: * Possible values include: error, warn, info, verbose, debug.
186: */
187: public static class VerbosityLevelChoices extends LogLevel {
188: }
189:
190: /**
191: * Gets the recorder that's associated with the passed in name. If the
192: * recorder doesn't exist, then a new one is created.
193: * @param name the name of the recoder
194: * @param proj the current project
195: * @return a recorder
196: * @throws BuildException on error
197: */
198: protected RecorderEntry getRecorder(String name, Project proj)
199: throws BuildException {
200: Object o = recorderEntries.get(name);
201: RecorderEntry entry;
202:
203: if (o == null) {
204: // create a recorder entry
205: entry = new RecorderEntry(name);
206:
207: if (append == null) {
208: entry.openFile(false);
209: } else {
210: entry.openFile(append.booleanValue());
211: }
212: entry.setProject(proj);
213: recorderEntries.put(name, entry);
214: } else {
215: entry = (RecorderEntry) o;
216: }
217: return entry;
218: }
219:
220: /**
221: * Empty implementation required by SubBuildListener interface.
222: * @param event ignored.
223: * @since Ant 1.7
224: */
225: public void buildStarted(BuildEvent event) {
226: }
227:
228: /**
229: * Empty implementation required by SubBuildListener interface.
230: * @param event ignored.
231: * @since Ant 1.7
232: */
233: public void subBuildStarted(BuildEvent event) {
234: }
235:
236: /**
237: * Empty implementation required by SubBuildListener interface.
238: * @param event ignored.
239: * @since Ant 1.7
240: */
241: public void targetStarted(BuildEvent event) {
242: }
243:
244: /**
245: * Empty implementation required by SubBuildListener interface.
246: * @param event ignored.
247: * @since Ant 1.7
248: */
249: public void targetFinished(BuildEvent event) {
250: }
251:
252: /**
253: * Empty implementation required by SubBuildListener interface.
254: * @param event ignored.
255: * @since Ant 1.7
256: */
257: public void taskStarted(BuildEvent event) {
258: }
259:
260: /**
261: * Empty implementation required by SubBuildListener interface.
262: * @param event ignored.
263: * @since Ant 1.7
264: */
265: public void taskFinished(BuildEvent event) {
266: }
267:
268: /**
269: * Empty implementation required by SubBuildListener interface.
270: * @param event ignored.
271: * @since Ant 1.7
272: */
273: public void messageLogged(BuildEvent event) {
274: }
275:
276: /**
277: * Cleans recorder registry.
278: * @param event ignored.
279: * @since Ant 1.7
280: */
281: public void buildFinished(BuildEvent event) {
282: cleanup();
283: }
284:
285: /**
286: * Cleans recorder registry, if this is the subbuild the task has
287: * been created in.
288: * @param event ignored.
289: * @since Ant 1.7
290: */
291: public void subBuildFinished(BuildEvent event) {
292: if (event.getProject() == getProject()) {
293: cleanup();
294: }
295: }
296:
297: /**
298: * cleans recorder registry and removes itself from BuildListener list.
299: *
300: * @since Ant 1.7
301: */
302: private void cleanup() {
303: recorderEntries.clear();
304: getProject().removeBuildListener(this);
305: }
306: }
|