001: /*
002: * Copyright 2003-2006 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.net.URI;
029: import java.text.MessageFormat;
030: import java.util.Locale;
031: import java.util.Map;
032: import java.util.MissingResourceException;
033: import java.util.ResourceBundle;
034:
035: import javax.tools.Diagnostic;
036: import javax.tools.FileObject;
037: import javax.tools.JavaFileObject;
038:
039: import com.sun.tools.javac.tree.JCTree;
040:
041: import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
042:
043: /** An abstraction of a diagnostic message generated by the compiler.
044: *
045: * <p><b>This is NOT part of any API supported by Sun Microsystems. If
046: * you write code that depends on this, you do so at your own risk.
047: * This code and its internal interfaces are subject to change or
048: * deletion without notice.</b>
049: */
050: @Version("@(#)JCDiagnostic.java 1.27 07/05/05")
051: public class JCDiagnostic implements Diagnostic<JavaFileObject> {
052: /** A factory for creating diagnostic objects. */
053: public static class Factory {
054: /** The context key for the diagnostic factory. */
055: protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey = new Context.Key<JCDiagnostic.Factory>();
056:
057: /** Get the Factory instance for this context. */
058: public static Factory instance(Context context) {
059: Factory instance = context.get(diagnosticFactoryKey);
060: if (instance == null)
061: instance = new Factory(context);
062: return instance;
063: }
064:
065: final Messages messages;
066: final String prefix;
067:
068: /** Create a new diagnostic factory. */
069: protected Factory(Context context) {
070: context.put(diagnosticFactoryKey, this );
071: messages = Messages.instance(context);
072: prefix = "compiler";
073: }
074:
075: /** Create a new diagnostic factory. */
076: public Factory(Messages messages, String prefix) {
077: this .messages = messages;
078: this .prefix = prefix;
079: }
080:
081: /**
082: * Create an error diagnostic.
083: * @param source The source of the compilation unit, if any, in which to report the error.
084: * @param pos The source position at which to report the error.
085: * @param key The key for the localized error message.
086: * @param args Fields of the error message.
087: */
088: public JCDiagnostic error(DiagnosticSource source,
089: DiagnosticPosition pos, String key, Object... args) {
090: return new JCDiagnostic(messages, ERROR, true, source, pos,
091: qualify(ERROR, key), args);
092: }
093:
094: /**
095: * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
096: * @param source The source of the compilation unit, if any, in which to report the warning.
097: * @param pos The source position at which to report the warning.
098: * @param key The key for the localized error message.
099: * @param args Fields of the error message.
100: * @see MandatoryWarningHandler
101: */
102: public JCDiagnostic mandatoryWarning(DiagnosticSource source,
103: DiagnosticPosition pos, String key, Object... args) {
104: return new JCDiagnostic(messages, WARNING, true, source,
105: pos, qualify(WARNING, key), args);
106: }
107:
108: /**
109: * Create a warning diagnostic.
110: * @param source The source of the compilation unit, if any, in which to report the warning.
111: * @param pos The source position at which to report the warning.
112: * @param key The key for the localized error message.
113: * @param args Fields of the error message.
114: */
115: public JCDiagnostic warning(DiagnosticSource source,
116: DiagnosticPosition pos, String key, Object... args) {
117: return new JCDiagnostic(messages, WARNING, false, source,
118: pos, qualify(WARNING, key), args);
119: }
120:
121: /**
122: * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
123: * @param key The key for the localized error message.
124: * @param args Fields of the error message.
125: * @see MandatoryWarningHandler
126: */
127: public JCDiagnostic mandatoryNote(DiagnosticSource source,
128: String key, Object... args) {
129: return new JCDiagnostic(messages, NOTE, true, source, null,
130: qualify(NOTE, key), args);
131: }
132:
133: /**
134: * Create a note diagnostic.
135: * @param key The key for the localized error message.
136: * @param args Fields of the error message.
137: */
138: public JCDiagnostic note(String key, Object... args) {
139: return note(null, null, key, args);
140: }
141:
142: /**
143: * Create a note diagnostic.
144: * @param source The source of the compilation unit, if any, in which to report the note.
145: * @param pos The source position at which to report the note.
146: * @param key The key for the localized error message.
147: * @param args Fields of the error message.
148: */
149: public JCDiagnostic note(DiagnosticSource source,
150: DiagnosticPosition pos, String key, Object... args) {
151: return new JCDiagnostic(messages, NOTE, false, source, pos,
152: qualify(NOTE, key), args);
153: }
154:
155: /**
156: * Create a fragment diagnostic, for use as an argument in other diagnostics
157: * @param key The key for the localized error message.
158: * @param args Fields of the error message.
159: */
160: public JCDiagnostic fragment(String key, Object... args) {
161: return new JCDiagnostic(messages, FRAGMENT, false, null,
162: null, qualify(FRAGMENT, key), args);
163: }
164:
165: protected String qualify(DiagnosticType t, String key) {
166: return prefix + "." + t.key + "." + key;
167: }
168: }
169:
170: /**
171: * Create a fragment diagnostic, for use as an argument in other diagnostics
172: * @param key The key for the localized error message.
173: * @param args Fields of the error message.
174: */
175: // should be deprecated
176: public static JCDiagnostic fragment(String key, Object... args) {
177: return new JCDiagnostic(Messages.getDefaultMessages(),
178: FRAGMENT, false, null, null, "compiler." + FRAGMENT.key
179: + "." + key, args);
180: }
181:
182: /**
183: * A simple abstraction of a source file, as needed for use in a diagnostic message.
184: */
185: // Note: This class may be superceded by a more general abstraction
186: public interface DiagnosticSource {
187: JavaFileObject getFile();
188:
189: CharSequence getName();
190:
191: int getLineNumber(int pos);
192:
193: int getColumnNumber(int pos);
194:
195: Map<JCTree, Integer> getEndPosTable();
196: };
197:
198: /**
199: * A DiagnosticType defines the type of the diagnostic.
200: **/
201: public enum DiagnosticType {
202: /** A fragment of an enclosing diagnostic. */
203: FRAGMENT("misc"),
204: /** A note: similar to, but less serious than, a warning. */
205: NOTE("note"),
206: /** A warning. */
207: WARNING("warn"),
208: /** An error. */
209: ERROR("err");
210:
211: final String key;
212:
213: /** Create a DiagnosticType.
214: * @param key A string used to create the resource key for the diagnostic.
215: */
216: DiagnosticType(String key) {
217: this .key = key;
218: }
219: };
220:
221: /**
222: * A DiagnosticPosition provides information about the positions in a file
223: * that gave rise to a diagnostic. It always defines a "preferred" position
224: * that most accurately defines the location of the diagnostic, it may also
225: * provide a related tree node that spans that location.
226: */
227: public static interface DiagnosticPosition {
228: /** Gets the tree node, if any, to which the diagnostic applies. */
229: JCTree getTree();
230:
231: /** If there is a tree node, get the start position of the tree node.
232: * Otherwise, just returns the same as getPreferredPosition(). */
233: int getStartPosition();
234:
235: /** Get the position within the file that most accurately defines the
236: * location for the diagnostic. */
237: int getPreferredPosition();
238:
239: /** If there is a tree node, and if endPositions are available, get
240: * the end position of the tree node. Otherwise, just returns the
241: * same as getPreferredPosition(). */
242: int getEndPosition(Map<JCTree, Integer> endPosTable);
243: }
244:
245: /**
246: * A DiagnosticPosition that simply identifies a position, but no related
247: * tree node, as the location for a diagnostic. Used for scanner and parser
248: * diagnostics. */
249: public static class SimpleDiagnosticPosition implements
250: DiagnosticPosition {
251: public SimpleDiagnosticPosition(int pos) {
252: this .pos = pos;
253: }
254:
255: public JCTree getTree() {
256: return null;
257: }
258:
259: public int getStartPosition() {
260: return pos;
261: }
262:
263: public int getPreferredPosition() {
264: return pos;
265: }
266:
267: public int getEndPosition(Map<JCTree, Integer> endPosTable) {
268: return pos;
269: }
270:
271: private final int pos;
272: }
273:
274: private final Messages messages;
275: private final DiagnosticType type;
276: private final DiagnosticSource source;
277: private final DiagnosticPosition position;
278: private final int line;
279: private final int column;
280: private final String key;
281: private final Object[] args;
282: private boolean mandatory;
283:
284: /**
285: * Create a diagnostic object.
286: * @param messages the resource for localized messages
287: * @param dt the type of diagnostic
288: * @param name the name of the source file, or null if none.
289: * @param pos the character offset within the source file, if given.
290: * @param key a resource key to identify the text of the diagnostic
291: * @param args arguments to be included in the text of the diagnostic
292: */
293: protected JCDiagnostic(Messages messages, DiagnosticType dt,
294: boolean mandatory, DiagnosticSource source,
295: DiagnosticPosition pos, String key, Object... args) {
296: if (source == null && pos != null
297: && pos.getPreferredPosition() != Position.NOPOS)
298: throw new IllegalArgumentException();
299:
300: this .messages = messages;
301: this .type = dt;
302: this .mandatory = mandatory;
303: this .source = source;
304: this .position = pos;
305: this .key = key;
306: this .args = args;
307:
308: int n = (pos == null ? Position.NOPOS : pos
309: .getPreferredPosition());
310: if (n == Position.NOPOS || source == null)
311: line = column = -1;
312: else {
313: line = source.getLineNumber(n);
314: column = source.getColumnNumber(n);
315: }
316: }
317:
318: /**
319: * Get the type of this diagnostic.
320: * @return the type of this diagnostic
321: */
322: public DiagnosticType getType() {
323: return type;
324: }
325:
326: /**
327: * Check whether or not this diagnostic is required to be shown.
328: * @return true if this diagnostic is required to be shown.
329: */
330: public boolean isMandatory() {
331: return mandatory;
332: }
333:
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 JavaFileObject getSource() {
339: if (source == null)
340: return null;
341: else
342: return source.getFile();
343: }
344:
345: /**
346: * Get the name of the source file referred to by this diagnostic.
347: * @return the name of the source referred to with this diagnostic, or null if none
348: */
349: public String getSourceName() {
350: JavaFileObject s = getSource();
351: return s == null ? null : JavacFileManager.getJavacFileName(s);
352: }
353:
354: /**
355: * Get the source referred to by this diagnostic.
356: * @return the source referred to with this diagnostic, or null if none
357: */
358: public DiagnosticSource getDiagnosticSource() {
359: return source;
360: }
361:
362: protected int getIntStartPosition() {
363: return (position == null ? Position.NOPOS : position
364: .getStartPosition());
365: }
366:
367: protected int getIntPosition() {
368: return (position == null ? Position.NOPOS : position
369: .getPreferredPosition());
370: }
371:
372: protected int getIntEndPosition() {
373: return (position == null ? Position.NOPOS : position
374: .getEndPosition(source.getEndPosTable()));
375: }
376:
377: public long getStartPosition() {
378: return getIntStartPosition();
379: }
380:
381: public long getPosition() {
382: return getIntPosition();
383: }
384:
385: public long getEndPosition() {
386: return getIntEndPosition();
387: }
388:
389: /**
390: * Get the line number within the source referred to by this diagnostic.
391: * @return the line number within the source referred to by this diagnostic
392: */
393: public long getLineNumber() {
394: return line;
395: }
396:
397: /**
398: * Get the column number within the line of source referred to by this diagnostic.
399: * @return the column number within the line of source referred to by this diagnostic
400: */
401: public long getColumnNumber() {
402: return column;
403: }
404:
405: /**
406: * Get the arguments to be included in the text of the diagnostic.
407: * @return the arguments to be included in the text of the diagnostic
408: */
409: public Object[] getArgs() {
410: return args;
411: }
412:
413: /**
414: * Get the prefix string associated with this type of diagnostic.
415: * @return the prefix string associated with this type of diagnostic
416: */
417: public String getPrefix() {
418: return getPrefix(type);
419: }
420:
421: /**
422: * Get the prefix string associated with a particular type of diagnostic.
423: * @return the prefix string associated with a particular type of diagnostic
424: */
425: public String getPrefix(DiagnosticType dt) {
426: switch (dt) {
427: case FRAGMENT:
428: return "";
429: case NOTE:
430: return getLocalizedString("compiler.note.note");
431: case WARNING:
432: return getLocalizedString("compiler.warn.warning");
433: case ERROR:
434: return getLocalizedString("compiler.err.error");
435: default:
436: throw new AssertionError("Unknown diagnostic type: " + dt);
437: }
438: }
439:
440: /**
441: * Return the standard presentation of this diagnostic.
442: */
443: public String toString() {
444: if (defaultFormatter == null) {
445: defaultFormatter = new DiagnosticFormatter();
446: }
447: return defaultFormatter.format(this );
448: }
449:
450: private static DiagnosticFormatter defaultFormatter;
451:
452: private static final String messageBundleName = "com.sun.tools.javac.resources.compiler";
453:
454: private String getLocalizedString(String key, Object... args) {
455: String[] strings = new String[args.length];
456: for (int i = 0; i < strings.length; i++) {
457: Object arg = args[i];
458: if (arg == null)
459: strings[i] = null;
460: else if (arg instanceof JCDiagnostic)
461: strings[i] = ((JCDiagnostic) arg).getMessage(null);
462: else
463: strings[i] = arg.toString();
464: }
465:
466: return messages.getLocalizedString(key, (Object[]) strings);
467: }
468:
469: // Methods for javax.tools.Diagnostic
470:
471: public Diagnostic.Kind getKind() {
472: switch (type) {
473: case NOTE:
474: return Diagnostic.Kind.NOTE;
475: case WARNING:
476: return mandatory ? Diagnostic.Kind.MANDATORY_WARNING
477: : Diagnostic.Kind.WARNING;
478: case ERROR:
479: return Diagnostic.Kind.ERROR;
480: default:
481: return Diagnostic.Kind.OTHER;
482: }
483: }
484:
485: public String getCode() {
486: return key;
487: }
488:
489: public String getMessage(Locale locale) {
490: // RFE 6406133: JCDiagnostic.getMessage ignores locale argument
491: return getLocalizedString(key, args);
492: }
493:
494: }
|