Mon, 14 Nov 2011 15:11:10 -0800
7106166: (javac) re-factor EndPos parser
Reviewed-by: jjg
1 /*
2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javac.util;
28 import java.util.EnumSet;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Set;
33 import javax.tools.Diagnostic;
34 import javax.tools.JavaFileObject;
36 import com.sun.tools.javac.api.DiagnosticFormatter;
37 import com.sun.tools.javac.code.Lint.LintCategory;
38 import com.sun.tools.javac.parser.EndPosTable;
39 import com.sun.tools.javac.tree.JCTree;
41 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
43 /** An abstraction of a diagnostic message generated by the compiler.
44 *
45 * <p><b>This is NOT part of any supported API.
46 * If you write code that depends on this, you do so at your own risk.
47 * This code and its internal interfaces are subject to change or
48 * deletion without notice.</b>
49 */
50 public class JCDiagnostic implements Diagnostic<JavaFileObject> {
51 /** A factory for creating diagnostic objects. */
52 public static class Factory {
53 /** The context key for the diagnostic factory. */
54 protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey =
55 new Context.Key<JCDiagnostic.Factory>();
57 /** Get the Factory instance for this context. */
58 public static Factory instance(Context context) {
59 Factory instance = context.get(diagnosticFactoryKey);
60 if (instance == null)
61 instance = new Factory(context);
62 return instance;
63 }
65 DiagnosticFormatter<JCDiagnostic> formatter;
66 final String prefix;
67 final Set<DiagnosticFlag> defaultErrorFlags;
69 /** Create a new diagnostic factory. */
70 protected Factory(Context context) {
71 this(JavacMessages.instance(context), "compiler");
72 context.put(diagnosticFactoryKey, this);
74 final Options options = Options.instance(context);
75 initOptions(options);
76 options.addListener(new Runnable() {
77 public void run() {
78 initOptions(options);
79 }
80 });
81 }
83 private void initOptions(Options options) {
84 if (options.isSet("onlySyntaxErrorsUnrecoverable"))
85 defaultErrorFlags.add(DiagnosticFlag.RECOVERABLE);
86 }
88 /** Create a new diagnostic factory. */
89 public Factory(JavacMessages messages, String prefix) {
90 this.prefix = prefix;
91 this.formatter = new BasicDiagnosticFormatter(messages);
92 defaultErrorFlags = EnumSet.of(DiagnosticFlag.MANDATORY);
93 }
95 /**
96 * Create an error diagnostic.
97 * @param source The source of the compilation unit, if any, in which to report the error.
98 * @param pos The source position at which to report the error.
99 * @param key The key for the localized error message.
100 * @param args Fields of the error message.
101 */
102 public JCDiagnostic error(
103 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
104 return create(ERROR, null, defaultErrorFlags, source, pos, key, args);
105 }
107 /**
108 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
109 * @param source The source of the compilation unit, if any, in which to report the warning.
110 * @param pos The source position at which to report the warning.
111 * @param key The key for the localized warning message.
112 * @param args Fields of the warning message.
113 * @see MandatoryWarningHandler
114 */
115 public JCDiagnostic mandatoryWarning(
116 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
117 return create(WARNING, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args);
118 }
120 /**
121 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
122 * @param lc The lint category for the diagnostic
123 * @param source The source of the compilation unit, if any, in which to report the warning.
124 * @param pos The source position at which to report the warning.
125 * @param key The key for the localized warning message.
126 * @param args Fields of the warning message.
127 * @see MandatoryWarningHandler
128 */
129 public JCDiagnostic mandatoryWarning(
130 LintCategory lc,
131 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
132 return create(WARNING, lc, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args);
133 }
135 /**
136 * Create a warning diagnostic.
137 * @param lc The lint category for the diagnostic
138 * @param key The key for the localized error message.
139 * @param args Fields of the warning message.
140 * @see MandatoryWarningHandler
141 */
142 public JCDiagnostic warning(
143 LintCategory lc, String key, Object... args) {
144 return create(WARNING, lc, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args);
145 }
147 /**
148 * Create a warning diagnostic.
149 * @param source The source of the compilation unit, if any, in which to report the warning.
150 * @param pos The source position at which to report the warning.
151 * @param key The key for the localized warning message.
152 * @param args Fields of the warning message.
153 */
154 public JCDiagnostic warning(
155 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
156 return create(WARNING, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
157 }
159 /**
160 * Create a warning diagnostic.
161 * @param lc The lint category for the diagnostic
162 * @param source The source of the compilation unit, if any, in which to report the warning.
163 * @param pos The source position at which to report the warning.
164 * @param key The key for the localized warning message.
165 * @param args Fields of the warning message.
166 * @see MandatoryWarningHandler
167 */
168 public JCDiagnostic warning(
169 LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
170 return create(WARNING, lc, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
171 }
173 /**
174 * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
175 * @param key The key for the localized message.
176 * @param args Fields of the message.
177 * @see MandatoryWarningHandler
178 */
179 public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
180 return create(NOTE, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, null, key, args);
181 }
183 /**
184 * Create a note diagnostic.
185 * @param key The key for the localized error message.
186 * @param args Fields of the message.
187 */
188 public JCDiagnostic note(String key, Object... args) {
189 return create(NOTE, null, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args);
190 }
192 /**
193 * Create a note diagnostic.
194 * @param source The source of the compilation unit, if any, in which to report the note.
195 * @param pos The source position at which to report the note.
196 * @param key The key for the localized message.
197 * @param args Fields of the message.
198 */
199 public JCDiagnostic note(
200 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
201 return create(NOTE, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
202 }
204 /**
205 * Create a fragment diagnostic, for use as an argument in other diagnostics
206 * @param key The key for the localized message.
207 * @param args Fields of the message.
208 */
209 public JCDiagnostic fragment(String key, Object... args) {
210 return create(FRAGMENT, null, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args);
211 }
213 /**
214 * Create a new diagnostic of the given kind, which is not mandatory and which has
215 * no lint category.
216 * @param kind The diagnostic kind
217 * @param ls The lint category, if applicable, or null
218 * @param source The source of the compilation unit, if any, in which to report the message.
219 * @param pos The source position at which to report the message.
220 * @param key The key for the localized message.
221 * @param args Fields of the message.
222 */
223 public JCDiagnostic create(
224 DiagnosticType kind, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
225 return create(kind, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
226 }
228 /**
229 * Create a new diagnostic of the given kind.
230 * @param kind The diagnostic kind
231 * @param lc The lint category, if applicable, or null
232 * @param isMandatory is diagnostic mandatory?
233 * @param source The source of the compilation unit, if any, in which to report the message.
234 * @param pos The source position at which to report the message.
235 * @param key The key for the localized message.
236 * @param args Fields of the message.
237 */
238 public JCDiagnostic create(
239 DiagnosticType kind, LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
240 return new JCDiagnostic(formatter, kind, lc, flags, source, pos, qualify(kind, key), args);
241 }
243 protected String qualify(DiagnosticType t, String key) {
244 return prefix + "." + t.key + "." + key;
245 }
246 }
250 /**
251 * Create a fragment diagnostic, for use as an argument in other diagnostics
252 * @param key The key for the localized error message.
253 * @param args Fields of the error message.
254 *
255 */
256 @Deprecated
257 public static JCDiagnostic fragment(String key, Object... args) {
258 return new JCDiagnostic(getFragmentFormatter(),
259 FRAGMENT,
260 null,
261 EnumSet.noneOf(DiagnosticFlag.class),
262 null,
263 null,
264 "compiler." + FRAGMENT.key + "." + key,
265 args);
266 }
267 //where
268 @Deprecated
269 public static DiagnosticFormatter<JCDiagnostic> getFragmentFormatter() {
270 if (fragmentFormatter == null) {
271 fragmentFormatter = new BasicDiagnosticFormatter(JavacMessages.getDefaultMessages());
272 }
273 return fragmentFormatter;
274 }
276 /**
277 * A DiagnosticType defines the type of the diagnostic.
278 **/
279 public enum DiagnosticType {
280 /** A fragment of an enclosing diagnostic. */
281 FRAGMENT("misc"),
282 /** A note: similar to, but less serious than, a warning. */
283 NOTE("note"),
284 /** A warning. */
285 WARNING("warn"),
286 /** An error. */
287 ERROR("err");
289 final String key;
291 /** Create a DiagnosticType.
292 * @param key A string used to create the resource key for the diagnostic.
293 */
294 DiagnosticType(String key) {
295 this.key = key;
296 }
297 };
299 /**
300 * A DiagnosticPosition provides information about the positions in a file
301 * that gave rise to a diagnostic. It always defines a "preferred" position
302 * that most accurately defines the location of the diagnostic, it may also
303 * provide a related tree node that spans that location.
304 */
305 public static interface DiagnosticPosition {
306 /** Gets the tree node, if any, to which the diagnostic applies. */
307 JCTree getTree();
308 /** If there is a tree node, get the start position of the tree node.
309 * Otherwise, just returns the same as getPreferredPosition(). */
310 int getStartPosition();
311 /** Get the position within the file that most accurately defines the
312 * location for the diagnostic. */
313 int getPreferredPosition();
314 /** If there is a tree node, and if endPositions are available, get
315 * the end position of the tree node. Otherwise, just returns the
316 * same as getPreferredPosition(). */
317 int getEndPosition(EndPosTable endPosTable);
318 }
320 /**
321 * A DiagnosticPosition that simply identifies a position, but no related
322 * tree node, as the location for a diagnostic. Used for scanner and parser
323 * diagnostics. */
324 public static class SimpleDiagnosticPosition implements DiagnosticPosition {
325 public SimpleDiagnosticPosition(int pos) {
326 this.pos = pos;
327 }
329 public JCTree getTree() {
330 return null;
331 }
333 public int getStartPosition() {
334 return pos;
335 }
337 public int getPreferredPosition() {
338 return pos;
339 }
341 public int getEndPosition(EndPosTable endPosTable) {
342 return pos;
343 }
345 private final int pos;
346 }
348 public enum DiagnosticFlag {
349 MANDATORY,
350 RESOLVE_ERROR,
351 SYNTAX,
352 RECOVERABLE
353 }
355 private final DiagnosticType type;
356 private final DiagnosticSource source;
357 private final DiagnosticPosition position;
358 private final int line;
359 private final int column;
360 private final String key;
361 protected final Object[] args;
362 private final Set<DiagnosticFlag> flags;
363 private final LintCategory lintCategory;
365 /**
366 * Create a diagnostic object.
367 * @param fomatter the formatter to use for the diagnostic
368 * @param dt the type of diagnostic
369 * @param lc the lint category for the diagnostic
370 * @param source the name of the source file, or null if none.
371 * @param pos the character offset within the source file, if given.
372 * @param key a resource key to identify the text of the diagnostic
373 * @param args arguments to be included in the text of the diagnostic
374 */
375 protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
376 DiagnosticType dt,
377 LintCategory lc,
378 Set<DiagnosticFlag> flags,
379 DiagnosticSource source,
380 DiagnosticPosition pos,
381 String key,
382 Object... args) {
383 if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
384 throw new IllegalArgumentException();
386 this.defaultFormatter = formatter;
387 this.type = dt;
388 this.lintCategory = lc;
389 this.flags = flags;
390 this.source = source;
391 this.position = pos;
392 this.key = key;
393 this.args = args;
395 int n = (pos == null ? Position.NOPOS : pos.getPreferredPosition());
396 if (n == Position.NOPOS || source == null)
397 line = column = -1;
398 else {
399 line = source.getLineNumber(n);
400 column = source.getColumnNumber(n, true);
401 }
402 }
404 /**
405 * Get the type of this diagnostic.
406 * @return the type of this diagnostic
407 */
408 public DiagnosticType getType() {
409 return type;
410 }
412 /**
413 * Get the subdiagnostic list
414 * @return subdiagnostic list
415 */
416 public List<JCDiagnostic> getSubdiagnostics() {
417 return List.nil();
418 }
420 public boolean isMultiline() {
421 return false;
422 }
424 /**
425 * Check whether or not this diagnostic is required to be shown.
426 * @return true if this diagnostic is required to be shown.
427 */
428 public boolean isMandatory() {
429 return flags.contains(DiagnosticFlag.MANDATORY);
430 }
432 /**
433 * Check whether this diagnostic has an associated lint category.
434 */
435 public boolean hasLintCategory() {
436 return (lintCategory != null);
437 }
439 /**
440 * Get the associated lint category, or null if none.
441 */
442 public LintCategory getLintCategory() {
443 return lintCategory;
444 }
446 /**
447 * Get the name of the source file referred to by this diagnostic.
448 * @return the name of the source referred to with this diagnostic, or null if none
449 */
450 public JavaFileObject getSource() {
451 if (source == null)
452 return null;
453 else
454 return source.getFile();
455 }
457 /**
458 * Get the source referred to by this diagnostic.
459 * @return the source referred to with this diagnostic, or null if none
460 */
461 public DiagnosticSource getDiagnosticSource() {
462 return source;
463 }
465 protected int getIntStartPosition() {
466 return (position == null ? Position.NOPOS : position.getStartPosition());
467 }
469 protected int getIntPosition() {
470 return (position == null ? Position.NOPOS : position.getPreferredPosition());
471 }
473 protected int getIntEndPosition() {
474 return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
475 }
477 public long getStartPosition() {
478 return getIntStartPosition();
479 }
481 public long getPosition() {
482 return getIntPosition();
483 }
485 public long getEndPosition() {
486 return getIntEndPosition();
487 }
489 /**
490 * Get the line number within the source referred to by this diagnostic.
491 * @return the line number within the source referred to by this diagnostic
492 */
493 public long getLineNumber() {
494 return line;
495 }
497 /**
498 * Get the column number within the line of source referred to by this diagnostic.
499 * @return the column number within the line of source referred to by this diagnostic
500 */
501 public long getColumnNumber() {
502 return column;
503 }
505 /**
506 * Get the arguments to be included in the text of the diagnostic.
507 * @return the arguments to be included in the text of the diagnostic
508 */
509 public Object[] getArgs() {
510 return args;
511 }
513 /**
514 * Get the prefix string associated with this type of diagnostic.
515 * @return the prefix string associated with this type of diagnostic
516 */
517 public String getPrefix() {
518 return getPrefix(type);
519 }
521 /**
522 * Get the prefix string associated with a particular type of diagnostic.
523 * @return the prefix string associated with a particular type of diagnostic
524 */
525 public String getPrefix(DiagnosticType dt) {
526 return defaultFormatter.formatKind(this, Locale.getDefault());
527 }
529 /**
530 * Return the standard presentation of this diagnostic.
531 */
532 @Override
533 public String toString() {
534 return defaultFormatter.format(this,Locale.getDefault());
535 }
537 private DiagnosticFormatter<JCDiagnostic> defaultFormatter;
538 @Deprecated
539 private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter;
541 // Methods for javax.tools.Diagnostic
543 public Diagnostic.Kind getKind() {
544 switch (type) {
545 case NOTE:
546 return Diagnostic.Kind.NOTE;
547 case WARNING:
548 return flags.contains(DiagnosticFlag.MANDATORY)
549 ? Diagnostic.Kind.MANDATORY_WARNING
550 : Diagnostic.Kind.WARNING;
551 case ERROR:
552 return Diagnostic.Kind.ERROR;
553 default:
554 return Diagnostic.Kind.OTHER;
555 }
556 }
558 public String getCode() {
559 return key;
560 }
562 public String getMessage(Locale locale) {
563 return defaultFormatter.formatMessage(this, locale);
564 }
566 public void setFlag(DiagnosticFlag flag) {
567 flags.add(flag);
569 if (type == DiagnosticType.ERROR) {
570 switch (flag) {
571 case SYNTAX:
572 flags.remove(DiagnosticFlag.RECOVERABLE);
573 break;
574 case RESOLVE_ERROR:
575 flags.add(DiagnosticFlag.RECOVERABLE);
576 break;
577 }
578 }
579 }
581 public boolean isFlagSet(DiagnosticFlag flag) {
582 return flags.contains(flag);
583 }
585 public static class MultilineDiagnostic extends JCDiagnostic {
587 private final List<JCDiagnostic> subdiagnostics;
589 public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) {
590 super(other.defaultFormatter,
591 other.getType(),
592 other.getLintCategory(),
593 other.flags,
594 other.getDiagnosticSource(),
595 other.position,
596 other.getCode(),
597 other.getArgs());
598 this.subdiagnostics = subdiagnostics;
599 }
601 @Override
602 public List<JCDiagnostic> getSubdiagnostics() {
603 return subdiagnostics;
604 }
606 @Override
607 public boolean isMultiline() {
608 return true;
609 }
610 }
611 }