Mon, 16 Jun 2008 13:28:00 -0700
6714364: refactor javac File handling code into new javac.file package
Reviewed-by: mcimadamore
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 */
26 package com.sun.tools.javac.util;
28 import java.util.Locale;
29 import java.util.Map;
31 import javax.tools.Diagnostic;
32 import javax.tools.JavaFileObject;
34 import com.sun.tools.javac.file.JavacFileManager;
35 import com.sun.tools.javac.tree.JCTree;
37 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
39 /** An abstraction of a diagnostic message generated by the compiler.
40 *
41 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
42 * you write code that depends on this, you do so at your own risk.
43 * This code and its internal interfaces are subject to change or
44 * deletion without notice.</b>
45 */
46 public class JCDiagnostic implements Diagnostic<JavaFileObject> {
47 /** A factory for creating diagnostic objects. */
48 public static class Factory {
49 /** The context key for the diagnostic factory. */
50 protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey =
51 new Context.Key<JCDiagnostic.Factory>();
53 /** Get the Factory instance for this context. */
54 public static Factory instance(Context context) {
55 Factory instance = context.get(diagnosticFactoryKey);
56 if (instance == null)
57 instance = new Factory(context);
58 return instance;
59 }
61 final Messages messages;
62 final String prefix;
64 /** Create a new diagnostic factory. */
65 protected Factory(Context context) {
66 context.put(diagnosticFactoryKey, this);
67 messages = Messages.instance(context);
68 prefix = "compiler";
69 }
71 /** Create a new diagnostic factory. */
72 public Factory(Messages messages, String prefix) {
73 this.messages = messages;
74 this.prefix = prefix;
75 }
77 /**
78 * Create an error diagnostic.
79 * @param source The source of the compilation unit, if any, in which to report the error.
80 * @param pos The source position at which to report the error.
81 * @param key The key for the localized error message.
82 * @param args Fields of the error message.
83 */
84 public JCDiagnostic error(
85 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
86 return new JCDiagnostic(messages, ERROR, true, source, pos, qualify(ERROR, key), args);
87 }
89 /**
90 * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
91 * @param source The source of the compilation unit, if any, in which to report the warning.
92 * @param pos The source position at which to report the warning.
93 * @param key The key for the localized error message.
94 * @param args Fields of the error message.
95 * @see MandatoryWarningHandler
96 */
97 public JCDiagnostic mandatoryWarning(
98 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
99 return new JCDiagnostic(messages, WARNING, true, source, pos, qualify(WARNING, key), args);
100 }
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(messages, WARNING, false, source, pos, qualify(WARNING, key), args);
112 }
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(messages, NOTE, true, source, null, qualify(NOTE, key), args);
122 }
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 }
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(messages, NOTE, false, source, pos, qualify(NOTE, key), args);
143 }
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(messages, FRAGMENT, false, null, null, qualify(FRAGMENT, key), args);
152 }
154 protected String qualify(DiagnosticType t, String key) {
155 return prefix + "." + t.key + "." + key;
156 }
157 }
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 // should be deprecated
167 public static JCDiagnostic fragment(String key, Object... args) {
168 return new JCDiagnostic(Messages.getDefaultMessages(),
169 FRAGMENT,
170 false,
171 null,
172 null,
173 "compiler." + FRAGMENT.key + "." + key,
174 args);
175 }
177 /**
178 * A simple abstraction of a source file, as needed for use in a diagnostic message.
179 */
180 // Note: This class may be superceded by a more general abstraction
181 public interface DiagnosticSource {
182 JavaFileObject getFile();
183 CharSequence getName();
184 int getLineNumber(int pos);
185 int getColumnNumber(int pos);
186 Map<JCTree, Integer> getEndPosTable();
187 };
189 /**
190 * A DiagnosticType defines the type of the diagnostic.
191 **/
192 public enum DiagnosticType {
193 /** A fragment of an enclosing diagnostic. */
194 FRAGMENT("misc"),
195 /** A note: similar to, but less serious than, a warning. */
196 NOTE("note"),
197 /** A warning. */
198 WARNING("warn"),
199 /** An error. */
200 ERROR("err");
202 final String key;
204 /** Create a DiagnosticType.
205 * @param key A string used to create the resource key for the diagnostic.
206 */
207 DiagnosticType(String key) {
208 this.key = key;
209 }
210 };
212 /**
213 * A DiagnosticPosition provides information about the positions in a file
214 * that gave rise to a diagnostic. It always defines a "preferred" position
215 * that most accurately defines the location of the diagnostic, it may also
216 * provide a related tree node that spans that location.
217 */
218 public static interface DiagnosticPosition {
219 /** Gets the tree node, if any, to which the diagnostic applies. */
220 JCTree getTree();
221 /** If there is a tree node, get the start position of the tree node.
222 * Otherwise, just returns the same as getPreferredPosition(). */
223 int getStartPosition();
224 /** Get the position within the file that most accurately defines the
225 * location for the diagnostic. */
226 int getPreferredPosition();
227 /** If there is a tree node, and if endPositions are available, get
228 * the end position of the tree node. Otherwise, just returns the
229 * same as getPreferredPosition(). */
230 int getEndPosition(Map<JCTree, Integer> endPosTable);
231 }
233 /**
234 * A DiagnosticPosition that simply identifies a position, but no related
235 * tree node, as the location for a diagnostic. Used for scanner and parser
236 * diagnostics. */
237 public static class SimpleDiagnosticPosition implements DiagnosticPosition {
238 public SimpleDiagnosticPosition(int pos) {
239 this.pos = pos;
240 }
242 public JCTree getTree() {
243 return null;
244 }
246 public int getStartPosition() {
247 return pos;
248 }
250 public int getPreferredPosition() {
251 return pos;
252 }
254 public int getEndPosition(Map<JCTree, Integer> endPosTable) {
255 return pos;
256 }
258 private final int pos;
259 }
261 private final Messages messages;
262 private final DiagnosticType type;
263 private final DiagnosticSource source;
264 private final DiagnosticPosition position;
265 private final int line;
266 private final int column;
267 private final String key;
268 private final Object[] args;
269 private boolean mandatory;
271 /**
272 * Create a diagnostic object.
273 * @param messages the resource for localized messages
274 * @param dt the type of diagnostic
275 * @param name the name of the source file, or null if none.
276 * @param pos the character offset within the source file, if given.
277 * @param key a resource key to identify the text of the diagnostic
278 * @param args arguments to be included in the text of the diagnostic
279 */
280 protected JCDiagnostic(Messages messages,
281 DiagnosticType dt,
282 boolean mandatory,
283 DiagnosticSource source,
284 DiagnosticPosition pos,
285 String key,
286 Object ... args) {
287 if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
288 throw new IllegalArgumentException();
290 this.messages = messages;
291 this.type = dt;
292 this.mandatory = mandatory;
293 this.source = source;
294 this.position = pos;
295 this.key = key;
296 this.args = args;
298 int n = (pos == null ? Position.NOPOS : pos.getPreferredPosition());
299 if (n == Position.NOPOS || source == null)
300 line = column = -1;
301 else {
302 line = source.getLineNumber(n);
303 column = source.getColumnNumber(n);
304 }
305 }
307 /**
308 * Get the type of this diagnostic.
309 * @return the type of this diagnostic
310 */
311 public DiagnosticType getType() {
312 return type;
313 }
315 /**
316 * Check whether or not this diagnostic is required to be shown.
317 * @return true if this diagnostic is required to be shown.
318 */
319 public boolean isMandatory() {
320 return mandatory;
321 }
323 /**
324 * Get the name of the source file referred to by this diagnostic.
325 * @return the name of the source referred to with this diagnostic, or null if none
326 */
327 public JavaFileObject getSource() {
328 if (source == null)
329 return null;
330 else
331 return source.getFile();
332 }
334 /**
335 * Get the name of the source file referred to by this diagnostic.
336 * @return the name of the source referred to with this diagnostic, or null if none
337 */
338 public String getSourceName() {
339 JavaFileObject s = getSource();
340 return s == null ? null : JavacFileManager.getJavacFileName(s);
341 }
343 /**
344 * Get the source referred to by this diagnostic.
345 * @return the source referred to with this diagnostic, or null if none
346 */
347 public DiagnosticSource getDiagnosticSource() {
348 return source;
349 }
351 protected int getIntStartPosition() {
352 return (position == null ? Position.NOPOS : position.getStartPosition());
353 }
355 protected int getIntPosition() {
356 return (position == null ? Position.NOPOS : position.getPreferredPosition());
357 }
359 protected int getIntEndPosition() {
360 return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
361 }
363 public long getStartPosition() {
364 return getIntStartPosition();
365 }
367 public long getPosition() {
368 return getIntPosition();
369 }
371 public long getEndPosition() {
372 return getIntEndPosition();
373 }
375 /**
376 * Get the line number within the source referred to by this diagnostic.
377 * @return the line number within the source referred to by this diagnostic
378 */
379 public long getLineNumber() {
380 return line;
381 }
383 /**
384 * Get the column number within the line of source referred to by this diagnostic.
385 * @return the column number within the line of source referred to by this diagnostic
386 */
387 public long getColumnNumber() {
388 return column;
389 }
391 /**
392 * Get the arguments to be included in the text of the diagnostic.
393 * @return the arguments to be included in the text of the diagnostic
394 */
395 public Object[] getArgs() {
396 return args;
397 }
399 /**
400 * Get the prefix string associated with this type of diagnostic.
401 * @return the prefix string associated with this type of diagnostic
402 */
403 public String getPrefix() {
404 return getPrefix(type);
405 }
407 /**
408 * Get the prefix string associated with a particular type of diagnostic.
409 * @return the prefix string associated with a particular type of diagnostic
410 */
411 public String getPrefix(DiagnosticType dt) {
412 switch (dt) {
413 case FRAGMENT: return "";
414 case NOTE: return getLocalizedString("compiler.note.note");
415 case WARNING: return getLocalizedString("compiler.warn.warning");
416 case ERROR: return getLocalizedString("compiler.err.error");
417 default:
418 throw new AssertionError("Unknown diagnostic type: " + dt);
419 }
420 }
422 /**
423 * Return the standard presentation of this diagnostic.
424 */
425 public String toString() {
426 if (defaultFormatter == null) {
427 defaultFormatter =
428 new DiagnosticFormatter();
429 }
430 return defaultFormatter.format(this);
431 }
433 private static DiagnosticFormatter defaultFormatter;
435 private static final String messageBundleName =
436 "com.sun.tools.javac.resources.compiler";
438 private String getLocalizedString(String key, Object... args) {
439 String[] strings = new String[args.length];
440 for (int i = 0; i < strings.length; i++) {
441 Object arg = args[i];
442 if (arg == null)
443 strings[i] = null;
444 else if (arg instanceof JCDiagnostic)
445 strings[i] = ((JCDiagnostic) arg).getMessage(null);
446 else
447 strings[i] = arg.toString();
448 }
450 return messages.getLocalizedString(key, (Object[]) strings);
451 }
453 // Methods for javax.tools.Diagnostic
455 public Diagnostic.Kind getKind() {
456 switch (type) {
457 case NOTE:
458 return Diagnostic.Kind.NOTE;
459 case WARNING:
460 return mandatory ? Diagnostic.Kind.MANDATORY_WARNING
461 : Diagnostic.Kind.WARNING;
462 case ERROR:
463 return Diagnostic.Kind.ERROR;
464 default:
465 return Diagnostic.Kind.OTHER;
466 }
467 }
469 public String getCode() {
470 return key;
471 }
473 public String getMessage(Locale locale) {
474 // RFE 6406133: JCDiagnostic.getMessage ignores locale argument
475 return getLocalizedString(key, args);
476 }
478 }