1 19 20 package org.apache.tools.ant.module.run; 21 22 import java.io.File ; 23 import java.io.PrintWriter ; 24 import java.io.StringWriter ; 25 import java.net.MalformedURLException ; 26 import java.net.URI ; 27 import java.net.URISyntaxException ; 28 import java.util.Stack ; 29 import java.util.StringTokenizer ; 30 import java.util.logging.Level ; 31 import java.util.logging.Logger ; 32 import java.util.regex.Matcher ; 33 import java.util.regex.Pattern ; 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 46 public final class StandardLogger extends AntLogger { 47 48 private static final Logger ERR = Logger.getLogger(StandardLogger.class.getName()); 49 50 58 private static final Pattern CARET_SHOWING_COLUMN = Pattern.compile("^( *)\\^$"); 66 private static final Pattern CWD_ENTER = Pattern.compile(".*Entering directory [`'\"]?([^`'\"]+)(['\"]|$|\\.\\.\\.$)"); 74 private static final Pattern CWD_LEAVE = Pattern.compile(".*Leaving directory [`'\"]?([^`'\"]+)(['\"]|$|\\.\\.\\.$)"); 87 private static final Pattern HYPERLINK = Pattern.compile("\"?(.+?)\"?(?::|, line )(?:(\\d+):(?:(\\d+):(?:(\\d+):(\\d+):)?)?)? +(.+)"); 89 92 private static final class SessionData { 93 94 public long startTime; 95 96 public Hyperlink lastHyperlink; 97 98 public Stack <File > currentDir = new Stack <File >(); 99 public SessionData() {} 100 } 101 102 103 private final long mockTotalTime; 104 105 106 public StandardLogger() { 107 mockTotalTime = 0L; 108 } 109 110 111 StandardLogger(long mockTotalTime) { 112 this.mockTotalTime = mockTotalTime; 113 } 114 115 @Override 116 public boolean interestedInSession(AntSession session) { 117 return true; 118 } 119 120 @Override 121 public boolean interestedInAllScripts(AntSession session) { 122 return true; 123 } 124 125 @Override 126 public String [] interestedInTargets(AntSession session) { 127 return AntLogger.ALL_TARGETS; 128 } 129 130 @Override 131 public String [] interestedInTasks(AntSession session) { 132 return AntLogger.ALL_TASKS; 133 } 134 135 @Override 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 156 public void buildInitializationFailed(AntEvent event) { 157 if (event.isConsumed()) { 158 return; 159 } 160 Throwable 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 lines, AntEvent originalEvent, int level) { 173 StringTokenizer tok = new StringTokenizer (lines, "\r\n"); while (tok.hasMoreTokens()) { 175 String line = tok.nextToken(); 176 originalEvent.getSession().deliverMessageLogged(originalEvent, line, level); 177 } 178 } 179 180 private static void deliverStackTrace(Throwable t, AntEvent originalEvent) { 181 StringWriter sw = new StringWriter (); 182 PrintWriter pw = new PrintWriter (sw); 183 t.printStackTrace(pw); 184 pw.flush(); 185 deliverBlockOfTextAsLines(sw.toString(), originalEvent, AntEvent.LOG_ERR); 186 } 187 188 @Override 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 event.consume(); 197 } 198 199 @Override 200 public void buildFinished(AntEvent event) { 201 if (event.isConsumed()) { 202 return; 203 } 204 AntSession session = event.getSession(); 205 Throwable t = event.getException(); 206 long time = System.currentTimeMillis() - getSessionData(session).startTime; 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 ) { 215 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) { String msg = t.toString(); 226 deliverBlockOfTextAsLines(msg, event, AntEvent.LOG_ERR); 227 } else if (!(t instanceof ThreadDeath ) || event.getSession().getVerbosity() >= AntEvent.LOG_VERBOSE) { 228 deliverStackTrace(t, event); 230 } 231 } 232 if (t instanceof ThreadDeath ) { 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); StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(StandardLogger.class, "FMT_target_failed_status", event.getSession().getDisplayName())); 238 } 239 } 240 event.consume(); 241 } 242 243 247 private static String formatMessageWithTime(String 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 (minutes), new Integer (seconds)); 252 } 253 254 @Override 255 public void targetStarted(AntEvent event) { 256 if (event.isConsumed()) { 257 return; 258 } 259 String name = event.getTargetName(); 261 if (name != null) { 262 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 272 public void messageLogged(AntEvent event) { 273 if (event.isConsumed()) { 274 return; 275 } 276 event.consume(); 277 AntSession session = event.getSession(); 278 String line = event.getMessage(); 279 ERR.log(Level.FINE, "Received message: {0}", line); 280 if (line.indexOf('\n') != -1) { 281 deliverBlockOfTextAsLines(line, event, event.getLogLevel()); 287 return; 288 } 289 Matcher m = CARET_SHOWING_COLUMN.matcher(line); 290 if (m.matches()) { 291 ERR.fine(" Looks like a special caret line"); 293 SessionData data = getSessionData(session); 294 if (data.lastHyperlink != null) { 295 data.lastHyperlink.setColumn1(m.group(1).length() + 1); 297 data.lastHyperlink = null; 298 return; 300 } 301 } 302 m = CWD_ENTER.matcher(line); 303 if (m.matches()) { 304 ERR.fine(" Looks like a change of CWD"); 305 File d = new File (m.group(1)); 306 if (d.isDirectory()) { 307 Stack <File > 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 d = new File (m.group(1)); 316 Stack <File > 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 previous = stack.pop(); 321 if (!previous.equals(d)) { 322 ERR.log(Level.FINE, " ...stack mismatch: {0} vs. {1}", new Object [] {previous, d}); 323 } 324 } 325 } 326 OutputListener hyperlink = findHyperlink(session, line); 327 if (hyperlink instanceof Hyperlink) { 328 getSessionData(session).lastHyperlink = (Hyperlink) hyperlink; 329 } 330 event.getSession().println(line, event.getLogLevel() <= AntEvent.LOG_WARN, hyperlink); 332 } 333 334 @Override 335 public void taskFinished(AntEvent event) { 336 getSessionData(event.getSession()).lastHyperlink = null; 338 } 339 340 343 private OutputListener findHyperlink(AntSession session, String line) { 344 Stack <File > cwd = getSessionData(session).currentDir; 345 Matcher m = HYPERLINK.matcher(line); 346 if (!m.matches()) { 347 ERR.fine("does not look like a hyperlink"); 348 return null; 349 } 350 String path = m.group(1); 351 File file; 352 if (path.startsWith("file:")) { 353 try { 354 file = new File (new URI (path)); 355 } catch (URISyntaxException e) { 356 ERR.log(Level.FINE, "invalid URI, skipping", e); 357 return null; 358 } catch (IllegalArgumentException e) { 359 ERR.log(Level.FINE, "invalid URI, skipping", e); 360 return null; 361 } 362 } else { 363 file = new File (path); 364 if (!file.isAbsolute()) { 365 if (cwd.isEmpty()) { 366 ERR.fine("Non-absolute path with no CWD, skipping"); 367 return null; 369 } else { 370 file = new File (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 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 e) { 395 ERR.log(Level.FINE, "bad line/col #", e); 396 return null; 397 } 398 399 String message = m.group(6); 400 401 file = FileUtil.normalizeFile(file); ERR.log(Level.FINE, "Hyperlink: {0} [{1}:{2}:{3}:{4}]: {5}", new Object [] {file, line1, col1, line2, col2, message}); 403 try { 404 return session.createStandardHyperlink(file.toURI().toURL(), message, line1, col1, line2, col2); 405 } catch (MalformedURLException e) { 406 assert false : e; 407 return null; 408 } 409 } 410 411 } 412 | Popular Tags |