1 /* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.tool; 27 28 29 import java.io.PrintWriter; 30 import java.util.Locale; 31 import java.util.ResourceBundle; 32 33 import javax.lang.model.element.Element; 34 import javax.tools.Diagnostic.Kind; 35 36 import jdk.javadoc.doclet.Reporter; 37 import com.sun.source.tree.CompilationUnitTree; 38 import com.sun.source.util.DocSourcePositions; 39 import com.sun.source.util.DocTreePath; 40 import com.sun.source.util.TreePath; 41 import com.sun.tools.javac.api.JavacTrees; 42 import com.sun.tools.javac.tree.JCTree; 43 import com.sun.tools.javac.util.Context; 44 import com.sun.tools.javac.util.JCDiagnostic; 45 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; 46 import com.sun.tools.javac.util.JavacMessages; 47 import com.sun.tools.javac.util.Log; 48 49 /** 50 * Utility for integrating with javadoc tools and for localization. 51 * Handle Resources. Access to error and warning counts. 52 * Message formatting. 53 * <br> 54 * Also provides implementation for DocErrorReporter. 55 * 56 * <p><b>This is NOT part of any supported API. 57 * If you write code that depends on this, you do so at your own risk. 58 * This code and its internal interfaces are subject to change or 59 * deletion without notice.</b> 60 * 61 * @see java.util.ResourceBundle 62 * @see java.text.MessageFormat 63 * @author Neal Gafter (rewrite) 64 */ 65 public class Messager extends Log implements Reporter { 66 final Context context; 67 68 /** Get the current messager, which is also the compiler log. */ 69 public static Messager instance0(Context context) { 70 Log instance = context.get(logKey); 71 if (instance == null || !(instance instanceof Messager)) 72 throw new InternalError("no messager instance!"); 73 return (Messager)instance; 74 } 75 76 public static void preRegister(Context context, 77 final String programName) { 78 context.put(logKey, new Context.Factory<Log>() { 79 public Log make(Context c) { 80 return new Messager(c, programName); 81 } 82 }); 83 } 84 85 public static void preRegister(Context context, final String programName, 86 final PrintWriter outWriter, final PrintWriter errWriter) { 87 context.put(logKey, new Context.Factory<Log>() { 88 public Log make(Context c) { 89 return new Messager(c, programName, outWriter, errWriter); 90 } 91 }); 92 } 93 94 @Override 95 public void print(Kind kind, String msg) { 96 switch (kind) { 97 case ERROR: 98 printError(msg); 99 return; 100 case WARNING: 101 case MANDATORY_WARNING: 102 printWarning(msg); 103 return; 104 default: 105 printNotice(msg); 106 return; 107 } 108 } 109 110 @Override 111 public void print(Kind kind, DocTreePath path, String msg) { 112 switch (kind) { 113 case ERROR: 114 printError(path, msg); 115 return; 116 case WARNING: 117 case MANDATORY_WARNING: 118 printWarning(path, msg); 119 return; 120 default: 121 printWarning(path, msg); 122 return; 123 } 124 } 125 126 @Override 127 public void print(Kind kind, Element e, String msg) { 128 switch (kind) { 129 case ERROR: 130 printError(e, msg); 131 return; 132 case WARNING: 133 case MANDATORY_WARNING: 134 printWarning(e, msg); 135 return; 136 default: 137 printWarning(e, msg); 138 return; 139 } 140 } 141 142 public static class ExitJavadoc extends Error { 143 private static final long serialVersionUID = 0; 144 } 145 146 final String programName; 147 148 private Locale locale; 149 private final JavacMessages messages; 150 private final JCDiagnostic.Factory javadocDiags; 151 152 /** The default writer for diagnostics 153 */ 154 static final PrintWriter defaultOutWriter = new PrintWriter(System.out); 155 static final PrintWriter defaultErrWriter = new PrintWriter(System.err); 156 157 /** 158 * Constructor 159 * @param programName Name of the program (for error messages). 160 */ 161 public Messager(Context context, String programName) { 162 this(context, programName, defaultOutWriter, defaultErrWriter); 163 } 164 165 /** 166 * Constructor 167 * @param programName Name of the program (for error messages). 168 * @param outWriter Stream for notices etc. 169 * @param errWriter Stream for errors and warnings 170 */ 171 @SuppressWarnings("deprecation") 172 public Messager(Context context, String programName, PrintWriter outWriter, PrintWriter errWriter) { 173 super(context, errWriter, errWriter, outWriter); 174 messages = JavacMessages.instance(context); 175 messages.add(locale -> ResourceBundle.getBundle("jdk.javadoc.internal.tool.resources.javadoc", 176 locale)); 177 javadocDiags = new JCDiagnostic.Factory(messages, "javadoc"); 178 this.programName = programName; 179 this.context = context; 180 locale = Locale.getDefault(); 181 } 182 183 public void setLocale(Locale locale) { 184 this.locale = locale; 185 } 186 187 /** 188 * get and format message string from resource 189 * 190 * @param key selects message from resource 191 * @param args arguments for the message 192 */ 193 String getText(String key, Object... args) { 194 return messages.getLocalizedString(locale, key, args); 195 } 196 197 private String getDiagSource(DocTreePath path) { 198 if (path == null) { 199 return programName; 200 } 201 JavacTrees trees = JavacTrees.instance(context); 202 DocSourcePositions sourcePositions = trees.getSourcePositions(); 203 CompilationUnitTree cu = path.getTreePath().getCompilationUnit(); 204 long spos = sourcePositions.getStartPosition(cu, path.getDocComment(), path.getLeaf()); 205 long lineNumber = cu.getLineMap().getLineNumber(spos); 206 String fname = cu.getSourceFile().getName(); 207 String posString = fname + ":" + lineNumber; 208 return posString; 209 } 210 211 private String getDiagSource(Element e) { 212 if (e == null) { 213 return programName; 214 } 215 JavacTrees trees = JavacTrees.instance(context); 216 TreePath path = trees.getPath(e); 217 DocSourcePositions sourcePositions = trees.getSourcePositions(); 218 JCTree tree = trees.getTree(e); 219 CompilationUnitTree cu = path.getCompilationUnit(); 220 long spos = sourcePositions.getStartPosition(cu, tree); 221 long lineNumber = cu.getLineMap().getLineNumber(spos); 222 String fname = cu.getSourceFile().getName(); 223 String posString = fname + ":" + lineNumber; 224 return posString; 225 } 226 227 /** 228 * Print error message, increment error count. 229 * Part of DocErrorReporter. 230 * 231 * @param msg message to print 232 */ 233 public void printError(String msg) { 234 printError((DocTreePath)null, msg); 235 } 236 237 public void printError(DocTreePath path, String msg) { 238 String prefix = getDiagSource(path); 239 if (diagListener != null) { 240 report(DiagnosticType.ERROR, prefix, msg); 241 return; 242 } 243 incrementErrorCount(prefix, msg); 244 } 245 246 public void printError(Element e, String msg) { 247 String prefix = getDiagSource(e); 248 if (diagListener != null) { 249 report(DiagnosticType.ERROR, prefix, msg); 250 return; 251 } 252 incrementErrorCount(prefix, msg); 253 } 254 255 private void incrementErrorCount(String prefix, String msg) { 256 if (nerrors < MaxErrors) { 257 PrintWriter errWriter = getWriter(WriterKind.ERROR); 258 printRawLines(errWriter, prefix + ": " + getText("javadoc.error") + " - " + msg); 259 errWriter.flush(); 260 prompt(); 261 nerrors++; 262 } 263 } 264 265 /** 266 * Print warning message, increment warning count. 267 * Part of DocErrorReporter. 268 * 269 * @param msg message to print 270 */ 271 public void printWarning(String msg) { 272 printWarning((DocTreePath)null, msg); 273 } 274 275 public void printWarning(DocTreePath path, String msg) { 276 String prefix = getDiagSource(path); 277 if (diagListener != null) { 278 report(DiagnosticType.WARNING, prefix, msg); 279 return; 280 } 281 incrementWarningCount(prefix, msg); 282 } 283 284 public void printWarning(Element e, String msg) { 285 String prefix = getDiagSource(e); 286 if (diagListener != null) { 287 report(DiagnosticType.WARNING, prefix, msg); 288 return; 289 } 290 incrementWarningCount(prefix, msg); 291 } 292 293 private void incrementWarningCount(String prefix, String msg) { 294 if (nwarnings < MaxWarnings) { 295 PrintWriter warnWriter = getWriter(WriterKind.WARNING); 296 printRawLines(warnWriter, prefix + ": " + getText("javadoc.warning") + " - " + msg); 297 warnWriter.flush(); 298 nwarnings++; 299 } 300 } 301 302 /** 303 * Print a message. 304 * Part of DocErrorReporter. 305 * 306 * @param msg message to print 307 */ 308 public void printNotice(String msg) { 309 printNotice((DocTreePath)null, msg); 310 } 311 312 public void printNotice(DocTreePath path, String msg) { 313 String prefix = getDiagSource(path); 314 if (diagListener != null) { 315 report(DiagnosticType.NOTE, null, prefix + ": " + msg); 316 return; 317 } 318 319 PrintWriter noticeWriter = getWriter(WriterKind.NOTICE); 320 if (path == null) { 321 printRawLines(noticeWriter, msg); 322 } else { 323 printRawLines(noticeWriter, prefix + ": " + msg); 324 } 325 noticeWriter.flush(); 326 } 327 328 public void printNotice(Element e, String msg) { 329 String pos = getDiagSource(e); 330 if (diagListener != null) { 331 report(DiagnosticType.NOTE, pos, msg); 332 return; 333 } 334 335 PrintWriter noticeWriter = getWriter(WriterKind.NOTICE); 336 if (e == null) { 337 printRawLines(noticeWriter, msg); 338 } else { 339 printRawLines(noticeWriter, pos + ": " + msg); 340 } 341 noticeWriter.flush(); 342 } 343 344 /** 345 * Print error message, increment error count. 346 * 347 * @param key selects message from resource 348 */ 349 public void error(Element e, String key, Object... args) { 350 printError(e, getText(key, args)); 351 } 352 353 /** 354 * Print error message, increment error count. 355 * 356 * @param key selects message from resource 357 */ 358 public void error(DocTreePath path, String key, Object... args) { 359 printError(path, getText(key, args)); 360 } 361 362 public void error(String key, Object... args) { 363 printError((Element)null, getText(key, args)); 364 } 365 366 public void warning(String key, Object... args) { 367 printWarning((Element)null, getText(key, args)); 368 } 369 370 /** 371 * Print warning message, increment warning count. 372 * 373 * @param key selects message from resource 374 */ 375 public void warning(Element e, String key, Object... args) { 376 printWarning(e, getText(key, args)); 377 } 378 379 /** 380 * Print warning message, increment warning count. 381 * 382 * @param key selects message from resource 383 */ 384 public void warning(DocTreePath path, String key, Object... args) { 385 printWarning(path, getText(key, args)); 386 } 387 388 /** 389 * Print a message. 390 * 391 * @param key selects message from resource 392 */ 393 public void notice(String key, Object... args) { 394 printNotice(getText(key, args)); 395 } 396 397 /** 398 * Return total number of errors, including those recorded 399 * in the compilation log. 400 */ 401 public int nerrors() { return nerrors; } 402 403 /** 404 * Return total number of warnings, including those recorded 405 * in the compilation log. 406 */ 407 public int nwarnings() { return nwarnings; } 408 409 /** 410 * Print exit message. 411 */ 412 public void exitNotice() { 413 if (nerrors > 0) { 414 notice((nerrors > 1) ? "main.errors" : "main.error", 415 "" + nerrors); 416 } 417 if (nwarnings > 0) { 418 notice((nwarnings > 1) ? "main.warnings" : "main.warning", 419 "" + nwarnings); 420 } 421 } 422 423 private void report(DiagnosticType type, String pos, String msg) { 424 switch (type) { 425 case ERROR: 426 case WARNING: 427 Object prefix = (pos == null) ? programName : pos; 428 report(javadocDiags.create(type, null, null, "msg", prefix, msg)); 429 break; 430 431 case NOTE: 432 String key = (pos == null) ? "msg" : "pos.msg"; 433 report(javadocDiags.create(type, null, null, key, pos, msg)); 434 break; 435 436 default: 437 throw new IllegalArgumentException(type.toString()); 438 } 439 } 440 }