Wed, 02 Jul 2008 12:56:02 -0700
6719955: Update copyright year
Summary: Update copyright year for files that have been modified in 2008
Reviewed-by: ohair, tbell
1 /*
2 * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.javac.util;
28 import java.io.*;
29 import java.nio.CharBuffer;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Map;
33 import java.util.Set;
34 import javax.tools.DiagnosticListener;
35 import javax.tools.JavaFileObject;
37 import com.sun.tools.javac.code.Source;
38 import com.sun.tools.javac.file.JavacFileManager;
39 import com.sun.tools.javac.tree.JCTree;
40 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
41 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
42 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
44 import static com.sun.tools.javac.util.LayoutCharacters.*;
46 /** A class for error logs. Reports errors and warnings, and
47 * keeps track of error numbers and positions.
48 *
49 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
50 * you write code that depends on this, you do so at your own risk.
51 * This code and its internal interfaces are subject to change or
52 * deletion without notice.</b>
53 */
54 public class Log {
55 /** The context key for the log. */
56 public static final Context.Key<Log> logKey
57 = new Context.Key<Log>();
59 /** The context key for the output PrintWriter. */
60 public static final Context.Key<PrintWriter> outKey =
61 new Context.Key<PrintWriter>();
63 //@Deprecated
64 public final PrintWriter errWriter;
66 //@Deprecated
67 public final PrintWriter warnWriter;
69 //@Deprecated
70 public final PrintWriter noticeWriter;
72 /** The maximum number of errors/warnings that are reported.
73 */
74 public final int MaxErrors;
75 public final int MaxWarnings;
77 /** Whether or not to display the line of source containing a diagnostic.
78 */
79 private final boolean showSourceLine;
81 /** Switch: prompt user on each error.
82 */
83 public boolean promptOnError;
85 /** Switch: emit warning messages.
86 */
87 public boolean emitWarnings;
89 /** Enforce mandatory warnings.
90 */
91 private boolean enforceMandatoryWarnings;
93 /** Print stack trace on errors?
94 */
95 public boolean dumpOnError;
97 /** Print multiple errors for same source locations.
98 */
99 public boolean multipleErrors;
101 /**
102 * Diagnostic listener, if provided through programmatic
103 * interface to javac (JSR 199).
104 */
105 protected DiagnosticListener<? super JavaFileObject> diagListener;
106 /**
107 * Formatter for diagnostics
108 */
109 private DiagnosticFormatter diagFormatter;
111 /**
112 * Factory for diagnostics
113 */
114 private JCDiagnostic.Factory diags;
117 /** Construct a log with given I/O redirections.
118 */
119 @Deprecated
120 protected Log(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
121 context.put(logKey, this);
122 this.errWriter = errWriter;
123 this.warnWriter = warnWriter;
124 this.noticeWriter = noticeWriter;
126 this.diags = JCDiagnostic.Factory.instance(context);
128 Options options = Options.instance(context);
129 this.dumpOnError = options.get("-doe") != null;
130 this.promptOnError = options.get("-prompt") != null;
131 this.emitWarnings = options.get("-Xlint:none") == null;
132 this.MaxErrors = getIntOption(options, "-Xmaxerrs", 100);
133 this.MaxWarnings = getIntOption(options, "-Xmaxwarns", 100);
134 this.showSourceLine = options.get("rawDiagnostics") == null;
136 this.diagFormatter = DiagnosticFormatter.instance(context);
137 @SuppressWarnings("unchecked") // FIXME
138 DiagnosticListener<? super JavaFileObject> diagListener =
139 context.get(DiagnosticListener.class);
140 this.diagListener = diagListener;
142 Source source = Source.instance(context);
143 this.enforceMandatoryWarnings = source.enforceMandatoryWarnings();
144 }
145 // where
146 private int getIntOption(Options options, String optionName, int defaultValue) {
147 String s = options.get(optionName);
148 try {
149 if (s != null) return Integer.parseInt(s);
150 } catch (NumberFormatException e) {
151 // silently ignore ill-formed numbers
152 }
153 return defaultValue;
154 }
156 /** The default writer for diagnostics
157 */
158 static final PrintWriter defaultWriter(Context context) {
159 PrintWriter result = context.get(outKey);
160 if (result == null)
161 context.put(outKey, result = new PrintWriter(System.err));
162 return result;
163 }
165 /** Construct a log with default settings.
166 */
167 protected Log(Context context) {
168 this(context, defaultWriter(context));
169 }
171 /** Construct a log with all output redirected.
172 */
173 protected Log(Context context, PrintWriter defaultWriter) {
174 this(context, defaultWriter, defaultWriter, defaultWriter);
175 }
177 /** Get the Log instance for this context. */
178 public static Log instance(Context context) {
179 Log instance = context.get(logKey);
180 if (instance == null)
181 instance = new Log(context);
182 return instance;
183 }
185 /** The file that's currently translated.
186 */
187 protected JCDiagnostic.DiagnosticSource source;
189 /** The number of errors encountered so far.
190 */
191 public int nerrors = 0;
193 /** The number of warnings encountered so far.
194 */
195 public int nwarnings = 0;
197 /** A set of all errors generated so far. This is used to avoid printing an
198 * error message more than once. For each error, a pair consisting of the
199 * source file name and source code position of the error is added to the set.
200 */
201 private Set<Pair<JavaFileObject, Integer>> recorded = new HashSet<Pair<JavaFileObject,Integer>>();
203 private Map<JavaFileObject, Map<JCTree, Integer>> endPosTables;
205 /** The buffer containing the file that's currently translated.
206 */
207 private char[] buf = null;
209 /** The length of useful data in buf
210 */
211 private int bufLen = 0;
213 /** The position in the buffer at which last error was reported
214 */
215 private int bp;
217 /** number of the current source line; first line is 1
218 */
219 private int line;
221 /** buffer index of the first character of the current source line
222 */
223 private int lineStart;
225 public boolean hasDiagnosticListener() {
226 return diagListener != null;
227 }
229 public void setEndPosTable(JavaFileObject name, Map<JCTree, Integer> table) {
230 if (endPosTables == null)
231 endPosTables = new HashMap<JavaFileObject, Map<JCTree, Integer>>();
232 endPosTables.put(name, table);
233 }
235 /** Re-assign source, returning previous setting.
236 */
237 public JavaFileObject useSource(final JavaFileObject name) {
238 JavaFileObject prev = currentSource();
239 if (name != prev) {
240 source = new JCDiagnostic.DiagnosticSource() {
241 public JavaFileObject getFile() {
242 return name;
243 }
244 public CharSequence getName() {
245 return JavacFileManager.getJavacBaseFileName(getFile());
246 }
247 public int getLineNumber(int pos) {
248 return Log.this.getLineNumber(pos);
249 }
250 public int getColumnNumber(int pos) {
251 return Log.this.getColumnNumber(pos);
252 }
253 public Map<JCTree, Integer> getEndPosTable() {
254 return (endPosTables == null ? null : endPosTables.get(name));
255 }
256 };
257 buf = null;
258 }
259 return prev;
260 }
262 /** Re-assign source buffer for existing source name.
263 */
264 protected void setBuf(char[] newBuf) {
265 buf = newBuf;
266 bufLen = buf.length;
267 bp = 0;
268 lineStart = 0;
269 line = 1;
270 }
272 protected char[] getBuf() {
273 return buf;
274 }
276 /** Return current source name.
277 */
278 public JavaFileObject currentSource() {
279 return source == null ? null : source.getFile();
280 }
282 /** Flush the logs
283 */
284 public void flush() {
285 errWriter.flush();
286 warnWriter.flush();
287 noticeWriter.flush();
288 }
290 /** Returns true if an error needs to be reported for a given
291 * source name and pos.
292 */
293 protected boolean shouldReport(JavaFileObject file, int pos) {
294 if (multipleErrors || file == null)
295 return true;
297 Pair<JavaFileObject,Integer> coords = new Pair<JavaFileObject,Integer>(file, pos);
298 boolean shouldReport = !recorded.contains(coords);
299 if (shouldReport)
300 recorded.add(coords);
301 return shouldReport;
302 }
304 /** Prompt user after an error.
305 */
306 public void prompt() {
307 if (promptOnError) {
308 System.err.println(getLocalizedString("resume.abort"));
309 char ch;
310 try {
311 while (true) {
312 switch (System.in.read()) {
313 case 'a': case 'A':
314 System.exit(-1);
315 return;
316 case 'r': case 'R':
317 return;
318 case 'x': case 'X':
319 throw new AssertionError("user abort");
320 default:
321 }
322 }
323 } catch (IOException e) {}
324 }
325 }
327 /** Print the faulty source code line and point to the error.
328 * @param pos Buffer index of the error position, must be on current line
329 */
330 private void printErrLine(int pos, PrintWriter writer) {
331 if (!findLine(pos))
332 return;
334 int lineEnd = lineStart;
335 while (lineEnd < bufLen && buf[lineEnd] != CR && buf[lineEnd] != LF)
336 lineEnd++;
337 if (lineEnd - lineStart == 0)
338 return;
339 printLines(writer, new String(buf, lineStart, lineEnd - lineStart));
340 for (bp = lineStart; bp < pos; bp++) {
341 writer.print((buf[bp] == '\t') ? "\t" : " ");
342 }
343 writer.println("^");
344 writer.flush();
345 }
347 protected void initBuf(JavaFileObject fileObject) throws IOException {
348 CharSequence cs = fileObject.getCharContent(true);
349 if (cs instanceof CharBuffer) {
350 CharBuffer cb = (CharBuffer) cs;
351 buf = JavacFileManager.toArray(cb);
352 bufLen = cb.limit();
353 } else {
354 buf = cs.toString().toCharArray();
355 bufLen = buf.length;
356 }
357 }
359 /** Find the line in the buffer that contains the current position
360 * @param pos Character offset into the buffer
361 */
362 private boolean findLine(int pos) {
363 if (pos == Position.NOPOS || currentSource() == null)
364 return false;
365 try {
366 if (buf == null) {
367 initBuf(currentSource());
368 lineStart = 0;
369 line = 1;
370 } else if (lineStart > pos) { // messages don't come in order
371 lineStart = 0;
372 line = 1;
373 }
374 bp = lineStart;
375 while (bp < bufLen && bp < pos) {
376 switch (buf[bp++]) {
377 case CR:
378 if (bp < bufLen && buf[bp] == LF) bp++;
379 line++;
380 lineStart = bp;
381 break;
382 case LF:
383 line++;
384 lineStart = bp;
385 break;
386 }
387 }
388 return bp <= bufLen;
389 } catch (IOException e) {
390 //e.printStackTrace();
391 // FIXME: include e.getLocalizedMessage() in error message
392 printLines(errWriter, getLocalizedString("source.unavailable"));
393 errWriter.flush();
394 buf = new char[0];
395 }
396 return false;
397 }
399 /** Print the text of a message, translating newlines appropriately
400 * for the platform.
401 */
402 public static void printLines(PrintWriter writer, String msg) {
403 int nl;
404 while ((nl = msg.indexOf('\n')) != -1) {
405 writer.println(msg.substring(0, nl));
406 msg = msg.substring(nl+1);
407 }
408 if (msg.length() != 0) writer.println(msg);
409 }
411 /** Report an error, unless another error was already reported at same
412 * source position.
413 * @param key The key for the localized error message.
414 * @param args Fields of the error message.
415 */
416 public void error(String key, Object ... args) {
417 report(diags.error(source, null, key, args));
418 }
420 /** Report an error, unless another error was already reported at same
421 * source position.
422 * @param pos The source position at which to report the error.
423 * @param key The key for the localized error message.
424 * @param args Fields of the error message.
425 */
426 public void error(DiagnosticPosition pos, String key, Object ... args) {
427 report(diags.error(source, pos, key, args));
428 }
430 /** Report an error, unless another error was already reported at same
431 * source position.
432 * @param pos The source position at which to report the error.
433 * @param key The key for the localized error message.
434 * @param args Fields of the error message.
435 */
436 public void error(int pos, String key, Object ... args) {
437 report(diags.error(source, wrap(pos), key, args));
438 }
440 /** Report a warning, unless suppressed by the -nowarn option or the
441 * maximum number of warnings has been reached.
442 * @param pos The source position at which to report the warning.
443 * @param key The key for the localized warning message.
444 * @param args Fields of the warning message.
445 */
446 public void warning(String key, Object ... args) {
447 report(diags.warning(source, null, key, args));
448 }
450 /** Report a warning, unless suppressed by the -nowarn option or the
451 * maximum number of warnings has been reached.
452 * @param pos The source position at which to report the warning.
453 * @param key The key for the localized warning message.
454 * @param args Fields of the warning message.
455 */
456 public void warning(DiagnosticPosition pos, String key, Object ... args) {
457 report(diags.warning(source, pos, key, args));
458 }
460 /** Report a warning, unless suppressed by the -nowarn option or the
461 * maximum number of warnings has been reached.
462 * @param pos The source position at which to report the warning.
463 * @param key The key for the localized warning message.
464 * @param args Fields of the warning message.
465 */
466 public void warning(int pos, String key, Object ... args) {
467 report(diags.warning(source, wrap(pos), key, args));
468 }
470 /** Report a warning.
471 * @param pos The source position at which to report the warning.
472 * @param key The key for the localized warning message.
473 * @param args Fields of the warning message.
474 */
475 public void mandatoryWarning(DiagnosticPosition pos, String key, Object ... args) {
476 if (enforceMandatoryWarnings)
477 report(diags.mandatoryWarning(source, pos, key, args));
478 else
479 report(diags.warning(source, pos, key, args));
480 }
482 /** Report a warning that cannot be suppressed.
483 * @param pos The source position at which to report the warning.
484 * @param key The key for the localized warning message.
485 * @param args Fields of the warning message.
486 */
487 public void strictWarning(DiagnosticPosition pos, String key, Object ... args) {
488 writeDiagnostic(diags.warning(source, pos, key, args));
489 nwarnings++;
490 }
492 /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
493 * @param key The key for the localized notification message.
494 * @param args Fields of the notification message.
495 */
496 public void note(String key, Object ... args) {
497 report(diags.note(source, null, key, args));
498 }
500 /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
501 * @param key The key for the localized notification message.
502 * @param args Fields of the notification message.
503 */
504 public void note(DiagnosticPosition pos, String key, Object ... args) {
505 report(diags.note(source, pos, key, args));
506 }
508 /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
509 * @param key The key for the localized notification message.
510 * @param args Fields of the notification message.
511 */
512 public void note(int pos, String key, Object ... args) {
513 report(diags.note(source, wrap(pos), key, args));
514 }
516 /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
517 * @param key The key for the localized notification message.
518 * @param args Fields of the notification message.
519 */
520 public void mandatoryNote(final JavaFileObject file, String key, Object ... args) {
521 JCDiagnostic.DiagnosticSource wrapper = null;
522 if (file != null) {
523 wrapper = new JCDiagnostic.DiagnosticSource() {
524 public JavaFileObject getFile() {
525 return file;
526 }
527 public CharSequence getName() {
528 return JavacFileManager.getJavacBaseFileName(getFile());
529 }
530 public int getLineNumber(int pos) {
531 return Log.this.getLineNumber(pos);
532 }
533 public int getColumnNumber(int pos) {
534 return Log.this.getColumnNumber(pos);
535 }
536 public Map<JCTree, Integer> getEndPosTable() {
537 return (endPosTables == null ? null : endPosTables.get(file));
538 }
539 };
540 }
541 if (enforceMandatoryWarnings)
542 report(diags.mandatoryNote(wrapper, key, args));
543 else
544 report(diags.note(wrapper, null, key, args));
545 }
547 private DiagnosticPosition wrap(int pos) {
548 return (pos == Position.NOPOS ? null : new SimpleDiagnosticPosition(pos));
549 }
551 /**
552 * Common diagnostic handling.
553 * The diagnostic is counted, and depending on the options and how many diagnostics have been
554 * reported so far, the diagnostic may be handed off to writeDiagnostic.
555 */
556 public void report(JCDiagnostic diagnostic) {
557 switch (diagnostic.getType()) {
558 case FRAGMENT:
559 throw new IllegalArgumentException();
561 case NOTE:
562 // Print out notes only when we are permitted to report warnings
563 // Notes are only generated at the end of a compilation, so should be small
564 // in number.
565 if (emitWarnings || diagnostic.isMandatory()) {
566 writeDiagnostic(diagnostic);
567 }
568 break;
570 case WARNING:
571 if (emitWarnings || diagnostic.isMandatory()) {
572 if (nwarnings < MaxWarnings) {
573 writeDiagnostic(diagnostic);
574 nwarnings++;
575 }
576 }
577 break;
579 case ERROR:
580 if (nerrors < MaxErrors
581 && shouldReport(diagnostic.getSource(), diagnostic.getIntPosition())) {
582 writeDiagnostic(diagnostic);
583 nerrors++;
584 }
585 break;
586 }
587 }
589 /**
590 * Write out a diagnostic.
591 */
592 protected void writeDiagnostic(JCDiagnostic diag) {
593 if (diagListener != null) {
594 try {
595 diagListener.report(diag);
596 return;
597 }
598 catch (Throwable t) {
599 throw new ClientCodeException(t);
600 }
601 }
603 PrintWriter writer = getWriterForDiagnosticType(diag.getType());
605 printLines(writer, diagFormatter.format(diag));
606 if (showSourceLine) {
607 int pos = diag.getIntPosition();
608 if (pos != Position.NOPOS) {
609 JavaFileObject prev = useSource(diag.getSource());
610 printErrLine(pos, writer);
611 useSource(prev);
612 }
613 }
615 if (promptOnError) {
616 switch (diag.getType()) {
617 case ERROR:
618 case WARNING:
619 prompt();
620 }
621 }
623 if (dumpOnError)
624 new RuntimeException().printStackTrace(writer);
626 writer.flush();
627 }
629 @Deprecated
630 protected PrintWriter getWriterForDiagnosticType(DiagnosticType dt) {
631 switch (dt) {
632 case FRAGMENT:
633 throw new IllegalArgumentException();
635 case NOTE:
636 return noticeWriter;
638 case WARNING:
639 return warnWriter;
641 case ERROR:
642 return errWriter;
644 default:
645 throw new Error();
646 }
647 }
649 /** Find a localized string in the resource bundle.
650 * @param key The key for the localized string.
651 * @param args Fields to substitute into the string.
652 */
653 public static String getLocalizedString(String key, Object ... args) {
654 return Messages.getDefaultLocalizedString("compiler.misc." + key, args);
655 }
657 /***************************************************************************
658 * raw error messages without internationalization; used for experimentation
659 * and quick prototyping
660 ***************************************************************************/
662 /** print an error or warning message:
663 */
664 private void printRawError(int pos, String msg) {
665 if (!findLine(pos)) {
666 printLines(errWriter, "error: " + msg);
667 } else {
668 JavaFileObject file = currentSource();
669 if (file != null)
670 printLines(errWriter,
671 JavacFileManager.getJavacFileName(file) + ":" +
672 line + ": " + msg);
673 printErrLine(pos, errWriter);
674 }
675 errWriter.flush();
676 }
678 /** report an error:
679 */
680 public void rawError(int pos, String msg) {
681 if (nerrors < MaxErrors && shouldReport(currentSource(), pos)) {
682 printRawError(pos, msg);
683 prompt();
684 nerrors++;
685 }
686 errWriter.flush();
687 }
689 /** report a warning:
690 */
691 public void rawWarning(int pos, String msg) {
692 if (nwarnings < MaxWarnings && emitWarnings) {
693 printRawError(pos, "warning: " + msg);
694 }
695 prompt();
696 nwarnings++;
697 errWriter.flush();
698 }
700 /** Return the one-based line number associated with a given pos
701 * for the current source file. Zero is returned if no line exists
702 * for the given position.
703 */
704 protected int getLineNumber(int pos) {
705 if (findLine(pos))
706 return line;
707 return 0;
708 }
710 /** Return the one-based column number associated with a given pos
711 * for the current source file. Zero is returned if no column exists
712 * for the given position.
713 */
714 protected int getColumnNumber(int pos) {
715 if (findLine(pos)) {
716 int column = 0;
717 for (bp = lineStart; bp < pos; bp++) {
718 if (bp >= bufLen)
719 return 0;
720 if (buf[bp] == '\t')
721 column = (column / TabInc * TabInc) + TabInc;
722 else
723 column++;
724 }
725 return column + 1; // positions are one-based
726 }
727 return 0;
728 }
730 public static String format(String fmt, Object... args) {
731 return String.format((java.util.Locale)null, fmt, args);
732 }
734 }