KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > module > run > StandardLogger


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.apache.tools.ant.module.run;
21
22 import java.io.File JavaDoc;
23 import java.io.PrintWriter JavaDoc;
24 import java.io.StringWriter JavaDoc;
25 import java.net.MalformedURLException JavaDoc;
26 import java.net.URI JavaDoc;
27 import java.net.URISyntaxException JavaDoc;
28 import java.util.Stack JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30 import java.util.logging.Level JavaDoc;
31 import java.util.logging.Logger JavaDoc;
32 import java.util.regex.Matcher JavaDoc;
33 import java.util.regex.Pattern JavaDoc;
34 import org.apache.tools.ant.module.spi.AntEvent;
35 import org.apache.tools.ant.module.spi.AntLogger;
36 import org.apache.tools.ant.module.spi.AntSession;
37 import org.openide.awt.StatusDisplayer;
38 import org.openide.filesystems.FileUtil;
39 import org.openide.util.NbBundle;
40 import org.openide.windows.OutputListener;
41
42 /**
43  * Standard logger for producing Ant output messages.
44  * @author Jesse Glick
45  */

46 public final class StandardLogger extends AntLogger {
47     
48     private static final Logger JavaDoc ERR = Logger.getLogger(StandardLogger.class.getName());
49     
50     /**
51      * Regexp matching an output line that is a column marker from a compiler or similar.
52      * Captured groups:
53      * <ol>
54      * <li>spaces preceding caret; length indicates column number
55      * </ol>
56      * @see "#37358"
57      */

58     private static final Pattern JavaDoc CARET_SHOWING_COLUMN = Pattern.compile("^( *)\\^$"); // NOI18N
59
/**
60      * Regexp matching an output line indicating a change into a current working directory, as e.g. from make.
61      * Captured groups:
62      * <ol>
63      * <li>new working dir
64      * </ol>
65      */

66     private static final Pattern JavaDoc CWD_ENTER = Pattern.compile(".*Entering directory [`'\"]?([^`'\"]+)(['\"]|$|\\.\\.\\.$)"); // NOI18N
67
/**
68      * Regexp matching an output line indicating a change out of a current working directory.
69      * Captured groups:
70      * <ol>
71      * <li>previous working dir
72      * </ol>
73      */

74     private static final Pattern JavaDoc CWD_LEAVE = Pattern.compile(".*Leaving directory [`'\"]?([^`'\"]+)(['\"]|$|\\.\\.\\.$)"); // NOI18N
75
/**
76      * Regexp matching an output line which should (perhaps) be hyperlinked to a file.
77      * Captured groups:
78      * <ol>
79      * <li>file name (rel/abs path or URL)
80      * <li>line1
81      * <li>col1
82      * <li>line2
83      * <li>col2
84      * <li>message
85      * </ol>
86      */

87     private static final Pattern JavaDoc HYPERLINK = Pattern.compile("\"?(.+?)\"?(?::|, line )(?:(\\d+):(?:(\\d+):(?:(\\d+):(\\d+):)?)?)? +(.+)"); // NOI18N
88

89     /**
90      * Data stored in the session.
91      */

92     private static final class SessionData {
93         /** Time build was started. */
94         public long startTime;
95         /** Last-created hyperlink, in case we need to adjust the column number. */
96         public Hyperlink lastHyperlink;
97         /** Current stack of working directories for which output is being displayed; top is current location. */
98         public Stack JavaDoc<File JavaDoc> currentDir = new Stack JavaDoc<File JavaDoc>();
99         public SessionData() {}
100     }
101     
102     /** used only for unit testing */
103     private final long mockTotalTime;
104     
105     /** Default constructor for lookup. */
106     public StandardLogger() {
107         mockTotalTime = 0L;
108     }
109     
110     /** used only for unit testing */
111     StandardLogger(long mockTotalTime) {
112         this.mockTotalTime = mockTotalTime;
113     }
114     
115     @Override JavaDoc
116     public boolean interestedInSession(AntSession session) {
117         return true;
118     }
119     
120     @Override JavaDoc
121     public boolean interestedInAllScripts(AntSession session) {
122         return true;
123     }
124     
125     @Override JavaDoc
126     public String JavaDoc[] interestedInTargets(AntSession session) {
127         return AntLogger.ALL_TARGETS;
128     }
129     
130     @Override JavaDoc
131     public String JavaDoc[] interestedInTasks(AntSession session) {
132         return AntLogger.ALL_TASKS;
133     }
134     
135     @Override JavaDoc
136     public int[] interestedInLogLevels(AntSession session) {
137         int verb = session.getVerbosity();
138         assert verb >= AntEvent.LOG_ERR && verb <= AntEvent.LOG_DEBUG : verb;
139         int[] levels = new int[verb + 1];
140         for (int i = 0; i <= verb; i++) {
141             levels[i] = i;
142         }
143         return levels;
144     }
145     
146     private SessionData getSessionData(AntSession session) {
147         SessionData data = (SessionData) session.getCustomData(this);
148         if (data == null) {
149             data = new SessionData();
150             session.putCustomData(this, data);
151         }
152         return data;
153     }
154     
155     @Override JavaDoc
156     public void buildInitializationFailed(AntEvent event) {
157         if (event.isConsumed()) {
158             return;
159         }
160         // Write errors to the output window, since
161
// a lot of errors could be annoying as dialogs
162
Throwable JavaDoc t = event.getException();
163         if (event.getSession().getVerbosity() >= AntEvent.LOG_VERBOSE) {
164             deliverStackTrace(t, event);
165         } else {
166             event.getSession().println(t.toString(), true, null);
167         }
168         StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(StandardLogger.class, "FMT_target_failed_status", event.getSession().getDisplayName()));
169         event.consume();
170     }
171
172     private static void deliverBlockOfTextAsLines(String JavaDoc lines, AntEvent originalEvent, int level) {
173         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(lines, "\r\n"); // NOI18N
174
while (tok.hasMoreTokens()) {
175             String JavaDoc line = tok.nextToken();
176             originalEvent.getSession().deliverMessageLogged(originalEvent, line, level);
177         }
178     }
179     
180     private static void deliverStackTrace(Throwable JavaDoc t, AntEvent originalEvent) {
181         StringWriter JavaDoc sw = new StringWriter JavaDoc();
182         PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
183         t.printStackTrace(pw);
184         pw.flush();
185         deliverBlockOfTextAsLines(sw.toString(), originalEvent, AntEvent.LOG_ERR);
186     }
187     
188     @Override JavaDoc
189     public void buildStarted(AntEvent event) {
190         if (event.isConsumed()) {
191             return;
192         }
193         getSessionData(event.getSession()).startTime = System.currentTimeMillis();
194         StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(StandardLogger.class, "FMT_running_ant", event.getSession().getDisplayName()));
195         // no messages printed for now
196
event.consume();
197     }
198     
199     @Override JavaDoc
200     public void buildFinished(AntEvent event) {
201         if (event.isConsumed()) {
202             return;
203         }
204         AntSession session = event.getSession();
205         Throwable JavaDoc t = event.getException();
206         long time = System.currentTimeMillis() - getSessionData(session).startTime; // #10305
207
if (mockTotalTime != 0L) {
208             time = mockTotalTime;
209         }
210         if (t == null) {
211             session.println(formatMessageWithTime("FMT_finished_target_printed", time), false, null);
212             StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(StandardLogger.class, "FMT_finished_target_status", session.getDisplayName()));
213         } else {
214             if (t.getCause() instanceof ThreadDeath JavaDoc) {
215                 // Sometimes wrapped, but we really want to know just that the thread was stopped.
216
t = t.getCause();
217             }
218             if (!session.isExceptionConsumed(t)) {
219                 session.consumeException(t);
220                 if (t.getClass().getName().equals("org.apache.tools.ant.BuildException") && session.getVerbosity() < AntEvent.LOG_VERBOSE) { // NOI18N
221
// Stack trace probably not required.
222
// Check for hyperlink to handle e.g. <fail>
223
// which produces a BE whose toString is the location + message.
224
// But send to other loggers since they may wish to suppress such an error.
225
String JavaDoc msg = t.toString();
226                     deliverBlockOfTextAsLines(msg, event, AntEvent.LOG_ERR);
227                 } else if (!(t instanceof ThreadDeath JavaDoc) || event.getSession().getVerbosity() >= AntEvent.LOG_VERBOSE) {
228                     // ThreadDeath can be thrown when killing an Ant process, so don't print it normally
229
deliverStackTrace(t, event);
230                 }
231             }
232             if (t instanceof ThreadDeath JavaDoc) {
233                 event.getSession().println(formatMessageWithTime("FMT_target_stopped_printed", time), true, null);
234                 StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(StandardLogger.class, "FMT_target_stopped_status", event.getSession().getDisplayName()));
235             } else {
236                 event.getSession().println(formatMessageWithTime("FMT_target_failed_printed", time), true, null); // #10305
237
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(StandardLogger.class, "FMT_target_failed_status", event.getSession().getDisplayName()));
238             }
239         }
240         event.consume();
241     }
242     
243     /** Formats the millis in a human readable String.
244      * Total time: {0} minutes
245      * {1} seconds
246      */

