24 */ |
24 */ |
25 |
25 |
26 package com.sun.tools.javac.util; |
26 package com.sun.tools.javac.util; |
27 |
27 |
28 import java.io.*; |
28 import java.io.*; |
29 import java.nio.CharBuffer; |
|
30 import java.util.HashMap; |
|
31 import java.util.HashSet; |
29 import java.util.HashSet; |
32 import java.util.Map; |
30 import java.util.Map; |
33 import java.util.Set; |
31 import java.util.Set; |
34 import javax.tools.DiagnosticListener; |
32 import javax.tools.DiagnosticListener; |
35 import javax.tools.JavaFileObject; |
33 import javax.tools.JavaFileObject; |
36 |
34 |
37 import com.sun.tools.javac.file.JavacFileManager; |
35 import com.sun.tools.javac.file.JavacFileManager; |
38 import com.sun.tools.javac.tree.JCTree; |
36 import com.sun.tools.javac.tree.JCTree; |
39 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
37 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
40 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; |
38 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; |
41 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; |
|
42 |
|
43 import static com.sun.tools.javac.util.LayoutCharacters.*; |
|
44 |
39 |
45 /** A class for error logs. Reports errors and warnings, and |
40 /** A class for error logs. Reports errors and warnings, and |
46 * keeps track of error numbers and positions. |
41 * keeps track of error numbers and positions. |
47 * |
42 * |
48 * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
43 * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
49 * you write code that depends on this, you do so at your own risk. |
44 * you write code that depends on this, you do so at your own risk. |
50 * This code and its internal interfaces are subject to change or |
45 * This code and its internal interfaces are subject to change or |
51 * deletion without notice.</b> |
46 * deletion without notice.</b> |
52 */ |
47 */ |
53 public class Log { |
48 public class Log extends AbstractLog { |
54 /** The context key for the log. */ |
49 /** The context key for the log. */ |
55 public static final Context.Key<Log> logKey |
50 public static final Context.Key<Log> logKey |
56 = new Context.Key<Log>(); |
51 = new Context.Key<Log>(); |
57 |
52 |
58 /** The context key for the output PrintWriter. */ |
53 /** The context key for the output PrintWriter. */ |
96 /** |
91 /** |
97 * Diagnostic listener, if provided through programmatic |
92 * Diagnostic listener, if provided through programmatic |
98 * interface to javac (JSR 199). |
93 * interface to javac (JSR 199). |
99 */ |
94 */ |
100 protected DiagnosticListener<? super JavaFileObject> diagListener; |
95 protected DiagnosticListener<? super JavaFileObject> diagListener; |
|
96 |
101 /** |
97 /** |
102 * Formatter for diagnostics |
98 * Formatter for diagnostics |
103 */ |
99 */ |
104 private DiagnosticFormatter diagFormatter; |
100 private DiagnosticFormatter diagFormatter; |
105 |
101 |
106 /** |
|
107 * Factory for diagnostics |
|
108 */ |
|
109 private JCDiagnostic.Factory diags; |
|
110 |
|
111 |
|
112 /** Construct a log with given I/O redirections. |
102 /** Construct a log with given I/O redirections. |
113 */ |
103 */ |
114 @Deprecated |
104 @Deprecated |
115 protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) { |
105 protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) { |
|
106 super(JCDiagnostic.Factory.instance(context)); |
116 context.put(logKey, this); |
107 context.put(logKey, this); |
117 this.errWriter = errWriter; |
108 this.errWriter = errWriter; |
118 this.warnWriter = warnWriter; |
109 this.warnWriter = warnWriter; |
119 this.noticeWriter = noticeWriter; |
110 this.noticeWriter = noticeWriter; |
120 |
|
121 this.diags = JCDiagnostic.Factory.instance(context); |
|
122 |
111 |
123 Options options = Options.instance(context); |
112 Options options = Options.instance(context); |
124 this.dumpOnError = options.get("-doe") != null; |
113 this.dumpOnError = options.get("-doe") != null; |
125 this.promptOnError = options.get("-prompt") != null; |
114 this.promptOnError = options.get("-prompt") != null; |
126 this.emitWarnings = options.get("-Xlint:none") == null; |
115 this.emitWarnings = options.get("-Xlint:none") == null; |
190 * error message more than once. For each error, a pair consisting of the |
175 * error message more than once. For each error, a pair consisting of the |
191 * source file name and source code position of the error is added to the set. |
176 * source file name and source code position of the error is added to the set. |
192 */ |
177 */ |
193 private Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<Pair<JavaFileObject,Integer>>(); |
178 private Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<Pair<JavaFileObject,Integer>>(); |
194 |
179 |
195 private Map<JavaFileObject, Map<JCTree, Integer>> endPosTables; |
|
196 |
|
197 /** The buffer containing the file that's currently translated. |
|
198 */ |
|
199 private char[] buf = null; |
|
200 |
|
201 /** The length of useful data in buf |
|
202 */ |
|
203 private int bufLen = 0; |
|
204 |
|
205 /** The position in the buffer at which last error was reported |
|
206 */ |
|
207 private int bp; |
|
208 |
|
209 /** number of the current source line; first line is 1 |
|
210 */ |
|
211 private int line; |
|
212 |
|
213 /** buffer index of the first character of the current source line |
|
214 */ |
|
215 private int lineStart; |
|
216 |
|
217 public boolean hasDiagnosticListener() { |
180 public boolean hasDiagnosticListener() { |
218 return diagListener != null; |
181 return diagListener != null; |
219 } |
182 } |
220 |
183 |
221 public void setEndPosTable(JavaFileObject name, Map<JCTree, Integer> table) { |
184 public void setEndPosTable(JavaFileObject name, Map<JCTree, Integer> table) { |
222 if (endPosTables == null) |
185 name.getClass(); // null check |
223 endPosTables = new HashMap<JavaFileObject, Map<JCTree, Integer>>(); |
186 getSource(name).setEndPosTable(table); |
224 endPosTables.put(name, table); |
|
225 } |
|
226 |
|
227 /** Re-assign source, returning previous setting. |
|
228 */ |
|
229 public JavaFileObject useSource(final JavaFileObject name) { |
|
230 JavaFileObject prev = currentSource(); |
|
231 if (name != prev) { |
|
232 source = new JCDiagnostic.DiagnosticSource() { |
|
233 public JavaFileObject getFile() { |
|
234 return name; |
|
235 } |
|
236 public CharSequence getName() { |
|
237 return JavacFileManager.getJavacBaseFileName(getFile()); |
|
238 } |
|
239 public int getLineNumber(int pos) { |
|
240 return Log.this.getLineNumber(pos); |
|
241 } |
|
242 public int getColumnNumber(int pos) { |
|
243 return Log.this.getColumnNumber(pos); |
|
244 } |
|
245 public Map<JCTree, Integer> getEndPosTable() { |
|
246 return (endPosTables == null ? null : endPosTables.get(name)); |
|
247 } |
|
248 }; |
|
249 buf = null; |
|
250 } |
|
251 return prev; |
|
252 } |
|
253 |
|
254 /** Re-assign source buffer for existing source name. |
|
255 */ |
|
256 protected void setBuf(char[] newBuf) { |
|
257 buf = newBuf; |
|
258 bufLen = buf.length; |
|
259 bp = 0; |
|
260 lineStart = 0; |
|
261 line = 1; |
|
262 } |
|
263 |
|
264 protected char[] getBuf() { |
|
265 return buf; |
|
266 } |
187 } |
267 |
188 |
268 /** Return current source name. |
189 /** Return current source name. |
269 */ |
190 */ |
270 public JavaFileObject currentSource() { |
191 public JavaFileObject currentSource() { |
318 |
239 |
319 /** Print the faulty source code line and point to the error. |
240 /** Print the faulty source code line and point to the error. |
320 * @param pos Buffer index of the error position, must be on current line |
241 * @param pos Buffer index of the error position, must be on current line |
321 */ |
242 */ |
322 private void printErrLine(int pos, PrintWriter writer) { |
243 private void printErrLine(int pos, PrintWriter writer) { |
323 if (!findLine(pos)) |
244 String line = (source == null ? null : source.getLine(pos)); |
|
245 if (line == null) |
324 return; |
246 return; |
325 |
247 int col = source.getColumnNumber(pos); |
326 int lineEnd = lineStart; |
248 |
327 while (lineEnd < bufLen && buf[lineEnd] != CR && buf[lineEnd] != LF) |
249 printLines(writer, line); |
328 lineEnd++; |
250 for (int i = 0; i < col - 1; i++) { |
329 if (lineEnd - lineStart == 0) |
251 writer.print((line.charAt(i) == '\t') ? "\t" : " "); |
330 return; |
|
331 printLines(writer, new String(buf, lineStart, lineEnd - lineStart)); |
|
332 for (bp = lineStart; bp < pos; bp++) { |
|
333 writer.print((buf[bp] == '\t') ? "\t" : " "); |
|
334 } |
252 } |
335 writer.println("^"); |
253 writer.println("^"); |
336 writer.flush(); |
254 writer.flush(); |
337 } |
|
338 |
|
339 protected void initBuf(JavaFileObject fileObject) throws IOException { |
|
340 CharSequence cs = fileObject.getCharContent(true); |
|
341 if (cs instanceof CharBuffer) { |
|
342 CharBuffer cb = (CharBuffer) cs; |
|
343 buf = JavacFileManager.toArray(cb); |
|
344 bufLen = cb.limit(); |
|
345 } else { |
|
346 buf = cs.toString().toCharArray(); |
|
347 bufLen = buf.length; |
|
348 } |
|
349 } |
|
350 |
|
351 /** Find the line in the buffer that contains the current position |
|
352 * @param pos Character offset into the buffer |
|
353 */ |
|
354 private boolean findLine(int pos) { |
|
355 if (pos == Position.NOPOS || currentSource() == null) |
|
356 return false; |
|
357 try { |
|
358 if (buf == null) { |
|
359 initBuf(currentSource()); |
|
360 lineStart = 0; |
|
361 line = 1; |
|
362 } else if (lineStart > pos) { // messages don't come in order |
|
363 lineStart = 0; |
|
364 line = 1; |
|
365 } |
|
366 bp = lineStart; |
|
367 while (bp < bufLen && bp < pos) { |
|
368 switch (buf[bp++]) { |
|
369 case CR: |
|
370 if (bp < bufLen && buf[bp] == LF) bp++; |
|
371 line++; |
|
372 lineStart = bp; |
|
373 break; |
|
374 case LF: |
|
375 line++; |
|
376 lineStart = bp; |
|
377 break; |
|
378 } |
|
379 } |
|
380 return bp <= bufLen; |
|
381 } catch (IOException e) { |
|
382 //e.printStackTrace(); |
|
383 // FIXME: include e.getLocalizedMessage() in error message |
|
384 printLines(errWriter, getLocalizedString("source.unavailable")); |
|
385 errWriter.flush(); |
|
386 buf = new char[0]; |
|
387 } |
|
388 return false; |
|
389 } |
255 } |
390 |
256 |
391 /** Print the text of a message, translating newlines appropriately |
257 /** Print the text of a message, translating newlines appropriately |
392 * for the platform. |
258 * for the platform. |
393 */ |
259 */ |
398 msg = msg.substring(nl+1); |
264 msg = msg.substring(nl+1); |
399 } |
265 } |
400 if (msg.length() != 0) writer.println(msg); |
266 if (msg.length() != 0) writer.println(msg); |
401 } |
267 } |
402 |
268 |
403 /** Report an error, unless another error was already reported at same |
269 protected void directError(String key, Object... args) { |
404 * source position. |
270 printLines(errWriter, getLocalizedString(key, args)); |
405 * @param key The key for the localized error message. |
271 errWriter.flush(); |
406 * @param args Fields of the error message. |
|
407 */ |
|
408 public void error(String key, Object ... args) { |
|
409 report(diags.error(source, null, key, args)); |
|
410 } |
|
411 |
|
412 /** Report an error, unless another error was already reported at same |
|
413 * source position. |
|
414 * @param pos The source position at which to report the error. |
|
415 * @param key The key for the localized error message. |
|
416 * @param args Fields of the error message. |
|
417 */ |
|
418 public void error(DiagnosticPosition pos, String key, Object ... args) { |
|
419 report(diags.error(source, pos, key, args)); |
|
420 } |
|
421 |
|
422 /** Report an error, unless another error was already reported at same |
|
423 * source position. |
|
424 * @param pos The source position at which to report the error. |
|
425 * @param key The key for the localized error message. |
|
426 * @param args Fields of the error message. |
|
427 */ |
|
428 public void error(int pos, String key, Object ... args) { |
|
429 report(diags.error(source, wrap(pos), key, args)); |
|
430 } |
|
431 |
|
432 /** Report a warning, unless suppressed by the -nowarn option or the |
|
433 * maximum number of warnings has been reached. |
|
434 * @param pos The source position at which to report the warning. |
|
435 * @param key The key for the localized warning message. |
|
436 * @param args Fields of the warning message. |
|
437 */ |
|
438 public void warning(String key, Object ... args) { |
|
439 report(diags.warning(source, null, key, args)); |
|
440 } |
|
441 |
|
442 /** Report a warning, unless suppressed by the -nowarn option or the |
|
443 * maximum number of warnings has been reached. |
|
444 * @param pos The source position at which to report the warning. |
|
445 * @param key The key for the localized warning message. |
|
446 * @param args Fields of the warning message. |
|
447 */ |
|
448 public void warning(DiagnosticPosition pos, String key, Object ... args) { |
|
449 report(diags.warning(source, pos, key, args)); |
|
450 } |
|
451 |
|
452 /** Report a warning, unless suppressed by the -nowarn option or the |
|
453 * maximum number of warnings has been reached. |
|
454 * @param pos The source position at which to report the warning. |
|
455 * @param key The key for the localized warning message. |
|
456 * @param args Fields of the warning message. |
|
457 */ |
|
458 public void warning(int pos, String key, Object ... args) { |
|
459 report(diags.warning(source, wrap(pos), key, args)); |
|
460 } |
|
461 |
|
462 /** Report a warning. |
|
463 * @param pos The source position at which to report the warning. |
|
464 * @param key The key for the localized warning message. |
|
465 * @param args Fields of the warning message. |
|
466 */ |
|
467 public void mandatoryWarning(DiagnosticPosition pos, String key, Object ... args) { |
|
468 report(diags.mandatoryWarning(source, pos, key, args)); |
|
469 } |
272 } |
470 |
273 |
471 /** Report a warning that cannot be suppressed. |
274 /** Report a warning that cannot be suppressed. |
472 * @param pos The source position at which to report the warning. |
275 * @param pos The source position at which to report the warning. |
473 * @param key The key for the localized warning message. |
276 * @param key The key for the localized warning message. |
474 * @param args Fields of the warning message. |
277 * @param args Fields of the warning message. |
475 */ |
278 */ |
476 public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { |
279 public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { |
477 writeDiagnostic(diags.warning(source, pos, key, args)); |
280 writeDiagnostic(diags.warning(source, pos, key, args)); |
478 nwarnings++; |
281 nwarnings++; |
479 } |
|
480 |
|
481 /** Provide a non-fatal notification, unless suppressed by the -nowarn option. |
|
482 * @param key The key for the localized notification message. |
|
483 * @param args Fields of the notification message. |
|
484 */ |
|
485 public void note(String key, Object ... args) { |
|
486 report(diags.note(source, null, key, args)); |
|
487 } |
|
488 |
|
489 /** Provide a non-fatal notification, unless suppressed by the -nowarn option. |
|
490 * @param key The key for the localized notification message. |
|
491 * @param args Fields of the notification message. |
|
492 */ |
|
493 public void note(DiagnosticPosition pos, String key, Object ... args) { |
|
494 report(diags.note(source, pos, key, args)); |
|
495 } |
|
496 |
|
497 /** Provide a non-fatal notification, unless suppressed by the -nowarn option. |
|
498 * @param key The key for the localized notification message. |
|
499 * @param args Fields of the notification message. |
|
500 */ |
|
501 public void note(int pos, String key, Object ... args) { |
|
502 report(diags.note(source, wrap(pos), key, args)); |
|
503 } |
|
504 |
|
505 /** Provide a non-fatal notification, unless suppressed by the -nowarn option. |
|
506 * @param file The file to which the note applies. |
|
507 * @param key The key for the localized notification message. |
|
508 * @param args Fields of the notification message. |
|
509 */ |
|
510 public void note(JavaFileObject file, String key, Object ... args) { |
|
511 report(diags.note(wrap(file), null, key, args)); |
|
512 } |
|
513 |
|
514 /** Provide a non-fatal notification, unless suppressed by the -nowarn option. |
|
515 * @param key The key for the localized notification message. |
|
516 * @param args Fields of the notification message. |
|
517 */ |
|
518 public void mandatoryNote(final JavaFileObject file, String key, Object ... args) { |
|
519 report(diags.mandatoryNote(wrap(file), key, args)); |
|
520 } |
|
521 |
|
522 private JCDiagnostic.DiagnosticSource wrap(final JavaFileObject file) { |
|
523 if (file == null) { |
|
524 return null; |
|
525 } |
|
526 return new JCDiagnostic.DiagnosticSource() { |
|
527 public JavaFileObject getFile() { |
|
528 return file; |
|
529 } |
|
530 public CharSequence getName() { |
|
531 return JavacFileManager.getJavacBaseFileName(getFile()); |
|
532 } |
|
533 public int getLineNumber(int pos) { |
|
534 return Log.this.getLineNumber(pos); |
|
535 } |
|
536 public int getColumnNumber(int pos) { |
|
537 return Log.this.getColumnNumber(pos); |
|
538 } |
|
539 public Map<JCTree, Integer> getEndPosTable() { |
|
540 return (endPosTables == null ? null : endPosTables.get(file)); |
|
541 } |
|
542 }; |
|
543 } |
|
544 |
|
545 private DiagnosticPosition wrap(int pos) { |
|
546 return (pos == Position.NOPOS ? null : new SimpleDiagnosticPosition(pos)); |
|
547 } |
282 } |
548 |
283 |
549 /** |
284 /** |
550 * Common diagnostic handling. |
285 * Common diagnostic handling. |
551 * The diagnostic is counted, and depending on the options and how many diagnostics have been |
286 * The diagnostic is counted, and depending on the options and how many diagnostics have been |
655 /*************************************************************************** |
390 /*************************************************************************** |
656 * raw error messages without internationalization; used for experimentation |
391 * raw error messages without internationalization; used for experimentation |
657 * and quick prototyping |
392 * and quick prototyping |
658 ***************************************************************************/ |
393 ***************************************************************************/ |
659 |
394 |
660 /** print an error or warning message: |
395 /** print an error or warning message: |
661 */ |
396 */ |
662 private void printRawError(int pos, String msg) { |
397 private void printRawError(int pos, String msg) { |
663 if (!findLine(pos)) { |
398 if (source == null || pos == Position.NOPOS) { |
664 printLines(errWriter, "error: " + msg); |
399 printLines(errWriter, "error: " + msg); |
665 } else { |
400 } else { |
|
401 int line = source.getLineNumber(pos); |
666 JavaFileObject file = currentSource(); |
402 JavaFileObject file = currentSource(); |
667 if (file != null) |
403 if (file != null) |
668 printLines(errWriter, |
404 printLines(errWriter, |
669 JavacFileManager.getJavacFileName(file) + ":" + |
405 JavacFileManager.getJavacFileName(file) + ":" + |
670 line + ": " + msg); |
406 line + ": " + msg); |
671 printErrLine(pos, errWriter); |
407 printErrLine(pos, errWriter); |
672 } |
408 } |
673 errWriter.flush(); |
409 errWriter.flush(); |
674 } |
410 } |
675 |
411 |
676 /** report an error: |
412 /** report an error: |
677 */ |
413 */ |
678 public void rawError(int pos, String msg) { |
414 public void rawError(int pos, String msg) { |
679 if (nerrors < MaxErrors && shouldReport(currentSource(), pos)) { |
415 if (nerrors < MaxErrors && shouldReport(currentSource(), pos)) { |
680 printRawError(pos, msg); |
416 printRawError(pos, msg); |
681 prompt(); |
417 prompt(); |
682 nerrors++; |
418 nerrors++; |
683 } |
419 } |
684 errWriter.flush(); |
420 errWriter.flush(); |
685 } |
421 } |
686 |
422 |
687 /** report a warning: |
423 /** report a warning: |
688 */ |
424 */ |
689 public void rawWarning(int pos, String msg) { |
425 public void rawWarning(int pos, String msg) { |
690 if (nwarnings < MaxWarnings && emitWarnings) { |
426 if (nwarnings < MaxWarnings && emitWarnings) { |
691 printRawError(pos, "warning: " + msg); |
427 printRawError(pos, "warning: " + msg); |
692 } |
428 } |
693 prompt(); |
429 prompt(); |
694 nwarnings++; |
430 nwarnings++; |
695 errWriter.flush(); |
431 errWriter.flush(); |
696 } |
432 } |
697 |
433 |
698 /** Return the one-based line number associated with a given pos |
|
699 * for the current source file. Zero is returned if no line exists |
|
700 * for the given position. |
|
701 */ |
|
702 protected int getLineNumber(int pos) { |
|
703 if (findLine(pos)) |
|
704 return line; |
|
705 return 0; |
|
706 } |
|
707 |
|
708 /** Return the one-based column number associated with a given pos |
|
709 * for the current source file. Zero is returned if no column exists |
|
710 * for the given position. |
|
711 */ |
|
712 protected int getColumnNumber(int pos) { |
|
713 if (findLine(pos)) { |
|
714 int column = 0; |
|
715 for (bp = lineStart; bp < pos; bp++) { |
|
716 if (bp >= bufLen) |
|
717 return 0; |
|
718 if (buf[bp] == '\t') |
|
719 column = (column / TabInc * TabInc) + TabInc; |
|
720 else |
|
721 column++; |
|
722 } |
|
723 return column + 1; // positions are one-based |
|
724 } |
|
725 return 0; |
|
726 } |
|
727 |
|
728 public static String format(String fmt, Object... args) { |
434 public static String format(String fmt, Object... args) { |
729 return String.format((java.util.Locale)null, fmt, args); |
435 return String.format((java.util.Locale)null, fmt, args); |
730 } |
436 } |
731 |
437 |
732 } |
438 } |