1 | /* |
2 | * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
23 | * have any questions. |
24 | */ |
25 | |
26 | package com.sun.tools.javac.util; |
27 | |
28 | import java.net.URI; |
29 | import java.text.MessageFormat; |
30 | import java.util.Locale; |
31 | import java.util.Map; |
32 | import java.util.MissingResourceException; |
33 | import java.util.ResourceBundle; |
34 | |
35 | import javax.tools.Diagnostic; |
36 | import javax.tools.FileObject; |
37 | import javax.tools.JavaFileObject; |
38 | |
39 | import com.sun.tools.javac.tree.JCTree; |
40 | |
41 | import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*; |
42 | |
43 | /** An abstraction of a diagnostic message generated by the compiler. |
44 | * |
45 | * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
46 | * you write code that depends on this, you do so at your own risk. |
47 | * This code and its internal interfaces are subject to change or |
48 | * deletion without notice.</b> |
49 | */ |
50 | public class JCDiagnostic implements Diagnostic<JavaFileObject> { |
51 | /** A factory for creating diagnostic objects. */ |
52 | public static class Factory { |
53 | /** The context key for the diagnostic factory. */ |
54 | protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey = |
55 | new Context.Key<JCDiagnostic.Factory>(); |
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 | final Messages messages; |
66 | final String prefix; |
67 | |
68 | /** Create a new diagnostic factory. */ |
69 | protected Factory(Context context) { |
70 | context.put(diagnosticFactoryKey, this); |
71 | messages = Messages.instance(context); |
72 | prefix = "compiler"; |
73 | } |
74 | |
75 | /** Create a new diagnostic factory. */ |
76 | public Factory(Messages messages, String prefix) { |
77 | this.messages = messages; |
78 | this.prefix = prefix; |
79 | } |
80 | |
81 | /** |
82 | * Create an error diagnostic. |
83 | * @param source The source of the compilation unit, if any, in which to report the error. |
84 | * @param pos The source position at which to report the error. |
85 | * @param key The key for the localized error message. |
86 | * @param args Fields of the error message. |
87 | */ |
88 | public JCDiagnostic error( |
89 | DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { |
90 | return new JCDiagnostic(messages, ERROR, true, source, pos, qualify(ERROR, key), args); |
91 | } |
92 | |
93 | /** |
94 | * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. |
95 | * @param source The source of the compilation unit, if any, in which to report the warning. |
96 | * @param pos The source position at which to report the warning. |
97 | * @param key The key for the localized error message. |
98 | * @param args Fields of the error message. |
99 | * @see MandatoryWarningHandler |
100 | */ |
101 | public JCDiagnostic mandatoryWarning( |
102 | DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { |
103 | return new JCDiagnostic(messages, WARNING, true, source, pos, qualify(WARNING, key), args); |
104 | } |
105 | |
106 | /** |
107 | * Create a warning diagnostic. |
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 error message. |
111 | * @param args Fields of the error message. |
112 | */ |
113 | public JCDiagnostic warning( |
114 | DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { |
115 | return new JCDiagnostic(messages, WARNING, false, source, pos, qualify(WARNING, key), args); |
116 | } |
117 | |
118 | /** |
119 | * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options. |
120 | * @param key The key for the localized error message. |
121 | * @param args Fields of the error message. |
122 | * @see MandatoryWarningHandler |
123 | */ |
124 | public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) { |
125 | return new JCDiagnostic(messages, NOTE, true, source, null, qualify(NOTE, key), args); |
126 | } |
127 | |
128 | /** |
129 | * Create a note diagnostic. |
130 | * @param key The key for the localized error message. |
131 | * @param args Fields of the error message. |
132 | */ |
133 | public JCDiagnostic note(String key, Object... args) { |
134 | return note(null, null, key, args); |
135 | } |
136 | |
137 | /** |
138 | * Create a note diagnostic. |
139 | * @param source The source of the compilation unit, if any, in which to report the note. |
140 | * @param pos The source position at which to report the note. |
141 | * @param key The key for the localized error message. |
142 | * @param args Fields of the error message. |
143 | */ |
144 | public JCDiagnostic note( |
145 | DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { |
146 | return new JCDiagnostic(messages, NOTE, false, source, pos, qualify(NOTE, key), args); |
147 | } |
148 | |
149 | /** |
150 | * Create a fragment diagnostic, for use as an argument in other diagnostics |
151 | * @param key The key for the localized error message. |
152 | * @param args Fields of the error message. |
153 | */ |
154 | public JCDiagnostic fragment(String key, Object... args) { |
155 | return new JCDiagnostic(messages, FRAGMENT, false, null, null, qualify(FRAGMENT, key), args); |
156 | } |
157 | |
158 | protected String qualify(DiagnosticType t, String key) { |
159 | return prefix + "." + t.key + "." + key; |
160 | } |
161 | } |
162 | |
163 | |
164 | |
165 | /** |
166 | * Create a fragment diagnostic, for use as an argument in other diagnostics |
167 | * @param key The key for the localized error message. |
168 | * @param args Fields of the error message. |
169 | */ |
170 | // should be deprecated |
171 | public static JCDiagnostic fragment(String key, Object... args) { |
172 | return new JCDiagnostic(Messages.getDefaultMessages(), |
173 | FRAGMENT, |
174 | false, |
175 | null, |
176 | null, |
177 | "compiler." + FRAGMENT.key + "." + key, |
178 | args); |
179 | } |
180 | |
181 | /** |
182 | * A simple abstraction of a source file, as needed for use in a diagnostic message. |
183 | */ |
184 | // Note: This class may be superceded by a more general abstraction |
185 | public interface DiagnosticSource { |
186 | JavaFileObject getFile(); |
187 | CharSequence getName(); |
188 | int getLineNumber(int pos); |
189 | int getColumnNumber(int pos); |
190 | Map<JCTree, Integer> getEndPosTable(); |
191 | }; |
192 | |
193 | /** |
194 | * A DiagnosticType defines the type of the diagnostic. |
195 | **/ |
196 | public enum DiagnosticType { |
197 | /** A fragment of an enclosing diagnostic. */ |
198 | FRAGMENT("misc"), |
199 | /** A note: similar to, but less serious than, a warning. */ |
200 | NOTE("note"), |
201 | /** A warning. */ |
202 | WARNING("warn"), |
203 | /** An error. */ |
204 | ERROR("err"); |
205 | |
206 | final String key; |
207 | |
208 | /** Create a DiagnosticType. |
209 | * @param key A string used to create the resource key for the diagnostic. |
210 | */ |
211 | DiagnosticType(String key) { |
212 | this.key = key; |
213 | } |
214 | }; |
215 | |
216 | /** |
217 | * A DiagnosticPosition provides information about the positions in a file |
218 | * that gave rise to a diagnostic. It always defines a "preferred" position |
219 | * that most accurately defines the location of the diagnostic, it may also |
220 | * provide a related tree node that spans that location. |
221 | */ |
222 | public static interface DiagnosticPosition { |
223 | /** Gets the tree node, if any, to which the diagnostic applies. */ |
224 | JCTree getTree(); |
225 | /** If there is a tree node, get the start position of the tree node. |
226 | * Otherwise, just returns the same as getPreferredPosition(). */ |
227 | int getStartPosition(); |
228 | /** Get the position within the file that most accurately defines the |
229 | * location for the diagnostic. */ |
230 | int getPreferredPosition(); |
231 | /** If there is a tree node, and if endPositions are available, get |
232 | * the end position of the tree node. Otherwise, just returns the |
233 | * same as getPreferredPosition(). */ |
234 | int getEndPosition(Map<JCTree, Integer> endPosTable); |
235 | } |
236 | |
237 | /** |
238 | * A DiagnosticPosition that simply identifies a position, but no related |
239 | * tree node, as the location for a diagnostic. Used for scanner and parser |
240 | * diagnostics. */ |
241 | public static class SimpleDiagnosticPosition implements DiagnosticPosition { |
242 | public SimpleDiagnosticPosition(int pos) { |
243 | this.pos = pos; |
244 | } |
245 | |
246 | public JCTree getTree() { |
247 | return null; |
248 | } |
249 | |
250 | public int getStartPosition() { |
251 | return pos; |
252 | } |
253 | |
254 | public int getPreferredPosition() { |
255 | return pos; |
256 | } |
257 | |
258 | public int getEndPosition(Map<JCTree, Integer> endPosTable) { |
259 | return pos; |
260 | } |
261 | |
262 | private final int pos; |
263 | } |
264 | |
265 | private final Messages messages; |
266 | private final DiagnosticType type; |
267 | private final DiagnosticSource source; |
268 | private final DiagnosticPosition position; |
269 | private final int line; |
270 | private final int column; |
271 | private final String key; |
272 | private final Object[] args; |
273 | private boolean mandatory; |
274 | |
275 | /** |
276 | * Create a diagnostic object. |
277 | * @param messages the resource for localized messages |
278 | * @param dt the type of diagnostic |
279 | * @param name the name of the source file, or null if none. |
280 | * @param pos the character offset within the source file, if given. |
281 | * @param key a resource key to identify the text of the diagnostic |
282 | * @param args arguments to be included in the text of the diagnostic |
283 | */ |
284 | protected JCDiagnostic(Messages messages, |
285 | DiagnosticType dt, |
286 | boolean mandatory, |
287 | DiagnosticSource source, |
288 | DiagnosticPosition pos, |
289 | String key, |
290 | Object ... args) { |
291 | if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS) |
292 | throw new IllegalArgumentException(); |
293 | |
294 | this.messages = messages; |
295 | this.type = dt; |
296 | this.mandatory = mandatory; |
297 | this.source = source; |
298 | this.position = pos; |
299 | this.key = key; |
300 | this.args = args; |
301 | |
302 | int n = (pos == null ? Position.NOPOS : pos.getPreferredPosition()); |
303 | if (n == Position.NOPOS || source == null) |
304 | line = column = -1; |
305 | else { |
306 | line = source.getLineNumber(n); |
307 | column = source.getColumnNumber(n); |
308 | } |
309 | } |
310 | |
311 | /** |
312 | * Get the type of this diagnostic. |
313 | * @return the type of this diagnostic |
314 | */ |
315 | public DiagnosticType getType() { |
316 | return type; |
317 | } |
318 | |
319 | /** |
320 | * Check whether or not this diagnostic is required to be shown. |
321 | * @return true if this diagnostic is required to be shown. |
322 | */ |
323 | public boolean isMandatory() { |
324 | return mandatory; |
325 | } |
326 | |
327 | /** |
328 | * Get the name of the source file referred to by this diagnostic. |
329 | * @return the name of the source referred to with this diagnostic, or null if none |
330 | */ |
331 | public JavaFileObject getSource() { |
332 | if (source == null) |
333 | return null; |
334 | else |
335 | return source.getFile(); |
336 | } |
337 | |
338 | /** |
339 | * Get the name of the source file referred to by this diagnostic. |
340 | * @return the name of the source referred to with this diagnostic, or null if none |
341 | */ |
342 | public String getSourceName() { |
343 | JavaFileObject s = getSource(); |
344 | return s == null ? null : JavacFileManager.getJavacFileName(s); |
345 | } |
346 | |
347 | /** |
348 | * Get the source referred to by this diagnostic. |
349 | * @return the source referred to with this diagnostic, or null if none |
350 | */ |
351 | public DiagnosticSource getDiagnosticSource() { |
352 | return source; |
353 | } |
354 | |
355 | protected int getIntStartPosition() { |
356 | return (position == null ? Position.NOPOS : position.getStartPosition()); |
357 | } |
358 | |
359 | protected int getIntPosition() { |
360 | return (position == null ? Position.NOPOS : position.getPreferredPosition()); |
361 | } |
362 | |
363 | protected int getIntEndPosition() { |
364 | return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable())); |
365 | } |
366 | |
367 | public long getStartPosition() { |
368 | return getIntStartPosition(); |
369 | } |
370 | |
371 | public long getPosition() { |
372 | return getIntPosition(); |
373 | } |
374 | |
375 | public long getEndPosition() { |
376 | return getIntEndPosition(); |
377 | } |
378 | |
379 | /** |
380 | * Get the line number within the source referred to by this diagnostic. |
381 | * @return the line number within the source referred to by this diagnostic |
382 | */ |
383 | public long getLineNumber() { |
384 | return line; |
385 | } |
386 | |
387 | /** |
388 | * Get the column number within the line of source referred to by this diagnostic. |
389 | * @return the column number within the line of source referred to by this diagnostic |
390 | */ |
391 | public long getColumnNumber() { |
392 | return column; |
393 | } |
394 | |
395 | /** |
396 | * Get the arguments to be included in the text of the diagnostic. |
397 | * @return the arguments to be included in the text of the diagnostic |
398 | */ |
399 | public Object[] getArgs() { |
400 | return args; |
401 | } |
402 | |
403 | /** |
404 | * Get the prefix string associated with this type of diagnostic. |
405 | * @return the prefix string associated with this type of diagnostic |
406 | */ |
407 | public String getPrefix() { |
408 | return getPrefix(type); |
409 | } |
410 | |
411 | /** |
412 | * Get the prefix string associated with a particular type of diagnostic. |
413 | * @return the prefix string associated with a particular type of diagnostic |
414 | */ |
415 | public String getPrefix(DiagnosticType dt) { |
416 | switch (dt) { |
417 | case FRAGMENT: return ""; |
418 | case NOTE: return getLocalizedString("compiler.note.note"); |
419 | case WARNING: return getLocalizedString("compiler.warn.warning"); |
420 | case ERROR: return getLocalizedString("compiler.err.error"); |
421 | default: |
422 | throw new AssertionError("Unknown diagnostic type: " + dt); |
423 | } |
424 | } |
425 | |
426 | /** |
427 | * Return the standard presentation of this diagnostic. |
428 | */ |
429 | public String toString() { |
430 | if (defaultFormatter == null) { |
431 | defaultFormatter = |
432 | new DiagnosticFormatter(); |
433 | } |
434 | return defaultFormatter.format(this); |
435 | } |
436 | |
437 | private static DiagnosticFormatter defaultFormatter; |
438 | |
439 | private static final String messageBundleName = |
440 | "com.sun.tools.javac.resources.compiler"; |
441 | |
442 | private String getLocalizedString(String key, Object... args) { |
443 | String[] strings = new String[args.length]; |
444 | for (int i = 0; i < strings.length; i++) { |
445 | Object arg = args[i]; |
446 | if (arg == null) |
447 | strings[i] = null; |
448 | else if (arg instanceof JCDiagnostic) |
449 | strings[i] = ((JCDiagnostic) arg).getMessage(null); |
450 | else |
451 | strings[i] = arg.toString(); |
452 | } |
453 | |
454 | return messages.getLocalizedString(key, (Object[]) strings); |
455 | } |
456 | |
457 | // Methods for javax.tools.Diagnostic |
458 | |
459 | public Diagnostic.Kind getKind() { |
460 | switch (type) { |
461 | case NOTE: |
462 | return Diagnostic.Kind.NOTE; |
463 | case WARNING: |
464 | return mandatory ? Diagnostic.Kind.MANDATORY_WARNING |
465 | : Diagnostic.Kind.WARNING; |
466 | case ERROR: |
467 | return Diagnostic.Kind.ERROR; |
468 | default: |
469 | return Diagnostic.Kind.OTHER; |
470 | } |
471 | } |
472 | |
473 | public String getCode() { |
474 | return key; |
475 | } |
476 | |
477 | public String getMessage(Locale locale) { |
478 | // RFE 6406133: JCDiagnostic.getMessage ignores locale argument |
479 | return getLocalizedString(key, args); |
480 | } |
481 | |
482 | } |