001 /*
002 * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package com.sun.tools.javac.util;
027
028 import java.util.Locale;
029 import java.util.Map;
030
031 import javax.tools.Diagnostic;
032 import javax.tools.JavaFileObject;
033
034 import com.sun.tools.javac.api.DiagnosticFormatter;
035 import com.sun.tools.javac.file.JavacFileManager;
036 import com.sun.tools.javac.tree.JCTree;
037
038 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
039
040 /** An abstraction of a diagnostic message generated by the compiler.
041 *
042 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
043 * you write code that depends on this, you do so at your own risk.
044 * This code and its internal interfaces are subject to change or
045 * deletion without notice.</b>
046 */
047 public class JCDiagnostic implements Diagnostic<JavaFileObject> {
048 /** A factory for creating diagnostic objects. */
049 public static class Factory {
050 /** The context key for the diagnostic factory. */
051 protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey =
052 new Context.Key<JCDiagnostic.Factory>();
053
054 /** Get the Factory instance for this context. */
055 public static Factory instance(Context context) {
056 Factory instance = context.get(diagnosticFactoryKey);
057 if (instance == null)
058 instance = new Factory(context);
059 return instance;
060 }
061
062 DiagnosticFormatter<JCDiagnostic> formatter;
063 final String prefix;
064
065 /** Create a new diagnostic factory. */
066 protected Factory(Context context) {
067 this(JavacMessages.instance(context), "compiler");
068 context.put(diagnosticFactoryKey, this);
069 }
070
071 /** Create a new diagnostic factory. */
072 public Factory(JavacMessages messages, String prefix) {
073 this.prefix = prefix;
074 this.formatter = new BasicDiagnosticFormatter(messages);
075 }
076
077 /**
078 * Create an error diagnostic.
079 * @param source The source of the compilation unit, if any, in which to report the error.
080 * @param pos The source position at which to report the error.
081 * @param key The key for the localized error message.
082 * @param args Fields of the error message.
083 */
084 public JCDiagnostic error(
085 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
086 return new JCDiagnostic(formatter, ERROR, true, source, pos, qualify(ERROR, key), args);
087 }
088
089 /**
090 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
091 * @param source The source of the compilation unit, if any, in which to report the warning.
092 * @param pos The source position at which to report the warning.
093 * @param key The key for the localized error message.
094 * @param args Fields of the error message.
095 * @see MandatoryWarningHandler
096 */
097 public JCDiagnostic mandatoryWarning(
098 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
099 return new JCDiagnostic(formatter, WARNING, true, source, pos, qualify(WARNING, key), args);
100 }
101
102 /**
103 * Create a warning diagnostic.
104 * @param source The source of the compilation unit, if any, in which to report the warning.
105 * @param pos The source position at which to report the warning.
106 * @param key The key for the localized error message.
107 * @param args Fields of the error message.
108 */
109 public JCDiagnostic warning(
110 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
111 return new JCDiagnostic(formatter, WARNING, false, source, pos, qualify(WARNING, key), args);
112 }
113
114 /**
115 * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
116 * @param key The key for the localized error message.
117 * @param args Fields of the error message.
118 * @see MandatoryWarningHandler
119 */
120 public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
121 return new JCDiagnostic(formatter, NOTE, true, source, null, qualify(NOTE, key), args);
122 }
123
124 /**
125 * Create a note diagnostic.
126 * @param key The key for the localized error message.
127 * @param args Fields of the error message.
128 */
129 public JCDiagnostic note(String key, Object... args) {
130 return note(null, null, key, args);
131 }
132
133 /**
134 * Create a note diagnostic.
135 * @param source The source of the compilation unit, if any, in which to report the note.
136 * @param pos The source position at which to report the note.
137 * @param key The key for the localized error message.
138 * @param args Fields of the error message.
139 */
140 public JCDiagnostic note(
141 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
142 return new JCDiagnostic(formatter, NOTE, false, source, pos, qualify(NOTE, key), args);
143 }
144
145 /**
146 * Create a fragment diagnostic, for use as an argument in other diagnostics
147 * @param key The key for the localized error message.
148 * @param args Fields of the error message.
149 */
150 public JCDiagnostic fragment(String key, Object... args) {
151 return new JCDiagnostic(formatter, FRAGMENT, false, null, null, qualify(FRAGMENT, key), args);
152 }
153
154 protected String qualify(DiagnosticType t, String key) {
155 return prefix + "." + t.key + "." + key;
156 }
157 }
158
159
160
161 /**
162 * Create a fragment diagnostic, for use as an argument in other diagnostics
163 * @param key The key for the localized error message.
164 * @param args Fields of the error message.
165 *
166 */
167 @Deprecated
168 public static JCDiagnostic fragment(String key, Object... args) {
169 return new JCDiagnostic(getFragmentFormatter(),
170 FRAGMENT,
171 false,
172 null,
173 null,
174 "compiler." + FRAGMENT.key + "." + key,
175 args);
176 }
177 //where
178 @Deprecated
179 public static DiagnosticFormatter<JCDiagnostic> getFragmentFormatter() {
180 if (fragmentFormatter == null) {
181 fragmentFormatter = new BasicDiagnosticFormatter(JavacMessages.getDefaultMessages());
182 }
183 return fragmentFormatter;
184 }
185
186 /**
187 * A DiagnosticType defines the type of the diagnostic.
188 **/
189 public enum DiagnosticType {
190 /** A fragment of an enclosing diagnostic. */
191 FRAGMENT("misc"),
192 /** A note: similar to, but less serious than, a warning. */
193 NOTE("note"),
194 /** A warning. */
195 WARNING("warn"),
196 /** An error. */
197 ERROR("err");
198
199 final String key;
200
201 /** Create a DiagnosticType.
202 * @param key A string used to create the resource key for the diagnostic.
203 */
204 DiagnosticType(String key) {
205 this.key = key;
206 }
207 };
208
209 /**
210 * A DiagnosticPosition provides information about the positions in a file
211 * that gave rise to a diagnostic. It always defines a "preferred" position
212 * that most accurately defines the location of the diagnostic, it may also
213 * provide a related tree node that spans that location.
214 */
215 public static interface DiagnosticPosition {
216 /** Gets the tree node, if any, to which the diagnostic applies. */
217 JCTree getTree();
218 /** If there is a tree node, get the start position of the tree node.
219 * Otherwise, just returns the same as getPreferredPosition(). */
220 int getStartPosition();
221 /** Get the position within the file that most accurately defines the
222 * location for the diagnostic. */
223 int getPreferredPosition();
224 /** If there is a tree node, and if endPositions are available, get
225 * the end position of the tree node. Otherwise, just returns the
226 * same as getPreferredPosition(). */
227 int getEndPosition(Map<JCTree, Integer> endPosTable);
228 }
229
230 /**
231 * A DiagnosticPosition that simply identifies a position, but no related
232 * tree node, as the location for a diagnostic. Used for scanner and parser
233 * diagnostics. */
234 public static class SimpleDiagnosticPosition implements DiagnosticPosition {
235 public SimpleDiagnosticPosition(int pos) {
236 this.pos = pos;
237 }
238
239 public JCTree getTree() {
240 return null;
241 }
242
243 public int getStartPosition() {
244 return pos;
245 }
246
247 public int getPreferredPosition() {
248 return pos;
249 }
250
251 public int getEndPosition(Map<JCTree, Integer> endPosTable) {
252 return pos;
253 }
254
255 private final int pos;
256 }
257
258 private final DiagnosticType type;
259 private final DiagnosticSource source;
260 private final DiagnosticPosition position;
261 private final int line;
262 private final int column;
263 private final String key;
264 protected Object[] args;
265 private boolean mandatory;
266
267 /**
268 * Create a diagnostic object.
269 * @param messages the resource for localized messages
270 * @param dt the type of diagnostic
271 * @param name the name of the source file, or null if none.
272 * @param pos the character offset within the source file, if given.
273 * @param key a resource key to identify the text of the diagnostic
274 * @param args arguments to be included in the text of the diagnostic
275 */
276 protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
277 DiagnosticType dt,
278 boolean mandatory,
279 DiagnosticSource source,
280 DiagnosticPosition pos,
281 String key,
282 Object ... args) {
283 if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
284 throw new IllegalArgumentException();
285
286 this.defaultFormatter = formatter;
287 this.type = dt;
288 this.mandatory = mandatory;
289 this.source = source;
290 this.position = pos;
291 this.key = key;
292 this.args = args;
293
294 int n = (pos == null ? Position.NOPOS : pos.getPreferredPosition());
295 if (n == Position.NOPOS || source == null)
296 line = column = -1;
297 else {
298 line = source.getLineNumber(n);
299 column = source.getColumnNumber(n, true);
300 }
301 }
302
303 /**
304 * Get the type of this diagnostic.
305 * @return the type of this diagnostic
306 */
307 public DiagnosticType getType() {
308 return type;
309 }
310
311 /**
312 * Get the subdiagnostic list
313 * @return subdiagnostic list
314 */
315 public List<JCDiagnostic> getSubdiagnostics() {
316 return List.nil();
317 }
318
319 public boolean isMultiline() {
320 return false;
321 }
322
323 /**
324 * Check whether or not this diagnostic is required to be shown.
325 * @return true if this diagnostic is required to be shown.
326 */
327 public boolean isMandatory() {
328 return mandatory;
329 }
330
331 /**
332 * Get the name of the source file referred to by this diagnostic.
333 * @return the name of the source referred to with this diagnostic, or null if none
334 */
335 public JavaFileObject getSource() {
336 if (source == null)
337 return null;
338 else
339 return source.getFile();
340 }
341
342 /**
343 * Get the name of the source file referred to by this diagnostic.
344 * @return the name of the source referred to with this diagnostic, or null if none
345 */
346 public String getSourceName() {
347 JavaFileObject s = getSource();
348 return s == null ? null : JavacFileManager.getJavacFileName(s);
349 }
350
351 /**
352 * Get the source referred to by this diagnostic.
353 * @return the source referred to with this diagnostic, or null if none
354 */
355 public DiagnosticSource getDiagnosticSource() {
356 return source;
357 }
358
359 protected int getIntStartPosition() {
360 return (position == null ? Position.NOPOS : position.getStartPosition());
361 }
362
363 protected int getIntPosition() {
364 return (position == null ? Position.NOPOS : position.getPreferredPosition());
365 }
366
367 protected int getIntEndPosition() {
368 return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
369 }
370
371 public long getStartPosition() {
372 return getIntStartPosition();
373 }
374
375 public long getPosition() {
376 return getIntPosition();
377 }
378
379 public long getEndPosition() {
380 return getIntEndPosition();
381 }
382
383 /**
384 * Get the line number within the source referred to by this diagnostic.
385 * @return the line number within the source referred to by this diagnostic
386 */
387 public long getLineNumber() {
388 return line;
389 }
390
391 /**
392 * Get the column number within the line of source referred to by this diagnostic.
393 * @return the column number within the line of source referred to by this diagnostic
394 */
395 public long getColumnNumber() {
396 return column;
397 }
398
399 /**
400 * Get the arguments to be included in the text of the diagnostic.
401 * @return the arguments to be included in the text of the diagnostic
402 */
403 public Object[] getArgs() {
404 return args;
405 }
406
407 /**
408 * Get the prefix string associated with this type of diagnostic.
409 * @return the prefix string associated with this type of diagnostic
410 */
411 public String getPrefix() {
412 return getPrefix(type);
413 }
414
415 /**
416 * Get the prefix string associated with a particular type of diagnostic.
417 * @return the prefix string associated with a particular type of diagnostic
418 */
419 public String getPrefix(DiagnosticType dt) {
420 return defaultFormatter.formatKind(this, Locale.getDefault());
421 }
422
423 /**
424 * Return the standard presentation of this diagnostic.
425 */
426 public String toString() {
427 return defaultFormatter.format(this,Locale.getDefault());
428 }
429
430 private DiagnosticFormatter<JCDiagnostic> defaultFormatter;
431 @Deprecated
432 private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter;
433
434 // Methods for javax.tools.Diagnostic
435
436 public Diagnostic.Kind getKind() {
437 switch (type) {
438 case NOTE:
439 return Diagnostic.Kind.NOTE;
440 case WARNING:
441 return mandatory ? Diagnostic.Kind.MANDATORY_WARNING
442 : Diagnostic.Kind.WARNING;
443 case ERROR:
444 return Diagnostic.Kind.ERROR;
445 default:
446 return Diagnostic.Kind.OTHER;
447 }
448 }
449
450 public String getCode() {
451 return key;
452 }
453
454 public String getMessage(Locale locale) {
455 return defaultFormatter.formatMessage(this, locale);
456 }
457
458 public static class MultilineDiagnostic extends JCDiagnostic {
459
460 private final List<JCDiagnostic> subdiagnostics;
461
462 public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) {
463 super(other.defaultFormatter,
464 other.getType(),
465 other.isMandatory(),
466 other.getDiagnosticSource(),
467 other.position,
468 other.getCode(),
469 other.getArgs());
470 this.subdiagnostics = subdiagnostics;
471 }
472
473 @Override
474 public List<JCDiagnostic> getSubdiagnostics() {
475 return subdiagnostics;
476 }
477
478 @Override
479 public boolean isMultiline() {
480 return true;
481 }
482 }
483 }