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