1/*
2 * Copyright (c) 2003, 2015, 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
26package com.sun.tools.javac.util;
27
28import java.util.EnumSet;
29import java.util.Locale;
30import java.util.Set;
31import java.util.stream.Stream;
32
33import javax.tools.Diagnostic;
34import javax.tools.JavaFileObject;
35
36import com.sun.tools.javac.api.DiagnosticFormatter;
37import com.sun.tools.javac.code.Lint.LintCategory;
38import com.sun.tools.javac.tree.EndPosTable;
39import com.sun.tools.javac.tree.JCTree;
40import com.sun.tools.javac.util.DefinedBy.Api;
41
42import 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 */
51public 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. */
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. */
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
79        private void initOptions(Options options) {
80            if (options.isSet("onlySyntaxErrorsUnrecoverable"))
81                defaultErrorFlags.add(DiagnosticFlag.RECOVERABLE);
82        }
83
84        /** Create a new diagnostic factory. */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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
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         */
298        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         */
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         */
312        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         */
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
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
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         */
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. */
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(). */
387        int getStartPosition();
388        /** Get the position within the file that most accurately defines the
389         *  location for the diagnostic. */
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(). */
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 {
402        public SimpleDiagnosticPosition(int pos) {
403            this.pos = pos;
404        }
405
406        public JCTree getTree() {
407            return null;
408        }
409
410        public int getStartPosition() {
411            return pos;
412        }
413
414        public int getPreferredPosition() {
415            return pos;
416        }
417
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        /** Print multiple errors for same source locations.
433         */
434        MULTIPLE,
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
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
467        public int getLineNumber() {
468            return line;
469        }
470
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
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         */
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         */
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
532    /**
533     * Class representing error diagnostic keys.
534     */
535    public static final class Error extends DiagnosticInfo {
536        public Error(String prefix, String key, Object... args) {
537            super(DiagnosticType.ERROR, prefix, key, args);
538        }
539    }
540
541    /**
542     * Class representing warning diagnostic keys.
543     */
544    public static final class Warning extends DiagnosticInfo {
545        public Warning(String prefix, String key, Object... args) {
546            super(DiagnosticType.WARNING, prefix, key, args);
547        }
548    }
549
550    /**
551     * Class representing note diagnostic keys.
552     */
553    public static final class Note extends DiagnosticInfo {
554        public Note(String prefix, String key, Object... args) {
555            super(DiagnosticType.NOTE, prefix, key, args);
556        }
557    }
558
559    /**
560     * Class representing fragment diagnostic keys.
561     */
562    public static final class Fragment extends DiagnosticInfo {
563        public Fragment(String prefix, String key, Object... args) {
564            super(DiagnosticType.FRAGMENT, prefix, key, args);
565        }
566    }
567
568    /**
569     * Create a diagnostic object.
570     * @param formatter the formatter to use for the diagnostic
571     * @param diagnosticInfo the diagnostic key
572     * @param lc     the lint category for the diagnostic
573     * @param source the name of the source file, or null if none.
574     * @param pos the character offset within the source file, if given.
575     */
576    protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
577                       DiagnosticInfo diagnosticInfo,
578                       LintCategory lc,
579                       Set<DiagnosticFlag> flags,
580                       DiagnosticSource source,
581                       DiagnosticPosition pos) {
582        if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
583            throw new IllegalArgumentException();
584
585        this.defaultFormatter = formatter;
586        this.diagnosticInfo = diagnosticInfo;
587        this.lintCategory = lc;
588        this.flags = flags;
589        this.source = source;
590        this.position = pos;
591    }
592
593    /**
594     * Get the type of this diagnostic.
595     * @return the type of this diagnostic
596     */
597    public DiagnosticType getType() {
598        return diagnosticInfo.type;
599    }
600
601    /**
602     * Get the subdiagnostic list
603     * @return subdiagnostic list
604     */
605    public List<JCDiagnostic> getSubdiagnostics() {
606        return List.nil();
607    }
608
609    public boolean isMultiline() {
610        return false;
611    }
612
613    /**
614     * Check whether or not this diagnostic is required to be shown.
615     * @return true if this diagnostic is required to be shown.
616     */
617    public boolean isMandatory() {
618        return flags.contains(DiagnosticFlag.MANDATORY);
619    }
620
621    /**
622     * Check whether this diagnostic has an associated lint category.
623     */
624    public boolean hasLintCategory() {
625        return (lintCategory != null);
626    }
627
628    /**
629     * Get the associated lint category, or null if none.
630     */
631    public LintCategory getLintCategory() {
632        return lintCategory;
633    }
634
635    /**
636     * Get the name of the source file referred to by this diagnostic.
637     * @return the name of the source referred to with this diagnostic, or null if none
638     */
639    @DefinedBy(Api.COMPILER)
640    public JavaFileObject getSource() {
641        if (source == null)
642            return null;
643        else
644            return source.getFile();
645    }
646
647    /**
648     * Get the source referred to by this diagnostic.
649     * @return the source referred to with this diagnostic, or null if none
650     */
651    public DiagnosticSource getDiagnosticSource() {
652        return source;
653    }
654
655    protected int getIntStartPosition() {
656        return (position == null ? Position.NOPOS : position.getStartPosition());
657    }
658
659    protected int getIntPosition() {
660        return (position == null ? Position.NOPOS : position.getPreferredPosition());
661    }
662
663    protected int getIntEndPosition() {
664        return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
665    }
666
667    @DefinedBy(Api.COMPILER)
668    public long getStartPosition() {
669        return getIntStartPosition();
670    }
671
672    @DefinedBy(Api.COMPILER)
673    public long getPosition() {
674        return getIntPosition();
675    }
676
677    @DefinedBy(Api.COMPILER)
678    public long getEndPosition() {
679        return getIntEndPosition();
680    }
681
682    public DiagnosticPosition getDiagnosticPosition() {
683        return position;
684    }
685
686    /**
687     * Get the line number within the source referred to by this diagnostic.
688     * @return  the line number within the source referred to by this diagnostic
689     */
690    @DefinedBy(Api.COMPILER)
691    public long getLineNumber() {
692        if (sourcePosition == null) {
693            sourcePosition = new SourcePosition();
694        }
695        return sourcePosition.getLineNumber();
696    }
697
698    /**
699     * Get the column number within the line of source referred to by this diagnostic.
700     * @return  the column number within the line of source referred to by this diagnostic
701     */
702    @DefinedBy(Api.COMPILER)
703    public long getColumnNumber() {
704        if (sourcePosition == null) {
705            sourcePosition = new SourcePosition();
706        }
707        return sourcePosition.getColumnNumber();
708    }
709
710    /**
711     * Get the arguments to be included in the text of the diagnostic.
712     * @return  the arguments to be included in the text of the diagnostic
713     */
714    public Object[] getArgs() {
715        return diagnosticInfo.args;
716    }
717
718    /**
719     * Get the prefix string associated with this type of diagnostic.
720     * @return the prefix string associated with this type of diagnostic
721     */
722    public String getPrefix() {
723        return getPrefix(diagnosticInfo.type);
724    }
725
726    /**
727     * Get the prefix string associated with a particular type of diagnostic.
728     * @return the prefix string associated with a particular type of diagnostic
729     */
730    public String getPrefix(DiagnosticType dt) {
731        return defaultFormatter.formatKind(this, Locale.getDefault());
732    }
733
734    /**
735     * Return the standard presentation of this diagnostic.
736     */
737    @Override
738    public String toString() {
739        return defaultFormatter.format(this, Locale.getDefault());
740    }
741
742    private DiagnosticFormatter<JCDiagnostic> defaultFormatter;
743    @Deprecated
744    private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter;
745
746    // Methods for javax.tools.Diagnostic
747
748    @DefinedBy(Api.COMPILER)
749    public Diagnostic.Kind getKind() {
750        switch (diagnosticInfo.type) {
751        case NOTE:
752            return Diagnostic.Kind.NOTE;
753        case WARNING:
754            return flags.contains(DiagnosticFlag.MANDATORY)
755                    ? Diagnostic.Kind.MANDATORY_WARNING
756                    : Diagnostic.Kind.WARNING;
757        case ERROR:
758            return Diagnostic.Kind.ERROR;
759        default:
760            return Diagnostic.Kind.OTHER;
761        }
762    }
763
764    @DefinedBy(Api.COMPILER)
765    public String getCode() {
766        return diagnosticInfo.key();
767    }
768
769    @DefinedBy(Api.COMPILER)
770    public String getMessage(Locale locale) {
771        return defaultFormatter.formatMessage(this, locale);
772    }
773
774    public void setFlag(DiagnosticFlag flag) {
775        flags.add(flag);
776
777        if (diagnosticInfo.type == DiagnosticType.ERROR) {
778            switch (flag) {
779                case SYNTAX:
780                    flags.remove(DiagnosticFlag.RECOVERABLE);
781                    break;
782                case RESOLVE_ERROR:
783                    flags.add(DiagnosticFlag.RECOVERABLE);
784                    break;
785            }
786        }
787    }
788
789    public boolean isFlagSet(DiagnosticFlag flag) {
790        return flags.contains(flag);
791    }
792
793    public static class MultilineDiagnostic extends JCDiagnostic {
794
795        private final List<JCDiagnostic> subdiagnostics;
796
797        public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) {
798            super(other.defaultFormatter,
799                  other.diagnosticInfo,
800                  other.getLintCategory(),
801                  other.flags,
802                  other.getDiagnosticSource(),
803                  other.position);
804            this.subdiagnostics = subdiagnostics;
805        }
806
807        @Override
808        public List<JCDiagnostic> getSubdiagnostics() {
809            return subdiagnostics;
810        }
811
812        @Override
813        public boolean isMultiline() {
814            return true;
815        }
816    }
817}
818