JCDiagnostic.java revision 2776:aa568700edd1
1246091Sdelphij/*
2246074Sgabor * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3246074Sgabor * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4246074Sgabor *
5246074Sgabor * This code is free software; you can redistribute it and/or modify it
6246074Sgabor * under the terms of the GNU General Public License version 2 only, as
7246074Sgabor * published by the Free Software Foundation.  Oracle designates this
8246074Sgabor * particular file as subject to the "Classpath" exception as provided
9246074Sgabor * by Oracle in the LICENSE file that accompanied this code.
10246074Sgabor *
11246074Sgabor * This code is distributed in the hope that it will be useful, but WITHOUT
12246074Sgabor * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13246074Sgabor * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14246074Sgabor * version 2 for more details (a copy is included in the LICENSE file that
15246074Sgabor * accompanied this code).
16246074Sgabor *
17246074Sgabor * You should have received a copy of the GNU General Public License version
18246074Sgabor * 2 along with this work; if not, write to the Free Software Foundation,
19246074Sgabor * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20246074Sgabor *
21246074Sgabor * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22246074Sgabor * or visit www.oracle.com if you need additional information or have any
23246074Sgabor * questions.
24246074Sgabor */
25246074Sgabor
26246074Sgaborpackage com.sun.tools.javac.util;
27246091Sdelphij
28246091Sdelphijimport java.util.EnumSet;
29246074Sgaborimport java.util.Locale;
30246074Sgaborimport java.util.Set;
31246074Sgaborimport java.util.stream.Stream;
32246074Sgabor
33246074Sgaborimport javax.tools.Diagnostic;
34246074Sgaborimport javax.tools.JavaFileObject;
35246074Sgabor
36246074Sgaborimport com.sun.tools.javac.api.DiagnosticFormatter;
37246074Sgaborimport com.sun.tools.javac.code.Lint.LintCategory;
38246074Sgaborimport com.sun.tools.javac.tree.EndPosTable;
39246074Sgaborimport com.sun.tools.javac.tree.JCTree;
40246074Sgaborimport com.sun.tools.javac.util.DefinedBy.Api;
41246074Sgabor
42246074Sgaborimport static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
43246074Sgabor
44246074Sgabor/** An abstraction of a diagnostic message generated by the compiler.
45246074Sgabor *
46246074Sgabor *  <p><b>This is NOT part of any supported API.
47246074Sgabor *  If you write code that depends on this, you do so at your own risk.
48246074Sgabor *  This code and its internal interfaces are subject to change or
49246074Sgabor *  deletion without notice.</b>
50246074Sgabor */
51246074Sgaborpublic class JCDiagnostic implements Diagnostic<JavaFileObject> {
52246074Sgabor    /** A factory for creating diagnostic objects. */
53246074Sgabor    public static class Factory {
54246074Sgabor        /** The context key for the diagnostic factory. */
55246074Sgabor        protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey = new Context.Key<>();
56246074Sgabor
57246074Sgabor        /** Get the Factory instance for this context. */
58246074Sgabor        public static Factory instance(Context context) {
59246074Sgabor            Factory instance = context.get(diagnosticFactoryKey);
60246074Sgabor            if (instance == null)
61246074Sgabor                instance = new Factory(context);
62246074Sgabor            return instance;
63246074Sgabor        }
64246074Sgabor
65246074Sgabor        DiagnosticFormatter<JCDiagnostic> formatter;
66246074Sgabor        final String prefix;
67246074Sgabor        final Set<DiagnosticFlag> defaultErrorFlags;
68246074Sgabor
69246074Sgabor        /** Create a new diagnostic factory. */
70246074Sgabor        protected Factory(Context context) {
71246074Sgabor            this(JavacMessages.instance(context), "compiler");
72246074Sgabor            context.put(diagnosticFactoryKey, this);
73246074Sgabor
74246074Sgabor            final Options options = Options.instance(context);
75246074Sgabor            initOptions(options);
76246074Sgabor            options.addListener(new Runnable() {
77246074Sgabor               public void run() {
78246074Sgabor                   initOptions(options);
79246074Sgabor               }
80246074Sgabor            });
81246074Sgabor        }
82246074Sgabor
83246074Sgabor        private void initOptions(Options options) {
84246074Sgabor            if (options.isSet("onlySyntaxErrorsUnrecoverable"))
85246074Sgabor                defaultErrorFlags.add(DiagnosticFlag.RECOVERABLE);
86246074Sgabor        }
87246074Sgabor
88246074Sgabor        /** Create a new diagnostic factory. */
89246074Sgabor        public Factory(JavacMessages messages, String prefix) {
90246074Sgabor            this.prefix = prefix;
91246074Sgabor            this.formatter = new BasicDiagnosticFormatter(messages);
92246074Sgabor            defaultErrorFlags = EnumSet.of(DiagnosticFlag.MANDATORY);
93246074Sgabor        }
94246074Sgabor
95246074Sgabor        /**
96246074Sgabor         * Create an error diagnostic
97246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the error.
98246074Sgabor         *  @param pos    The source position at which to report the error.
99246074Sgabor         *  @param key    The key for the localized error message.
100246074Sgabor         *  @param args   Fields of the error message.
101246074Sgabor         */
102246074Sgabor        public JCDiagnostic error(
103246074Sgabor                DiagnosticFlag flag, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
104252636Sobrien            return error(flag, source, pos, errorKey(key, args));
105246074Sgabor        }
106246074Sgabor
107246074Sgabor        /**
108246074Sgabor         * Create an error diagnostic
109246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the error.
110252636Sobrien         *  @param pos    The source position at which to report the error.
111252636Sobrien         *  @param errorKey    The key for the localized error message.
112252636Sobrien         */
113252636Sobrien        public JCDiagnostic error(
114252636Sobrien                DiagnosticFlag flag, DiagnosticSource source, DiagnosticPosition pos, Error errorKey) {
115246074Sgabor            JCDiagnostic diag = create(null, defaultErrorFlags, source, pos, errorKey);
116246074Sgabor            if (flag != null) {
117246074Sgabor                diag.setFlag(flag);
118246074Sgabor            }
119246074Sgabor            return diag;
120246074Sgabor        }
121246074Sgabor
122246074Sgabor        /**
123246074Sgabor         * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
124246074Sgabor         *  @param lc     The lint category for the diagnostic
125246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the warning.
126246074Sgabor         *  @param pos    The source position at which to report the warning.
127246074Sgabor         *  @param key    The key for the localized warning message.
128246074Sgabor         *  @param args   Fields of the warning message.
129246074Sgabor         *  @see MandatoryWarningHandler
130246074Sgabor         */
131246074Sgabor        public JCDiagnostic mandatoryWarning(
132246074Sgabor                LintCategory lc,
133246074Sgabor                DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
134246074Sgabor            return mandatoryWarning(lc, source, pos, warningKey(key, args));
135246074Sgabor        }
136246074Sgabor
137246074Sgabor        /**
138246074Sgabor         * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
139246074Sgabor         *  @param lc     The lint category for the diagnostic
140246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the warning.
141246074Sgabor         *  @param pos    The source position at which to report the warning.
142246074Sgabor         *  @param warningKey    The key for the localized warning message.
143246074Sgabor         *  @see MandatoryWarningHandler
144246074Sgabor         */
145246074Sgabor        public JCDiagnostic mandatoryWarning(
146246074Sgabor                LintCategory lc,
147246074Sgabor                DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) {
148246074Sgabor            return create(lc, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, warningKey);
149246074Sgabor        }
150246074Sgabor
151246074Sgabor        /**
152246074Sgabor         * Create a warning diagnostic.
153246074Sgabor         *  @param lc     The lint category for the diagnostic
154246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the warning.
155246074Sgabor         *  @param pos    The source position at which to report the warning.
156246074Sgabor         *  @param key    The key for the localized error message.
157246074Sgabor         *  @param args   Fields of the warning message.
158246074Sgabor         *  @see MandatoryWarningHandler
159246074Sgabor         */
160246074Sgabor        public JCDiagnostic warning(
161246074Sgabor                LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
162246074Sgabor            return warning(lc, source, pos, warningKey(key, args));
163246074Sgabor        }
164246074Sgabor
165246074Sgabor        /**
166246074Sgabor         * Create a warning diagnostic.
167246074Sgabor         *  @param lc     The lint category for the diagnostic
168246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the warning.
169246074Sgabor         *  @param pos    The source position at which to report the warning.
170246074Sgabor         *  @param warningKey    The key for the localized warning message.
171246074Sgabor         *  @see MandatoryWarningHandler
172246074Sgabor         */
173246074Sgabor        public JCDiagnostic warning(
174246074Sgabor                LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) {
175246074Sgabor            return create(lc, EnumSet.noneOf(DiagnosticFlag.class), source, pos, warningKey);
176246074Sgabor        }
177246074Sgabor
178246074Sgabor        /**
179246074Sgabor         * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
180246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the warning.
181246074Sgabor         *  @param key    The key for the localized warning message.
182246074Sgabor         *  @param args   Fields of the warning message.
183246074Sgabor         *  @see MandatoryWarningHandler
184246074Sgabor         */
185246074Sgabor        public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
186246074Sgabor            return mandatoryNote(source, noteKey(key, args));
187246074Sgabor        }
188246074Sgabor
189246074Sgabor        /**
190246074Sgabor         * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
191246074Sgabor         *  @param noteKey    The key for the localized note message.
192246074Sgabor         *  @see MandatoryWarningHandler
193246074Sgabor         */
194246074Sgabor        public JCDiagnostic mandatoryNote(DiagnosticSource source, Note noteKey) {
195246074Sgabor            return create(null, EnumSet.of(DiagnosticFlag.MANDATORY), source, null, noteKey);
196246074Sgabor        }
197246074Sgabor
198246074Sgabor        /**
199246074Sgabor         * Create a note diagnostic.
200246074Sgabor         *  @param key    The key for the localized error message.
201246074Sgabor         *  @param args   Fields of the message.
202246074Sgabor         */
203246074Sgabor        public JCDiagnostic note(
204246074Sgabor                DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
205246074Sgabor            return note(source, pos, noteKey(key, args));
206246074Sgabor        }
207246074Sgabor
208246074Sgabor        /**
209246074Sgabor         * Create a note diagnostic.
210246074Sgabor         *  @param source The source of the compilation unit, if any, in which to report the note.
211246074Sgabor         *  @param pos    The source position at which to report the note.
212246074Sgabor         *  @param noteKey    The key for the localized note message.
213246074Sgabor         */
214246074Sgabor        public JCDiagnostic note(
215246074Sgabor                DiagnosticSource source, DiagnosticPosition pos, Note noteKey) {
216246074Sgabor            return create(null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, noteKey);
217246074Sgabor        }
218246074Sgabor
219246074Sgabor        /**
220246074Sgabor         * Create a fragment diagnostic, for use as an argument in other diagnostics
221246074Sgabor         *  @param key    The key for the localized message.
222246074Sgabor         *  @param args   Fields of the message.
223246074Sgabor         */
224246074Sgabor        public JCDiagnostic fragment(String key, Object... args) {
225246074Sgabor            return fragment(fragmentKey(key, args));
226246074Sgabor        }
227246074Sgabor
228246074Sgabor        /**
229246074Sgabor         * Create a fragment diagnostic, for use as an argument in other diagnostics
230246074Sgabor         *  @param fragmentKey    The key for the localized subdiagnostic message.
231246074Sgabor         */
232246074Sgabor        public JCDiagnostic fragment(Fragment fragmentKey) {
233246074Sgabor            return create(null, EnumSet.noneOf(DiagnosticFlag.class), null, null, fragmentKey);
234246074Sgabor        }
235246074Sgabor
236246074Sgabor        /**
237246074Sgabor         * Create a new diagnostic of the given kind, which is not mandatory and which has
238246074Sgabor         * no lint category.
239246074Sgabor         *  @param kind        The diagnostic kind
240246074Sgabor         *  @param source      The source of the compilation unit, if any, in which to report the message.
241246074Sgabor         *  @param pos         The source position at which to report the message.
242246074Sgabor         *  @param key         The key for the localized message.
243246074Sgabor         *  @param args        Fields of the message.
244246074Sgabor         */
245246074Sgabor        public JCDiagnostic create(
246246074Sgabor                DiagnosticType kind, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
247246074Sgabor            return create(null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, DiagnosticInfo.of(kind, prefix, key, args));
248246074Sgabor        }
249246074Sgabor
250246074Sgabor        /**
251246074Sgabor         * Create a new diagnostic of the given kind, which is not mandatory and which has
252246074Sgabor         * no lint category.
253246074Sgabor         *  @param source      The source of the compilation unit, if any, in which to report the message.
254246074Sgabor         *  @param pos         The source position at which to report the message.
255246074Sgabor         *  @param diagnosticInfo         The key for the localized message.
256246074Sgabor         */
257246074Sgabor        public JCDiagnostic create(
258246074Sgabor                DiagnosticSource source, DiagnosticPosition pos, DiagnosticInfo diagnosticInfo) {
259246074Sgabor            return create(null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, diagnosticInfo);
260246074Sgabor        }
261246074Sgabor
262246074Sgabor        /**
263246074Sgabor         * Create a new diagnostic of the given kind.
264246074Sgabor         *  @param kind        The diagnostic kind
265246074Sgabor         *  @param lc          The lint category, if applicable, or null
266246074Sgabor         *  @param flags       The set of flags for the diagnostic
267246074Sgabor         *  @param source      The source of the compilation unit, if any, in which to report the message.
268246074Sgabor         *  @param pos         The source position at which to report the message.
269246074Sgabor         *  @param key         The key for the localized message.
270246074Sgabor         *  @param args        Fields of the message.
271246074Sgabor         */
272246074Sgabor        public JCDiagnostic create(DiagnosticType kind,
273246074Sgabor                LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
274246074Sgabor            return create(lc, flags, source, pos, DiagnosticInfo.of(kind, prefix, key, args));
275246074Sgabor        }
276246074Sgabor
277246074Sgabor        /**
278246074Sgabor         * Create a new diagnostic with given key.
279246074Sgabor         *  @param lc          The lint category, if applicable, or null
280246074Sgabor         *  @param flags       The set of flags for the diagnostic
281246074Sgabor         *  @param source      The source of the compilation unit, if any, in which to report the message.
282246074Sgabor         *  @param pos         The source position at which to report the message.
283246074Sgabor         *  @param diagnosticInfo    The key for the localized message.
284246074Sgabor         */
285246074Sgabor        public JCDiagnostic create(
286246074Sgabor                LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, DiagnosticInfo diagnosticInfo) {
287246074Sgabor            return new JCDiagnostic(formatter, normalize(diagnosticInfo), lc, flags, source, pos);
288246074Sgabor        }
289246074Sgabor        //where
290246074Sgabor            DiagnosticInfo normalize(DiagnosticInfo diagnosticInfo) {
291246074Sgabor                //replace all nested FragmentKey with full-blown JCDiagnostic objects
292246074Sgabor                return DiagnosticInfo.of(diagnosticInfo.type, diagnosticInfo.prefix, diagnosticInfo.code,
293246074Sgabor                        Stream.of(diagnosticInfo.args).map(o -> {
294246074Sgabor                            return (o instanceof Fragment) ?
295246074Sgabor                                    fragment((Fragment)o) : o;
296246074Sgabor                        }).toArray());
297246074Sgabor            }
298246074Sgabor
299246074Sgabor        /**
300246074Sgabor         * Create a new error key.
301246074Sgabor         */
302246074Sgabor        Error errorKey(String code, Object... args) {
303246074Sgabor            return (Error)DiagnosticInfo.of(ERROR, prefix, code, args);
304246074Sgabor        }
305246074Sgabor
306246074Sgabor        /**
307246074Sgabor         * Create a new warning key.
308246074Sgabor         */
309246074Sgabor        Warning warningKey(String code, Object... args) {
310246074Sgabor            return (Warning)DiagnosticInfo.of(WARNING, prefix, code, args);
311246074Sgabor        }
312246074Sgabor
313246074Sgabor        /**
314246074Sgabor         * Create a new note key.
315246074Sgabor         */
316246074Sgabor        Note noteKey(String code, Object... args) {
317246074Sgabor            return (Note)DiagnosticInfo.of(NOTE, prefix, code, args);
318246074Sgabor        }
319246074Sgabor
320246074Sgabor        /**
321246074Sgabor         * Create a new fragment key.
322246074Sgabor         */
323246074Sgabor        Fragment fragmentKey(String code, Object... args) {
324246074Sgabor            return (Fragment)DiagnosticInfo.of(FRAGMENT, prefix, code, args);
325246074Sgabor        }
326246074Sgabor    }
327246074Sgabor
328246074Sgabor
329246074Sgabor
330246074Sgabor    /**
331246074Sgabor     * Create a fragment diagnostic, for use as an argument in other diagnostics
332246074Sgabor     *  @param key    The key for the localized error message.
333246074Sgabor     *  @param args   Fields of the error message.
334246074Sgabor     *
335246074Sgabor     */
336246074Sgabor    @Deprecated
337246074Sgabor    public static JCDiagnostic fragment(String key, Object... args) {
338246074Sgabor        return new JCDiagnostic(getFragmentFormatter(),
339246074Sgabor                              DiagnosticInfo.of(FRAGMENT,
340246074Sgabor                                      "compiler",
341246074Sgabor                                      key,
342246074Sgabor                                      args),
343246074Sgabor                              null,
344246074Sgabor                              EnumSet.noneOf(DiagnosticFlag.class),
345246074Sgabor                              null,
346246074Sgabor                              null);
347246074Sgabor    }
348246074Sgabor    //where
349246074Sgabor    @Deprecated
350246074Sgabor    public static DiagnosticFormatter<JCDiagnostic> getFragmentFormatter() {
351246074Sgabor        if (fragmentFormatter == null) {
352246074Sgabor            fragmentFormatter = new BasicDiagnosticFormatter(JavacMessages.getDefaultMessages());
353246074Sgabor        }
354246074Sgabor        return fragmentFormatter;
355246074Sgabor    }
356246074Sgabor
357246074Sgabor    /**
358246074Sgabor     * A DiagnosticType defines the type of the diagnostic.
359246074Sgabor     **/
360246074Sgabor    public enum DiagnosticType {
361246074Sgabor        /** A fragment of an enclosing diagnostic. */
362246074Sgabor        FRAGMENT("misc"),
363246074Sgabor        /** A note: similar to, but less serious than, a warning. */
364246074Sgabor        NOTE("note"),
365246074Sgabor        /** A warning. */
366246074Sgabor        WARNING("warn"),
367246074Sgabor        /** An error. */
368246074Sgabor        ERROR("err");
369246074Sgabor
370246074Sgabor        final String key;
371246074Sgabor
372246074Sgabor        /** Create a DiagnosticType.
373246074Sgabor         * @param key A string used to create the resource key for the diagnostic.
374246074Sgabor         */
375246074Sgabor        DiagnosticType(String key) {
376246074Sgabor            this.key = key;
377246074Sgabor        }
378246074Sgabor    }
379246074Sgabor
380246074Sgabor    /**
381246074Sgabor     * A DiagnosticPosition provides information about the positions in a file
382246074Sgabor     * that gave rise to a diagnostic. It always defines a "preferred" position
383246074Sgabor     * that most accurately defines the location of the diagnostic, it may also
384246074Sgabor     * provide a related tree node that spans that location.
385246074Sgabor     */
386246074Sgabor    public static interface DiagnosticPosition {
387246074Sgabor        /** Gets the tree node, if any, to which the diagnostic applies. */
388246074Sgabor        JCTree getTree();
389246074Sgabor        /** If there is a tree node, get the start position of the tree node.
390246074Sgabor         *  Otherwise, just returns the same as getPreferredPosition(). */
391246074Sgabor        int getStartPosition();
392246074Sgabor        /** Get the position within the file that most accurately defines the
393246074Sgabor         *  location for the diagnostic. */
394246074Sgabor        int getPreferredPosition();
395246074Sgabor        /** If there is a tree node, and if endPositions are available, get
396246074Sgabor         *  the end position of the tree node. Otherwise, just returns the
397246074Sgabor         *  same as getPreferredPosition(). */
398246074Sgabor        int getEndPosition(EndPosTable endPosTable);
399246074Sgabor    }
400246074Sgabor
401246074Sgabor    /**
402246074Sgabor     * A DiagnosticPosition that simply identifies a position, but no related
403246074Sgabor     * tree node, as the location for a diagnostic. Used for scanner and parser
404246074Sgabor     * diagnostics. */
405246074Sgabor    public static class SimpleDiagnosticPosition implements DiagnosticPosition {
406246074Sgabor        public SimpleDiagnosticPosition(int pos) {
407246074Sgabor            this.pos = pos;
408246074Sgabor        }
409246074Sgabor
410246074Sgabor        public JCTree getTree() {
411246074Sgabor            return null;
412246074Sgabor        }
413246074Sgabor
414246074Sgabor        public int getStartPosition() {
415246074Sgabor            return pos;
416246074Sgabor        }
417246074Sgabor
418246074Sgabor        public int getPreferredPosition() {
419246074Sgabor            return pos;
420246074Sgabor        }
421246074Sgabor
422246074Sgabor        public int getEndPosition(EndPosTable endPosTable) {
423246074Sgabor            return pos;
424246074Sgabor        }
425246074Sgabor
426246074Sgabor        private final int pos;
427246074Sgabor    }
428246074Sgabor
429246074Sgabor    public enum DiagnosticFlag {
430246074Sgabor        MANDATORY,
431246074Sgabor        RESOLVE_ERROR,
432246074Sgabor        SYNTAX,
433246074Sgabor        RECOVERABLE,
434246074Sgabor        NON_DEFERRABLE,
435246074Sgabor        COMPRESSED
436246074Sgabor    }
437246074Sgabor
438246074Sgabor    private final DiagnosticSource source;
439246074Sgabor    private final DiagnosticPosition position;
440246074Sgabor    private final DiagnosticInfo diagnosticInfo;
441246074Sgabor    private final Set<DiagnosticFlag> flags;
442246074Sgabor    private final LintCategory lintCategory;
443246074Sgabor
444246074Sgabor    /** source line position (set lazily) */
445246074Sgabor    private SourcePosition sourcePosition;
446246074Sgabor
447246074Sgabor    /**
448246074Sgabor     * This class is used to defer the line/column position fetch logic after diagnostic construction.
449246074Sgabor     */
450246074Sgabor    class SourcePosition {
451246074Sgabor
452246074Sgabor        private final int line;
453246074Sgabor        private final int column;
454246074Sgabor
455246074Sgabor        SourcePosition() {
456246074Sgabor            int n = (position == null ? Position.NOPOS : position.getPreferredPosition());
457246074Sgabor            if (n == Position.NOPOS || source == null)
458246074Sgabor                line = column = -1;
459246074Sgabor            else {
460246074Sgabor                line = source.getLineNumber(n);
461246074Sgabor                column = source.getColumnNumber(n, true);
462246074Sgabor            }
463246074Sgabor        }
464246074Sgabor
465246074Sgabor        public int getLineNumber() {
466246074Sgabor            return line;
467246074Sgabor        }
468246074Sgabor
469246074Sgabor        public int getColumnNumber() {
470246074Sgabor            return column;
471246074Sgabor        }
472246074Sgabor    }
473246074Sgabor
474246074Sgabor    /**
475246074Sgabor     * A diagnostic key object encapsulates basic properties of a diagnostic, such as the resource key,
476246074Sgabor     * the arguments and the kind associated with the diagnostic object. Diagnostic keys can be either
477246074Sgabor     * created programmatically (by using the supplied factory method) or obtained through build-time
478246074Sgabor     * generated factory methods.
479246074Sgabor     */
480246074Sgabor    public static abstract class DiagnosticInfo {
481246074Sgabor
482246074Sgabor        /** The diagnostic kind (i.e. error). */
483246074Sgabor        DiagnosticType type;
484246074Sgabor
485246074Sgabor        /** The diagnostic prefix (i.e. 'javac'); used to compute full resource key. */
486246074Sgabor        String prefix;
487246074Sgabor
488246074Sgabor        /** The diagnostic code (i.e. 'cannot.resolve.sym'); together with {@code prefix} it forms
489246074Sgabor         * the full resource key. */
490246074Sgabor        String code;
491246074Sgabor
492246074Sgabor        /** The diagnostic arguments. */
493246074Sgabor        Object[] args;
494246074Sgabor
495246074Sgabor        private DiagnosticInfo(DiagnosticType type, String prefix, String code, Object... args) {
496246074Sgabor            this.type = type;
497246074Sgabor            this.prefix = prefix;
498246074Sgabor            this.code = code;
499246074Sgabor            this.args = args;
500246074Sgabor        }
501246074Sgabor
502246074Sgabor        /**
503246074Sgabor         * Compute the resource key.
504246074Sgabor         */
505246074Sgabor        public String key() {
506246074Sgabor            return prefix + "." + type.key + "." + code;
507246074Sgabor        }
508246074Sgabor
509246074Sgabor        /**
510246074Sgabor         * Static factory method; build a custom diagnostic key using given kind, prefix, code and args.
511246074Sgabor         */
512246074Sgabor        public static DiagnosticInfo of(DiagnosticType type, String prefix, String code, Object... args) {
513246074Sgabor            switch (type) {
514246074Sgabor                case ERROR:
515246074Sgabor                    return new Error(prefix, code, args);
516246074Sgabor                case WARNING:
517246074Sgabor                    return new Warning(prefix, code, args);
518246074Sgabor                case NOTE:
519246074Sgabor                    return new Note(prefix, code, args);
520246074Sgabor                case FRAGMENT:
521246074Sgabor                    return new Fragment(prefix, code, args);
522246074Sgabor                default:
523246074Sgabor                    Assert.error("Wrong diagnostic type: " + type);
524246074Sgabor                    return null;
525246074Sgabor            }
526246074Sgabor        }
527246074Sgabor
528246074Sgabor    }
529246074Sgabor
530246074Sgabor    /**
531246074Sgabor     * Class representing error diagnostic keys.
532246074Sgabor     */
533246074Sgabor    public static final class Error extends DiagnosticInfo {
534246074Sgabor        public Error(String prefix, String key, Object... args) {
535246074Sgabor            super(DiagnosticType.ERROR, prefix, key, args);
536246074Sgabor        }
537246074Sgabor    }
538246074Sgabor
539246074Sgabor    /**
540246074Sgabor     * Class representing warning diagnostic keys.
541246074Sgabor     */
542246074Sgabor    public static final class Warning extends DiagnosticInfo {
543246074Sgabor        public Warning(String prefix, String key, Object... args) {
544246074Sgabor            super(DiagnosticType.WARNING, prefix, key, args);
545246074Sgabor        }
546246074Sgabor    }
547246074Sgabor
548246074Sgabor    /**
549246074Sgabor     * Class representing note diagnostic keys.
550246074Sgabor     */
551246074Sgabor    public static final class Note extends DiagnosticInfo {
552246074Sgabor        public Note(String prefix, String key, Object... args) {
553246074Sgabor            super(DiagnosticType.NOTE, prefix, key, args);
554246074Sgabor        }
555246074Sgabor    }
556246074Sgabor
557246074Sgabor    /**
558246074Sgabor     * Class representing fragment diagnostic keys.
559246074Sgabor     */
560246074Sgabor    public static final class Fragment extends DiagnosticInfo {
561246074Sgabor        public Fragment(String prefix, String key, Object... args) {
562246074Sgabor            super(DiagnosticType.FRAGMENT, prefix, key, args);
563246074Sgabor        }
564246074Sgabor    }
565246074Sgabor
566246074Sgabor    /**
567246074Sgabor     * Create a diagnostic object.
568246074Sgabor     * @param formatter the formatter to use for the diagnostic
569246074Sgabor     * @param diagnosticInfo the diagnostic key
570246074Sgabor     * @param lc     the lint category for the diagnostic
571246074Sgabor     * @param source the name of the source file, or null if none.
572246074Sgabor     * @param pos the character offset within the source file, if given.
573246074Sgabor     */
574246074Sgabor    protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
575246074Sgabor                       DiagnosticInfo diagnosticInfo,
576246074Sgabor                       LintCategory lc,
577246074Sgabor                       Set<DiagnosticFlag> flags,
578246074Sgabor                       DiagnosticSource source,
579246074Sgabor                       DiagnosticPosition pos) {
580246074Sgabor        if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
581246074Sgabor            throw new IllegalArgumentException();
582246074Sgabor
583246074Sgabor        this.defaultFormatter = formatter;
584246074Sgabor        this.diagnosticInfo = diagnosticInfo;
585246074Sgabor        this.lintCategory = lc;
586246074Sgabor        this.flags = flags;
587246074Sgabor        this.source = source;
588246074Sgabor        this.position = pos;
589246074Sgabor    }
590246074Sgabor
591246074Sgabor    /**
592246074Sgabor     * Get the type of this diagnostic.
593246074Sgabor     * @return the type of this diagnostic
594246074Sgabor     */
595246074Sgabor    public DiagnosticType getType() {
596246074Sgabor        return diagnosticInfo.type;
597246074Sgabor    }
598246074Sgabor
599246074Sgabor    /**
600246074Sgabor     * Get the subdiagnostic list
601246074Sgabor     * @return subdiagnostic list
602246074Sgabor     */
603246074Sgabor    public List<JCDiagnostic> getSubdiagnostics() {
604246074Sgabor        return List.nil();
605246074Sgabor    }
606246074Sgabor
607246074Sgabor    public boolean isMultiline() {
608246074Sgabor        return false;
609246074Sgabor    }
610246074Sgabor
611246074Sgabor    /**
612246074Sgabor     * Check whether or not this diagnostic is required to be shown.
613246074Sgabor     * @return true if this diagnostic is required to be shown.
614246074Sgabor     */
615246074Sgabor    public boolean isMandatory() {
616246074Sgabor        return flags.contains(DiagnosticFlag.MANDATORY);
617246074Sgabor    }
618246074Sgabor
619246074Sgabor    /**
620246074Sgabor     * Check whether this diagnostic has an associated lint category.
621246074Sgabor     */
622246074Sgabor    public boolean hasLintCategory() {
623246074Sgabor        return (lintCategory != null);
624246074Sgabor    }
625246074Sgabor
626246074Sgabor    /**
627246074Sgabor     * Get the associated lint category, or null if none.
628246074Sgabor     */
629246074Sgabor    public LintCategory getLintCategory() {
630246074Sgabor        return lintCategory;
631246074Sgabor    }
632246074Sgabor
633246074Sgabor    /**
634246074Sgabor     * Get the name of the source file referred to by this diagnostic.
635246074Sgabor     * @return the name of the source referred to with this diagnostic, or null if none
636246074Sgabor     */
637246074Sgabor    @DefinedBy(Api.COMPILER)
638246074Sgabor    public JavaFileObject getSource() {
639246074Sgabor        if (source == null)
640246074Sgabor            return null;
641246074Sgabor        else
642246074Sgabor            return source.getFile();
643246074Sgabor    }
644246074Sgabor
645246074Sgabor    /**
646246074Sgabor     * Get the source referred to by this diagnostic.
647246074Sgabor     * @return the source referred to with this diagnostic, or null if none
648246074Sgabor     */
649246074Sgabor    public DiagnosticSource getDiagnosticSource() {
650246074Sgabor        return source;
651246074Sgabor    }
652246074Sgabor
653246074Sgabor    protected int getIntStartPosition() {
654246074Sgabor        return (position == null ? Position.NOPOS : position.getStartPosition());
655246074Sgabor    }
656246074Sgabor
657246074Sgabor    protected int getIntPosition() {
658246074Sgabor        return (position == null ? Position.NOPOS : position.getPreferredPosition());
659246074Sgabor    }
660246074Sgabor
661246074Sgabor    protected int getIntEndPosition() {
662246074Sgabor        return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
663246074Sgabor    }
664246074Sgabor
665246074Sgabor    @DefinedBy(Api.COMPILER)
666246074Sgabor    public long getStartPosition() {
667246074Sgabor        return getIntStartPosition();
668246074Sgabor    }
669246074Sgabor
670246074Sgabor    @DefinedBy(Api.COMPILER)
671246074Sgabor    public long getPosition() {
672246074Sgabor        return getIntPosition();
673246074Sgabor    }
674246074Sgabor
675246074Sgabor    @DefinedBy(Api.COMPILER)
676246074Sgabor    public long getEndPosition() {
677246074Sgabor        return getIntEndPosition();
678246074Sgabor    }
679246074Sgabor
680246074Sgabor    public DiagnosticPosition getDiagnosticPosition() {
681246074Sgabor        return position;
682246074Sgabor    }
683246074Sgabor
684246074Sgabor    /**
685246074Sgabor     * Get the line number within the source referred to by this diagnostic.
686246074Sgabor     * @return  the line number within the source referred to by this diagnostic
687246074Sgabor     */
688246074Sgabor    @DefinedBy(Api.COMPILER)
689246074Sgabor    public long getLineNumber() {
690246074Sgabor        if (sourcePosition == null) {
691246074Sgabor            sourcePosition = new SourcePosition();
692246074Sgabor        }
693246074Sgabor        return sourcePosition.getLineNumber();
694246074Sgabor    }
695246074Sgabor
696246074Sgabor    /**
697246074Sgabor     * Get the column number within the line of source referred to by this diagnostic.
698246074Sgabor     * @return  the column number within the line of source referred to by this diagnostic
699246074Sgabor     */
700246074Sgabor    @DefinedBy(Api.COMPILER)
701246074Sgabor    public long getColumnNumber() {
702246074Sgabor        if (sourcePosition == null) {
703246074Sgabor            sourcePosition = new SourcePosition();
704246074Sgabor        }
705246074Sgabor        return sourcePosition.getColumnNumber();
706246074Sgabor    }
707246074Sgabor
708246074Sgabor    /**
709246074Sgabor     * Get the arguments to be included in the text of the diagnostic.
710246074Sgabor     * @return  the arguments to be included in the text of the diagnostic
711246074Sgabor     */
712246074Sgabor    public Object[] getArgs() {
713246074Sgabor        return diagnosticInfo.args;
714246074Sgabor    }
715246074Sgabor
716246074Sgabor    /**
717246074Sgabor     * Get the prefix string associated with this type of diagnostic.
718246074Sgabor     * @return the prefix string associated with this type of diagnostic
719246074Sgabor     */
720246074Sgabor    public String getPrefix() {
721246074Sgabor        return getPrefix(diagnosticInfo.type);
722246074Sgabor    }
723246074Sgabor
724246074Sgabor    /**
725246074Sgabor     * Get the prefix string associated with a particular type of diagnostic.
726246074Sgabor     * @return the prefix string associated with a particular type of diagnostic
727246074Sgabor     */
728246074Sgabor    public String getPrefix(DiagnosticType dt) {
729246074Sgabor        return defaultFormatter.formatKind(this, Locale.getDefault());
730246074Sgabor    }
731246074Sgabor
732246074Sgabor    /**
733246074Sgabor     * Return the standard presentation of this diagnostic.
734246074Sgabor     */
735246074Sgabor    @Override
736246074Sgabor    public String toString() {
737246074Sgabor        return defaultFormatter.format(this, Locale.getDefault());
738246074Sgabor    }
739246074Sgabor
740246074Sgabor    private DiagnosticFormatter<JCDiagnostic> defaultFormatter;
741246074Sgabor    @Deprecated
742246074Sgabor    private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter;
743246074Sgabor
744246074Sgabor    // Methods for javax.tools.Diagnostic
745246074Sgabor
746246074Sgabor    @DefinedBy(Api.COMPILER)
747246074Sgabor    public Diagnostic.Kind getKind() {
748246074Sgabor        switch (diagnosticInfo.type) {
749246074Sgabor        case NOTE:
750246074Sgabor            return Diagnostic.Kind.NOTE;
751246074Sgabor        case WARNING:
752246074Sgabor            return flags.contains(DiagnosticFlag.MANDATORY)
753246074Sgabor                    ? Diagnostic.Kind.MANDATORY_WARNING
754246074Sgabor                    : Diagnostic.Kind.WARNING;
755246074Sgabor        case ERROR:
756246074Sgabor            return Diagnostic.Kind.ERROR;
757246074Sgabor        default:
758246074Sgabor            return Diagnostic.Kind.OTHER;
759246074Sgabor        }
760246074Sgabor    }
761246074Sgabor
762246074Sgabor    @DefinedBy(Api.COMPILER)
763246074Sgabor    public String getCode() {
764246074Sgabor        return diagnosticInfo.key();
765246074Sgabor    }
766246074Sgabor
767246074Sgabor    @DefinedBy(Api.COMPILER)
768246074Sgabor    public String getMessage(Locale locale) {
769246074Sgabor        return defaultFormatter.formatMessage(this, locale);
770246074Sgabor    }
771246074Sgabor
772246074Sgabor    public void setFlag(DiagnosticFlag flag) {
773246074Sgabor        flags.add(flag);
774246074Sgabor
775246074Sgabor        if (diagnosticInfo.type == DiagnosticType.ERROR) {
776246074Sgabor            switch (flag) {
777246074Sgabor                case SYNTAX:
778246074Sgabor                    flags.remove(DiagnosticFlag.RECOVERABLE);
779246074Sgabor                    break;
780246074Sgabor                case RESOLVE_ERROR:
781246074Sgabor                    flags.add(DiagnosticFlag.RECOVERABLE);
782246074Sgabor                    break;
783246074Sgabor            }
784246074Sgabor        }
785246074Sgabor    }
786246074Sgabor
787246074Sgabor    public boolean isFlagSet(DiagnosticFlag flag) {
788246074Sgabor        return flags.contains(flag);
789246074Sgabor    }
790246074Sgabor
791246074Sgabor    public static class MultilineDiagnostic extends JCDiagnostic {
792246074Sgabor
793246074Sgabor        private final List<JCDiagnostic> subdiagnostics;
794246074Sgabor
795246074Sgabor        public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) {
796246074Sgabor            super(other.defaultFormatter,
797246074Sgabor                  other.diagnosticInfo,
798246074Sgabor                  other.getLintCategory(),
799246074Sgabor                  other.flags,
800246074Sgabor                  other.getDiagnosticSource(),
801246074Sgabor                  other.position);
802246074Sgabor            this.subdiagnostics = subdiagnostics;
803246074Sgabor        }
804246074Sgabor
805246074Sgabor        @Override
806246074Sgabor        public List<JCDiagnostic> getSubdiagnostics() {
807246074Sgabor            return subdiagnostics;
808246074Sgabor        }
809246074Sgabor
810246074Sgabor        @Override
811246074Sgabor        public boolean isMultiline() {
812246074Sgabor            return true;
813246074Sgabor        }
814246074Sgabor    }
815246074Sgabor}
816246074Sgabor