247     private static String JavaDoc formatMessageWithTime(String JavaDoc key, long millis) {
248         int secs = (int) (millis / 1000);
249         int minutes = secs / 60;
250         int seconds = secs % 60;
251         return NbBundle.getMessage(StandardLogger.class, key, new Integer JavaDoc(minutes), new Integer JavaDoc(seconds));
252     }
253     
254     @Override JavaDoc
255     public void targetStarted(AntEvent event) {
256         if (event.isConsumed()) {
257             return;
258         }
259         // XXX this could start indenting messages, perhaps
260
String JavaDoc name = event.getTargetName();
261         if (name != null) {
262             // Avoid printing internal targets normally:
263
int minlevel = (name.length() > 0 && name.charAt(0) == '-') ? AntEvent.LOG_VERBOSE : AntEvent.LOG_INFO;
264             if (event.getSession().getVerbosity() >= minlevel) {
265                 event.getSession().println(NbBundle.getMessage(StandardLogger.class, "MSG_target_started_printed", name), false, null);
266             }
267         }
268         event.consume();
269     }
270     
271     @Override JavaDoc
272     public void messageLogged(AntEvent event) {
273         if (event.isConsumed()) {
274             return;
275         }
276         event.consume();
277         AntSession session = event.getSession();
278         String JavaDoc line = event.getMessage();
279         ERR.log(Level.FINE, "Received message: {0}", line);
280         if (line.indexOf('\n') != -1) {
281             // Multiline message. Should be split into blocks and redelivered,
282
// to allow other loggers (e.g. JavaAntLogger) to process individual
283
// lines (e.g. stack traces). Note that other loggers are still capable
284
// of handling the original multiline message specially. Note also that
285
// only messages at or above the session verbosity will be split.
286
deliverBlockOfTextAsLines(line, event, event.getLogLevel());
287             return;
288         }
289         Matcher JavaDoc m = CARET_SHOWING_COLUMN.matcher(line);
290         if (m.matches()) {
291             // #37358: adjust the column number of the last hyperlink accordingly.
292
ERR.fine(" Looks like a special caret line");
293             SessionData data = getSessionData(session);
294             if (data.lastHyperlink != null) {
295                 // For " ^", infer a column number of 3.
296
data.lastHyperlink.setColumn1(m.group(1).length() + 1);
297                 data.lastHyperlink = null;
298                 // Don't print the actual caret line, just noise.
299
return;
300             }
301         }
302         m = CWD_ENTER.matcher(line);
303         if (m.matches()) {
304             ERR.fine(" Looks like a change of CWD");
305             File JavaDoc d = new File JavaDoc(m.group(1));
306             if (d.isDirectory()) {
307                 Stack JavaDoc<File JavaDoc> stack = getSessionData(session).currentDir;
308                 stack.push(d);
309                 ERR.log(Level.FINE, " ...is a change of CWD; stack now: {0}", stack);
310             }
311         }
312         m = CWD_LEAVE.matcher(line);
313         if (m.matches()) {
314             ERR.fine(" Looks like a change of CWD back out");
315             File JavaDoc d = new File JavaDoc(m.group(1));
316             Stack JavaDoc<File JavaDoc> stack = getSessionData(session).currentDir;
317             if (stack.empty()) {
318                 ERR.log(Level.FINE, " ...but there was nowhere to change out of");
319             } else {
320                 File JavaDoc previous = stack.pop();
321                 if (!previous.equals(d)) {
322                     ERR.log(Level.FINE, " ...stack mismatch: {0} vs. {1}", new Object JavaDoc[] {previous, d});
323                 }
324             }
325         }
326         OutputListener hyperlink = findHyperlink(session, line);
327         if (hyperlink instanceof Hyperlink) {
328             getSessionData(session).lastHyperlink = (Hyperlink) hyperlink;
329         }
330         // XXX should translate tabs to spaces here as a safety measure (esp. since output window messes it up...)
331
event.getSession().println(line, event.getLogLevel() <= AntEvent.LOG_WARN, hyperlink);
332     }
333     
334     @Override JavaDoc
335     public void taskFinished(AntEvent event) {
336         // Do not consider hyperlinks from previous tasks.
337
getSessionData(event.getSession()).lastHyperlink = null;
338     }
339
340     /**
341      * Possibly hyperlink a message logged event.
342      */

