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