1 /*
2  * Copyright (c) 2003, 2018, 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  */
25 
26 package com.sun.tools.javac.util;
27 
28 import java.util.EnumSet;
29 import java.util.Locale;
30 import java.util.Set;
31 import java.util.stream.Stream;
32 
33 import javax.tools.Diagnostic;
34 import javax.tools.JavaFileObject;
35 
36 import com.sun.tools.javac.api.DiagnosticFormatter;
37 import com.sun.tools.javac.code.Lint.LintCategory;
38 import com.sun.tools.javac.tree.EndPosTable;
39 import com.sun.tools.javac.tree.JCTree;
40 import com.sun.tools.javac.util.DefinedBy.Api;
41 
42 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
43 
44 /** An abstraction of a diagnostic message generated by the compiler.
45  *
46  *  <p><b>This is NOT part of any supported API.
47  *  If you write code that depends on this, you do so at your own risk.
48  *  This code and its internal interfaces are subject to change or
49  *  deletion without notice.</b>
50  */
51 public class JCDiagnostic implements Diagnostic<JavaFileObject> {
52     /** A factory for creating diagnostic objects. */
53     public static class Factory {
54         /** The context key for the diagnostic factory. */
55         protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey = new Context.Key<>();
56 
57         /** Get the Factory instance for this context. */
instance(Context 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         }
64 
65         DiagnosticFormatter<JCDiagnostic> formatter;
66         final String prefix;
67         final Set<DiagnosticFlag> defaultErrorFlags;
68 
69         /** Create a new diagnostic factory. */
Factory(Context context)70         protected Factory(Context context) {
71             this(JavacMessages.instance(context), "compiler");
72             context.put(diagnosticFactoryKey, this);
73 
74             final Options options = Options.instance(context);
75             initOptions(options);
76             options.addListener(() -> initOptions(options));
77         }
78 
initOptions(Options options)79         private void initOptions(Options options) {
80             if (options.isSet("onlySyntaxErrorsUnrecoverable"))
81                 defaultErrorFlags.add(DiagnosticFlag.RECOVERABLE);
82         }
83 
84         /** Create a new diagnostic factory. */
Factory(JavacMessages messages, String prefix)85         public Factory(JavacMessages messages, String prefix) {
86             this.prefix = prefix;
87             this.formatter = new BasicDiagnosticFormatter(messages);
88             defaultErrorFlags = EnumSet.of(DiagnosticFlag.MANDATORY);
89         }
90 
91         /**
92          * Create an error diagnostic
93          *  @param source The source of the compilation unit, if any, in which to report the error.
94          *  @param pos    The source position at which to report the error.
95          *  @param key    The key for the localized error message.
96          *  @param args   Fields of the error message.
97          */
error( DiagnosticFlag flag, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args)98         public JCDiagnostic error(
99                 DiagnosticFlag flag, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
100             return error(flag, source, pos, errorKey(key, args));
101         }
102 
103         /**
104          * Create an error diagnostic
105          *  @param source The source of the compilation unit, if any, in which to report the error.
106          *  @param pos    The source position at which to report the error.
107          *  @param errorKey    The key for the localized error message.
108          */
error( DiagnosticFlag flag, DiagnosticSource source, DiagnosticPosition pos, Error errorKey)109         public JCDiagnostic error(
110                 DiagnosticFlag flag, DiagnosticSource source, DiagnosticPosition pos, Error errorKey) {
111             JCDiagnostic diag = create(null, EnumSet.copyOf(defaultErrorFlags), source, pos, errorKey);
112             if (flag != null) {
113                 diag.setFlag(flag);
114             }
115             return diag;
116         }
117 
118         /**
119          * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
120          *  @param lc     The lint category for the diagnostic
121          *  @param source The source of the compilation unit, if any, in which to report the warning.
122          *  @param pos    The source position at which to report the warning.
123          *  @param key    The key for the localized warning message.
124          *  @param args   Fields of the warning message.
125          *  @see MandatoryWarningHandler
126          */
mandatoryWarning( LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args)127         public JCDiagnostic mandatoryWarning(
128                 LintCategory lc,
129                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
130             return mandatoryWarning(lc, source, pos, warningKey(key, args));
131         }
132 
133         /**
134          * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
135          *  @param lc     The lint category for the diagnostic
136          *  @param source The source of the compilation unit, if any, in which to report the warning.
137          *  @param pos    The source position at which to report the warning.
138          *  @param warningKey    The key for the localized warning message.
139          *  @see MandatoryWarningHandler
140          */
mandatoryWarning( LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, Warning warningKey)141         public JCDiagnostic mandatoryWarning(
142                 LintCategory lc,
143                 DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) {
144             return create(lc, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, warningKey);
145         }
146 
147         /**
148          * Create a warning diagnostic.
149          *  @param lc     The lint category for the diagnostic
150          *  @param source The source of the compilation unit, if any, in which to report the warning.
151          *  @param pos    The source position at which to report the warning.
152          *  @param key    The key for the localized error message.
153          *  @param args   Fields of the warning message.
154          *  @see MandatoryWarningHandler
155          */
warning( LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args)156         public JCDiagnostic warning(
157                 LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
158             return warning(lc, source, pos, warningKey(key, args));
159         }
160 
161         /**
162          * Create a warning diagnostic.
163          *  @param lc     The lint category for the diagnostic
164          *  @param source The source of the compilation unit, if any, in which to report the warning.
165          *  @param pos    The source position at which to report the warning.
166          *  @param warningKey    The key for the localized warning message.
167          *  @see MandatoryWarningHandler
168          */
warning( LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, Warning warningKey)169         public JCDiagnostic warning(
170                 LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) {
171             return create(lc, EnumSet.noneOf(DiagnosticFlag.class), source, pos, warningKey);
172         }
173 
174         /**
175          * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
176          *  @param source The source of the compilation unit, if any, in which to report the warning.
177          *  @param key    The key for the localized warning message.
178          *  @param args   Fields of the warning message.
179          *  @see MandatoryWarningHandler
180          */
mandatoryNote(DiagnosticSource source, String key, Object... args)181         public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
182             return mandatoryNote(source, noteKey(key, args));
183         }
184 
185         /**
186          * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
187          *  @param noteKey    The key for the localized note message.
188          *  @see MandatoryWarningHandler
189          */
mandatoryNote(DiagnosticSource source, Note noteKey)190         public JCDiagnostic mandatoryNote(DiagnosticSource source, Note noteKey) {
191             return create(null, EnumSet.of(DiagnosticFlag.MANDATORY), source, null, noteKey);
192         }
193 
194         /**
195          * Create a note diagnostic.
196          *  @param key    The key for the localized error message.
197          *  @param args   Fields of the message.
198          */
note( DiagnosticSource source, DiagnosticPosition pos, String key, Object... args)199         public JCDiagnostic note(
200                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
201             return note(source, pos, noteKey(key, args));
202         }
203 
204         /**
205          * Create a note diagnostic.
206          *  @param source The source of the compilation unit, if any, in which to report the note.
207          *  @param pos    The source position at which to report the note.
208          *  @param noteKey    The key for the localized note message.
209          */
note( DiagnosticSource source, DiagnosticPosition pos, Note noteKey)210         public JCDiagnostic note(
211                 DiagnosticSource source, DiagnosticPosition pos, Note noteKey) {
212             return create(null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, noteKey);
213         }
214 
215         /**
216          * Create a fragment diagnostic, for use as an argument in other diagnostics
217          *  @param key    The key for the localized message.
218          *  @param args   Fields of the message.
219          */
fragment(String key, Object... args)220         public JCDiagnostic fragment(String key, Object... args) {
221             return fragment(fragmentKey(key, args));
222         }
223 
224         /**
225          * Create a fragment diagnostic, for use as an argument in other diagnostics
226          *  @param fragmentKey    The key for the localized subdiagnostic message.
227          */
fragment(Fragment fragmentKey)228         public JCDiagnostic fragment(Fragment fragmentKey) {
229             return create(null, EnumSet.noneOf(DiagnosticFlag.class), null, null, fragmentKey);
230         }
231 
232         /**
233          * Create a new diagnostic of the given kind, which is not mandatory and which has
234          * no lint category.
235          *  @param kind        The diagnostic kind
236          *  @param source      The source of the compilation unit, if any, in which to report the message.
237          *  @param pos         The source position at which to report the message.
238          *  @param key         The key for the localized message.
239          *  @param args        Fields of the message.
240          */
create( DiagnosticType kind, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args)241         public JCDiagnostic create(
242                 DiagnosticType kind, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
243             return create(null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, DiagnosticInfo.of(kind, prefix, key, args));
244         }
245 
246         /**
247          * Create a new diagnostic of the given kind, which is not mandatory and which has
248          * no lint category.
249          *  @param source      The source of the compilation unit, if any, in which to report the message.
250          *  @param pos         The source position at which to report the message.
251          *  @param diagnosticInfo         The key for the localized message.
252          */
create( DiagnosticSource source, DiagnosticPosition pos, DiagnosticInfo diagnosticInfo)253         public JCDiagnostic create(
254                 DiagnosticSource source, DiagnosticPosition pos, DiagnosticInfo diagnosticInfo) {
255             return create(null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, diagnosticInfo);
256         }
257 
258         /**
259          * Create a new diagnostic of the given kind.
260          *  @param kind        The diagnostic kind
261          *  @param lc          The lint category, if applicable, or null
262          *  @param flags       The set of flags for the diagnostic
263          *  @param source      The source of the compilation unit, if any, in which to report the message.
264          *  @param pos         The source position at which to report the message.
265          *  @param key         The key for the localized message.
266          *  @param args        Fields of the message.
267          */
create(DiagnosticType kind, LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args)268         public JCDiagnostic create(DiagnosticType kind,
269                 LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
270             return create(lc, flags, source, pos, DiagnosticInfo.of(kind, prefix, key, args));
271         }
272 
273         /**
274          * Create a new diagnostic with given key.
275          *  @param lc          The lint category, if applicable, or null
276          *  @param flags       The set of flags for the diagnostic
277          *  @param source      The source of the compilation unit, if any, in which to report the message.
278          *  @param pos         The source position at which to report the message.
279          *  @param diagnosticInfo    The key for the localized message.
280          */
create( LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, DiagnosticInfo diagnosticInfo)281         public JCDiagnostic create(
282                 LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, DiagnosticInfo diagnosticInfo) {
283             return new JCDiagnostic(formatter, normalize(diagnosticInfo), lc, flags, source, pos);
284         }
285         //where
normalize(DiagnosticInfo diagnosticInfo)286             DiagnosticInfo normalize(DiagnosticInfo diagnosticInfo) {
287                 //replace all nested FragmentKey with full-blown JCDiagnostic objects
288                 return DiagnosticInfo.of(diagnosticInfo.type, diagnosticInfo.prefix, diagnosticInfo.code,
289                         Stream.of(diagnosticInfo.args).map(o -> {
290                             return (o instanceof Fragment) ?
291                                     fragment((Fragment)o) : o;
292                         }).toArray());
293             }
294 
295         /**
296          * Create a new error key.
297          */
errorKey(String code, Object... args)298         public Error errorKey(String code, Object... args) {
299             return (Error)DiagnosticInfo.of(ERROR, prefix, code, args);
300         }
301 
302         /**
303          * Create a new warning key.
304          */
warningKey(String code, Object... args)305         Warning warningKey(String code, Object... args) {
306             return (Warning)DiagnosticInfo.of(WARNING, prefix, code, args);
307         }
308 
309         /**
310          * Create a new note key.
311          */
noteKey(String code, Object... args)312         public Note noteKey(String code, Object... args) {
313             return (Note)DiagnosticInfo.of(NOTE, prefix, code, args);
314         }
315 
316         /**
317          * Create a new fragment key.
318          */
fragmentKey(String code, Object... args)319         Fragment fragmentKey(String code, Object... args) {
320             return (Fragment)DiagnosticInfo.of(FRAGMENT, prefix, code, args);
321         }
322     }
323 
324 
325 
326     /**
327      * Create a fragment diagnostic, for use as an argument in other diagnostics
328      *  @param key    The key for the localized error message.
329      *  @param args   Fields of the error message.
330      *
331      */
332     @Deprecated
fragment(String key, Object... args)333     public static JCDiagnostic fragment(String key, Object... args) {
334         return new JCDiagnostic(getFragmentFormatter(),
335                               DiagnosticInfo.of(FRAGMENT,
336                                       "compiler",
337                                       key,
338                                       args),
339                               null,
340                               EnumSet.noneOf(DiagnosticFlag.class),
341                               null,
342                               null);
343     }
344     //where
345     @Deprecated
getFragmentFormatter()346     public static DiagnosticFormatter<JCDiagnostic> getFragmentFormatter() {
347         if (fragmentFormatter == null) {
348             fragmentFormatter = new BasicDiagnosticFormatter(JavacMessages.getDefaultMessages());
349         }
350         return fragmentFormatter;
351     }
352 
353     /**
354      * A DiagnosticType defines the type of the diagnostic.
355      **/
356     public enum DiagnosticType {
357         /** A fragment of an enclosing diagnostic. */
358         FRAGMENT("misc"),
359         /** A note: similar to, but less serious than, a warning. */
360         NOTE("note"),
361         /** A warning. */
362         WARNING("warn"),
363         /** An error. */
364         ERROR("err");
365 
366         final String key;
367 
368         /** Create a DiagnosticType.
369          * @param key A string used to create the resource key for the diagnostic.
370          */
DiagnosticType(String key)371         DiagnosticType(String key) {
372             this.key = key;
373         }
374     }
375 
376     /**
377      * A DiagnosticPosition provides information about the positions in a file
378      * that gave rise to a diagnostic. It always defines a "preferred" position
379      * that most accurately defines the location of the diagnostic, it may also
380      * provide a related tree node that spans that location.
381      */
382     public static interface DiagnosticPosition {
383         /** Gets the tree node, if any, to which the diagnostic applies. */
getTree()384         JCTree getTree();
385         /** If there is a tree node, get the start position of the tree node.
386          *  Otherwise, just returns the same as getPreferredPosition(). */
getStartPosition()387         int getStartPosition();
388         /** Get the position within the file that most accurately defines the
389          *  location for the diagnostic. */
getPreferredPosition()390         int getPreferredPosition();
391         /** If there is a tree node, and if endPositions are available, get
392          *  the end position of the tree node. Otherwise, just returns the
393          *  same as getPreferredPosition(). */
getEndPosition(EndPosTable endPosTable)394         int getEndPosition(EndPosTable endPosTable);
395     }
396 
397     /**
398      * A DiagnosticPosition that simply identifies a position, but no related
399      * tree node, as the location for a diagnostic. Used for scanner and parser
400      * diagnostics. */
401     public static class SimpleDiagnosticPosition implements DiagnosticPosition {
SimpleDiagnosticPosition(int pos)402         public SimpleDiagnosticPosition(int pos) {
403             this.pos = pos;
404         }
405 
getTree()406         public JCTree getTree() {
407             return null;
408         }
409 
getStartPosition()410         public int getStartPosition() {
411             return pos;
412         }
413 
getPreferredPosition()414         public int getPreferredPosition() {
415             return pos;
416         }
417 
getEndPosition(EndPosTable endPosTable)418         public int getEndPosition(EndPosTable endPosTable) {
419             return pos;
420         }
421 
422         private final int pos;
423     }
424 
425     public enum DiagnosticFlag {
426         MANDATORY,
427         RESOLVE_ERROR,
428         SYNTAX,
429         RECOVERABLE,
430         NON_DEFERRABLE,
431         COMPRESSED,
432         /** Flag for diagnostics that were reported through API methods.
433          */
434         API,
435         /** Flag for not-supported-in-source-X errors.
436          */
437         SOURCE_LEVEL;
438     }
439 
440     private final DiagnosticSource source;
441     private final DiagnosticPosition position;
442     private final DiagnosticInfo diagnosticInfo;
443     private final Set<DiagnosticFlag> flags;
444     private final LintCategory lintCategory;
445 
446     /** source line position (set lazily) */
447     private SourcePosition sourcePosition;
448 
449     /**
450      * This class is used to defer the line/column position fetch logic after diagnostic construction.
451      */
452     class SourcePosition {
453 
454         private final int line;
455         private final int column;
456 
SourcePosition()457         SourcePosition() {
458             int n = (position == null ? Position.NOPOS : position.getPreferredPosition());
459             if (n == Position.NOPOS || source == null)
460                 line = column = -1;
461             else {
462                 line = source.getLineNumber(n);
463                 column = source.getColumnNumber(n, true);
464             }
465         }
466 
getLineNumber()467         public int getLineNumber() {
468             return line;
469         }
470 
getColumnNumber()471         public int getColumnNumber() {
472             return column;
473         }
474     }
475 
476     /**
477      * A diagnostic key object encapsulates basic properties of a diagnostic, such as the resource key,
478      * the arguments and the kind associated with the diagnostic object. Diagnostic keys can be either
479      * created programmatically (by using the supplied factory method) or obtained through build-time
480      * generated factory methods.
481      */
482     public static abstract class DiagnosticInfo {
483 
484         /** The diagnostic kind (i.e. error). */
485         DiagnosticType type;
486 
487         /** The diagnostic prefix (i.e. 'javac'); used to compute full resource key. */
488         String prefix;
489 
490         /** The diagnostic code (i.e. 'cannot.resolve.sym'); together with {@code prefix} it forms
491          * the full resource key. */
492         String code;
493 
494         /** The diagnostic arguments. */
495         Object[] args;
496 
DiagnosticInfo(DiagnosticType type, String prefix, String code, Object... args)497         private DiagnosticInfo(DiagnosticType type, String prefix, String code, Object... args) {
498             this.type = type;
499             this.prefix = prefix;
500             this.code = code;
501             this.args = args;
502         }
503 
504         /**
505          * Compute the resource key.
506          */
key()507         public String key() {
508             return prefix + "." + type.key + "." + code;
509         }
510 
511         /**
512          * Static factory method; build a custom diagnostic key using given kind, prefix, code and args.
513          */
of(DiagnosticType type, String prefix, String code, Object... args)514         public static DiagnosticInfo of(DiagnosticType type, String prefix, String code, Object... args) {
515             switch (type) {
516                 case ERROR:
517                     return new Error(prefix, code, args);
518                 case WARNING:
519                     return new Warning(prefix, code, args);
520                 case NOTE:
521                     return new Note(prefix, code, args);
522                 case FRAGMENT:
523                     return new Fragment(prefix, code, args);
524                 default:
525                     Assert.error("Wrong diagnostic type: " + type);
526                     return null;
527             }
528         }
529 
530         /**
531          * Returns the code for this diagnostic info, provided mainly for backward compatibility
532          */
getCode()533         public String getCode() {
534             return code;
535         }
536 
537         /**
538          * Returns the arguments for this diagnostic info, provided mainly for backward compatibility
539          */
getArgs()540         public Object[] getArgs() {
541             return args;
542         }
543 
setArgs(Object[] args)544         public void setArgs(Object[] args) {
545             this.args = args;
546         }
547     }
548 
549     /**
550      * Class representing error diagnostic keys.
551      */
552     public static final class Error extends DiagnosticInfo {
Error(String prefix, String key, Object... args)553         public Error(String prefix, String key, Object... args) {
554             super(DiagnosticType.ERROR, prefix, key, args);
555         }
556     }
557 
558     /**
559      * Class representing warning diagnostic keys.
560      */
561     public static final class Warning extends DiagnosticInfo {
Warning(String prefix, String key, Object... args)562         public Warning(String prefix, String key, Object... args) {
563             super(DiagnosticType.WARNING, prefix, key, args);
564         }
565     }
566 
567     /**
568      * Class representing note diagnostic keys.
569      */
570     public static final class Note extends DiagnosticInfo {
Note(String prefix, String key, Object... args)571         public Note(String prefix, String key, Object... args) {
572             super(DiagnosticType.NOTE, prefix, key, args);
573         }
574     }
575 
576     /**
577      * Class representing fragment diagnostic keys.
578      */
579     public static final class Fragment extends DiagnosticInfo {
Fragment(String prefix, String key, Object... args)580         public Fragment(String prefix, String key, Object... args) {
581             super(DiagnosticType.FRAGMENT, prefix, key, args);
582         }
583     }
584 
585     /**
586      * Create a diagnostic object.
587      * @param formatter the formatter to use for the diagnostic
588      * @param diagnosticInfo the diagnostic key
589      * @param lc     the lint category for the diagnostic
590      * @param source the name of the source file, or null if none.
591      * @param pos the character offset within the source file, if given.
592      */
JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter, DiagnosticInfo diagnosticInfo, LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos)593     protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
594                        DiagnosticInfo diagnosticInfo,
595                        LintCategory lc,
596                        Set<DiagnosticFlag> flags,
597                        DiagnosticSource source,
598                        DiagnosticPosition pos) {
599         if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
600             throw new IllegalArgumentException();
601 
602         this.defaultFormatter = formatter;
603         this.diagnosticInfo = diagnosticInfo;
604         this.lintCategory = lc;
605         this.flags = flags;
606         this.source = source;
607         this.position = pos;
608     }
609 
610     /**
611      * Get the type of this diagnostic.
612      * @return the type of this diagnostic
613      */
getType()614     public DiagnosticType getType() {
615         return diagnosticInfo.type;
616     }
617 
618     /**
619      * Get the subdiagnostic list
620      * @return subdiagnostic list
621      */
getSubdiagnostics()622     public List<JCDiagnostic> getSubdiagnostics() {
623         return List.nil();
624     }
625 
isMultiline()626     public boolean isMultiline() {
627         return false;
628     }
629 
630     /**
631      * Check whether or not this diagnostic is required to be shown.
632      * @return true if this diagnostic is required to be shown.
633      */
isMandatory()634     public boolean isMandatory() {
635         return flags.contains(DiagnosticFlag.MANDATORY);
636     }
637 
638     /**
639      * Check whether this diagnostic has an associated lint category.
640      */
hasLintCategory()641     public boolean hasLintCategory() {
642         return (lintCategory != null);
643     }
644 
645     /**
646      * Get the associated lint category, or null if none.
647      */
getLintCategory()648     public LintCategory getLintCategory() {
649         return lintCategory;
650     }
651 
652     /**
653      * Get the name of the source file referred to by this diagnostic.
654      * @return the name of the source referred to with this diagnostic, or null if none
655      */
656     @DefinedBy(Api.COMPILER)
getSource()657     public JavaFileObject getSource() {
658         if (source == null)
659             return null;
660         else
661             return source.getFile();
662     }
663 
664     /**
665      * Get the source referred to by this diagnostic.
666      * @return the source referred to with this diagnostic, or null if none
667      */
getDiagnosticSource()668     public DiagnosticSource getDiagnosticSource() {
669         return source;
670     }
671 
getIntStartPosition()672     protected int getIntStartPosition() {
673         return (position == null ? Position.NOPOS : position.getStartPosition());
674     }
675 
getIntPosition()676     protected int getIntPosition() {
677         return (position == null ? Position.NOPOS : position.getPreferredPosition());
678     }
679 
getIntEndPosition()680     protected int getIntEndPosition() {
681         return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
682     }
683 
684     @DefinedBy(Api.COMPILER)
getStartPosition()685     public long getStartPosition() {
686         return getIntStartPosition();
687     }
688 
689     @DefinedBy(Api.COMPILER)
getPosition()690     public long getPosition() {
691         return getIntPosition();
692     }
693 
694     @DefinedBy(Api.COMPILER)
getEndPosition()695     public long getEndPosition() {
696         return getIntEndPosition();
697     }
698 
getDiagnosticPosition()699     public DiagnosticPosition getDiagnosticPosition() {
700         return position;
701     }
702 
703     /**
704      * Get the line number within the source referred to by this diagnostic.
705      * @return  the line number within the source referred to by this diagnostic
706      */
707     @DefinedBy(Api.COMPILER)
getLineNumber()708     public long getLineNumber() {
709         if (sourcePosition == null) {
710             sourcePosition = new SourcePosition();
711         }
712         return sourcePosition.getLineNumber();
713     }
714 
715     /**
716      * Get the column number within the line of source referred to by this diagnostic.
717      * @return  the column number within the line of source referred to by this diagnostic
718      */
719     @DefinedBy(Api.COMPILER)
getColumnNumber()720     public long getColumnNumber() {
721         if (sourcePosition == null) {
722             sourcePosition = new SourcePosition();
723         }
724         return sourcePosition.getColumnNumber();
725     }
726 
727     /**
728      * Get the arguments to be included in the text of the diagnostic.
729      * @return  the arguments to be included in the text of the diagnostic
730      */
getArgs()731     public Object[] getArgs() {
732         return diagnosticInfo.args;
733     }
734 
735     /**
736      * Get the prefix string associated with this type of diagnostic.
737      * @return the prefix string associated with this type of diagnostic
738      */
getPrefix()739     public String getPrefix() {
740         return getPrefix(diagnosticInfo.type);
741     }
742 
743     /**
744      * Get the prefix string associated with a particular type of diagnostic.
745      * @return the prefix string associated with a particular type of diagnostic
746      */
getPrefix(DiagnosticType dt)747     public String getPrefix(DiagnosticType dt) {
748         return defaultFormatter.formatKind(this, Locale.getDefault());
749     }
750 
751     /**
752      * Return the standard presentation of this diagnostic.
753      */
754     @Override
toString()755     public String toString() {
756         return defaultFormatter.format(this, Locale.getDefault());
757     }
758 
759     private DiagnosticFormatter<JCDiagnostic> defaultFormatter;
760     @Deprecated
761     private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter;
762 
763     // Methods for javax.tools.Diagnostic
764 
765     @DefinedBy(Api.COMPILER)
getKind()766     public Diagnostic.Kind getKind() {
767         switch (diagnosticInfo.type) {
768         case NOTE:
769             return Diagnostic.Kind.NOTE;
770         case WARNING:
771             return flags.contains(DiagnosticFlag.MANDATORY)
772                     ? Diagnostic.Kind.MANDATORY_WARNING
773                     : Diagnostic.Kind.WARNING;
774         case ERROR:
775             return Diagnostic.Kind.ERROR;
776         default:
777             return Diagnostic.Kind.OTHER;
778         }
779     }
780 
781     @DefinedBy(Api.COMPILER)
getCode()782     public String getCode() {
783         return diagnosticInfo.key();
784     }
785 
786     @DefinedBy(Api.COMPILER)
getMessage(Locale locale)787     public String getMessage(Locale locale) {
788         return defaultFormatter.formatMessage(this, locale);
789     }
790 
setFlag(DiagnosticFlag flag)791     public void setFlag(DiagnosticFlag flag) {
792         flags.add(flag);
793 
794         if (diagnosticInfo.type == DiagnosticType.ERROR) {
795             switch (flag) {
796                 case SYNTAX:
797                     flags.remove(DiagnosticFlag.RECOVERABLE);
798                     break;
799                 case RESOLVE_ERROR:
800                     flags.add(DiagnosticFlag.RECOVERABLE);
801                     break;
802             }
803         }
804     }
805 
isFlagSet(DiagnosticFlag flag)806     public boolean isFlagSet(DiagnosticFlag flag) {
807         return flags.contains(flag);
808     }
809 
810     public static class MultilineDiagnostic extends JCDiagnostic {
811 
812         private final List<JCDiagnostic> subdiagnostics;
813 
MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics)814         public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) {
815             super(other.defaultFormatter,
816                   other.diagnosticInfo,
817                   other.getLintCategory(),
818                   other.flags,
819                   other.getDiagnosticSource(),
820                   other.position);
821             this.subdiagnostics = subdiagnostics;
822         }
823 
824         @Override
getSubdiagnostics()825         public List<JCDiagnostic> getSubdiagnostics() {
826             return subdiagnostics;
827         }
828 
829         @Override
isMultiline()830         public boolean isMultiline() {
831             return true;
832         }
833     }
834 }
835