| 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 | } |