343     private OutputListener findHyperlink(AntSession session, String JavaDoc line) {
344         Stack JavaDoc<File JavaDoc> cwd = getSessionData(session).currentDir;
345         Matcher JavaDoc m = HYPERLINK.matcher(line);
346         if (!m.matches()) {
347             ERR.fine("does not look like a hyperlink");
348             return null;
349         }
350         String JavaDoc path = m.group(1);
351         File JavaDoc file;
352         if (path.startsWith("file:")) {
353             try {
354                 file = new File JavaDoc(new URI JavaDoc(path));
355             } catch (URISyntaxException JavaDoc e) {
356                 ERR.log(Level.FINE, "invalid URI, skipping", e);
357                 return null;
358             } catch (IllegalArgumentException JavaDoc e) {
359                 ERR.log(Level.FINE, "invalid URI, skipping", e);
360                 return null;
361             }
362         } else {
363             file = new File JavaDoc(path);
364             if (!file.isAbsolute()) {
365                 if (cwd.isEmpty()) {
366                     ERR.fine("Non-absolute path with no CWD, skipping");
367                     // don't waste time on File.exists!
368
return null;
369                 } else {
370                     file = new File JavaDoc(cwd.peek(), path);
371                 }
372             }
373         }
374         if (!file.exists()) {
375             ERR.log(Level.FINE, "no such file {0}, skipping", file);
376             return null;
377         }
378
379         int line1 = -1, col1 = -1, line2 = -1, col2 = -1;
380         String JavaDoc num = m.group(2);
381         try {
382             if (num != null) {
383                 line1 = Integer.parseInt(num);
384                 num = m.group(3);
385                 if (num != null) {
386                     col1 = Integer.parseInt(num);
387                     num = m.group(4);
388                     if (num != null) {
389                         line2 = Integer.parseInt(num);
390                         col2 = Integer.parseInt(m.group(5));
391                     }
392                 }
393             }
394         } catch (NumberFormatException JavaDoc e) {
395             ERR.log(Level.FINE, "bad line/col #", e);
396             return null;
397         }
398
399         String JavaDoc message = m.group(6);
400         
401         file = FileUtil.normalizeFile(file); // do this late, after File.exists
402
ERR.log(Level.FINE, "Hyperlink: {0} [{1}:{2}:{3}:{4}]: {5}", new Object JavaDoc[] {file, line1, col1, line2, col2, message});
403         try {
404             return session.createStandardHyperlink(file.toURI().toURL(), message, line1, col1, line2, col2);
405         } catch (MalformedURLException JavaDoc e) {
406             assert false : e;
407             return null;
408         }
409     }
410
411 }
412
Popular Tags