1 /* 2 * Copyright (c) 2012, 2018, 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 com.sun.tools.doclint; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.PrintWriter; 31 import java.util.ArrayList; 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.util.Queue; 35 36 import javax.lang.model.element.Name; 37 import javax.tools.StandardLocation; 38 39 import com.sun.source.doctree.DocCommentTree; 40 import com.sun.source.tree.BlockTree; 41 import com.sun.source.tree.ClassTree; 42 import com.sun.source.tree.CompilationUnitTree; 43 import com.sun.source.tree.LambdaExpressionTree; 44 import com.sun.source.tree.ModuleTree; 45 import com.sun.source.tree.PackageTree; 46 import com.sun.source.tree.MethodTree; 47 import com.sun.source.tree.Tree; 48 import com.sun.source.tree.VariableTree; 49 import com.sun.source.util.JavacTask; 50 import com.sun.source.util.Plugin; 51 import com.sun.source.util.TaskEvent; 52 import com.sun.source.util.TaskListener; 53 import com.sun.source.util.TreePath; 54 import com.sun.source.util.TreePathScanner; 55 import com.sun.tools.javac.api.JavacTaskImpl; 56 import com.sun.tools.javac.api.JavacTool; 57 import com.sun.tools.javac.file.JavacFileManager; 58 import com.sun.tools.javac.main.JavaCompiler; 59 import com.sun.tools.javac.util.Context; 60 import com.sun.tools.javac.util.DefinedBy; 61 import com.sun.tools.javac.util.DefinedBy.Api; 62 63 /** 64 * Multi-function entry point for the doc check utility. 65 * 66 * This class can be invoked in the following ways: 67 * <ul> 68 * <li>From the command line 69 * <li>From javac, as a plugin 70 * <li>Directly, via a simple API 71 * </ul> 72 * 73 * <p><b>This is NOT part of any supported API. 74 * If you write code that depends on this, you do so at your own 75 * risk. This code and its internal interfaces are subject to change 76 * or deletion without notice.</b></p> 77 */ 78 public class DocLint implements Plugin { 79 80 public static final String XMSGS_OPTION = "-Xmsgs"; 81 public static final String XMSGS_CUSTOM_PREFIX = "-Xmsgs:"; 82 private static final String STATS = "-stats"; 83 public static final String XIMPLICIT_HEADERS = "-XimplicitHeaders:"; 84 public static final String XCUSTOM_TAGS_PREFIX = "-XcustomTags:"; 85 public static final String XHTML_VERSION_PREFIX = "-XhtmlVersion:"; 86 public static final String XCHECK_PACKAGE = "-XcheckPackage:"; 87 public static final String SEPARATOR = ","; 88 89 // <editor-fold defaultstate="collapsed" desc="Command-line entry point"> 90 public static void main(String... args) { 91 DocLint dl = new DocLint(); 92 try { 93 dl.run(args); 94 } catch (BadArgs e) { 95 System.err.println(e.getMessage()); 96 System.exit(1); 97 } catch (IOException e) { 98 System.err.println(dl.localize("dc.main.ioerror", e.getLocalizedMessage())); 99 System.exit(2); 100 } 101 } 102 103 // </editor-fold> 104 105 // <editor-fold defaultstate="collapsed" desc="Simple API"> 106 107 public class BadArgs extends Exception { 108 private static final long serialVersionUID = 0; 109 BadArgs(String code, Object... args) { 110 super(localize(code, args)); 111 this.code = code; 112 this.args = args; 113 } 114 115 final String code; 116 final Object[] args; 117 } 118 119 /** 120 * Simple API entry point. 121 * @param args Options and operands for doclint 122 * @throws BadArgs if an error is detected in any args 123 * @throws IOException if there are problems with any of the file arguments 124 */ 125 public void run(String... args) throws BadArgs, IOException { 126 PrintWriter out = new PrintWriter(System.out); 127 try { 128 run(out, args); 129 } finally { 130 out.flush(); 131 } 132 } 133 134 public void run(PrintWriter out, String... args) throws BadArgs, IOException { 135 env = new Env(); 136 processArgs(args); 137 138 boolean noFiles = javacFiles.isEmpty(); 139 if (needHelp) { 140 showHelp(out); 141 if (noFiles) 142 return; 143 } else if (noFiles) { 144 out.println(localize("dc.main.no.files.given")); 145 return; 146 } 147 148 JavacTool tool = JavacTool.create(); 149 150 JavacFileManager fm = new JavacFileManager(new Context(), false, null); 151 fm.setSymbolFileEnabled(false); 152 if (javacBootClassPath != null) { 153 fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, javacBootClassPath); 154 } 155 if (javacClassPath != null) { 156 fm.setLocation(StandardLocation.CLASS_PATH, javacClassPath); 157 } 158 if (javacSourcePath != null) { 159 fm.setLocation(StandardLocation.SOURCE_PATH, javacSourcePath); 160 } 161 162 JavacTask task = tool.getTask(out, fm, null, javacOpts, null, 163 fm.getJavaFileObjectsFromFiles(javacFiles)); 164 Iterable<? extends CompilationUnitTree> units = task.parse(); 165 ((JavacTaskImpl) task).enter(); 166 167 env.init(task); 168 checker = new Checker(env); 169 170 DeclScanner ds = new DeclScanner(env) { 171 @Override 172 void visitDecl(Tree tree, Name name) { 173 TreePath p = getCurrentPath(); 174 DocCommentTree dc = env.trees.getDocCommentTree(p); 175 176 checker.scan(dc, p); 177 } 178 }; 179 180 ds.scan(units, null); 181 182 reportStats(out); 183 184 Context ctx = ((JavacTaskImpl) task).getContext(); 185 JavaCompiler c = JavaCompiler.instance(ctx); 186 c.printCount("error", c.errorCount()); 187 c.printCount("warn", c.warningCount()); 188 } 189 190 void processArgs(String... args) throws BadArgs { 191 javacOpts = new ArrayList<>(); 192 javacFiles = new ArrayList<>(); 193 194 if (args.length == 0) 195 needHelp = true; 196 197 for (int i = 0; i < args.length; i++) { 198 String arg = args[i]; 199 if (arg.matches("-Xmax(errs|warns)") && i + 1 < args.length) { 200 if (args[++i].matches("[0-9]+")) { 201 javacOpts.add(arg); 202 javacOpts.add(args[i]); 203 } else { 204 throw new BadArgs("dc.bad.value.for.option", arg, args[i]); 205 } 206 } else if ((arg.equals("-target") || arg.equals("-source")) && i + 1 < args.length) { 207 javacOpts.add(arg); 208 javacOpts.add(args[++i]); 209 } else if (arg.equals(STATS)) { 210 env.messages.setStatsEnabled(true); 211 } else if (arg.equals("-bootclasspath") && i + 1 < args.length) { 212 javacBootClassPath = splitPath(args[++i]); 213 } else if (arg.equals("-classpath") && i + 1 < args.length) { 214 javacClassPath = splitPath(args[++i]); 215 } else if (arg.equals("-cp") && i + 1 < args.length) { 216 javacClassPath = splitPath(args[++i]); 217 } else if (arg.equals("-sourcepath") && i + 1 < args.length) { 218 javacSourcePath = splitPath(args[++i]); 219 } else if (arg.equals(XMSGS_OPTION)) { 220 env.messages.setOptions(null); 221 } else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) { 222 env.messages.setOptions(arg.substring(arg.indexOf(":") + 1)); 223 } else if (arg.startsWith(XCUSTOM_TAGS_PREFIX)) { 224 env.setCustomTags(arg.substring(arg.indexOf(":") + 1)); 225 } else if (arg.startsWith(XHTML_VERSION_PREFIX)) { 226 String argsVersion = arg.substring(arg.indexOf(":") + 1); 227 HtmlVersion htmlVersion = HtmlVersion.getHtmlVersion(argsVersion); 228 if (htmlVersion != null) { 229 env.setHtmlVersion(htmlVersion); 230 } else { 231 throw new BadArgs("dc.bad.value.for.option", arg, argsVersion); 232 } 233 } else if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help") 234 || arg.equals("-?") || arg.equals("-usage")) { 235 needHelp = true; 236 } else if (arg.startsWith("-")) { 237 throw new BadArgs("dc.bad.option", arg); 238 } else { 239 while (i < args.length) 240 javacFiles.add(new File(args[i++])); 241 } 242 } 243 } 244 245 void showHelp(PrintWriter out) { 246 String msg = localize("dc.main.usage"); 247 for (String line: msg.split("\n")) 248 out.println(line); 249 } 250 251 List<File> splitPath(String path) { 252 List<File> files = new ArrayList<>(); 253 for (String f: path.split(File.pathSeparator)) { 254 if (f.length() > 0) 255 files.add(new File(f)); 256 } 257 return files; 258 } 259 260 List<File> javacBootClassPath; 261 List<File> javacClassPath; 262 List<File> javacSourcePath; 263 List<String> javacOpts; 264 List<File> javacFiles; 265 boolean needHelp = false; 266 267 // </editor-fold> 268 269 // <editor-fold defaultstate="collapsed" desc="javac Plugin"> 270 271 @Override @DefinedBy(Api.COMPILER_TREE) 272 public String getName() { 273 return "doclint"; 274 } 275 276 @Override @DefinedBy(Api.COMPILER_TREE) 277 public void init(JavacTask task, String... args) { 278 init(task, args, true); 279 } 280 281 // </editor-fold> 282 283 // <editor-fold defaultstate="collapsed" desc="Embedding API"> 284 285 public void init(JavacTask task, String[] args, boolean addTaskListener) { 286 env = new Env(); 287 for (String arg : args) { 288 if (arg.equals(XMSGS_OPTION)) { 289 env.messages.setOptions(null); 290 } else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) { 291 env.messages.setOptions(arg.substring(arg.indexOf(":") + 1)); 292 } else if (arg.matches(XIMPLICIT_HEADERS + "[1-6]")) { 293 char ch = arg.charAt(arg.length() - 1); 294 env.setImplicitHeaders(Character.digit(ch, 10)); 295 } else if (arg.startsWith(XCUSTOM_TAGS_PREFIX)) { 296 env.setCustomTags(arg.substring(arg.indexOf(":") + 1)); 297 } else if (arg.startsWith(XHTML_VERSION_PREFIX)) { 298 String argsVersion = arg.substring(arg.indexOf(":") + 1); 299 HtmlVersion htmlVersion = HtmlVersion.getHtmlVersion(argsVersion); 300 if (htmlVersion != null) { 301 env.setHtmlVersion(htmlVersion); 302 } else { 303 throw new IllegalArgumentException(argsVersion); 304 } 305 } else if (arg.startsWith(XCHECK_PACKAGE)) { 306 env.setCheckPackages(arg.substring(arg.indexOf(":") + 1)); 307 } else 308 throw new IllegalArgumentException(arg); 309 } 310 env.init(task); 311 312 checker = new Checker(env); 313 314 if (addTaskListener) { 315 final DeclScanner ds = new DeclScanner(env) { 316 @Override 317 void visitDecl(Tree tree, Name name) { 318 TreePath p = getCurrentPath(); 319 DocCommentTree dc = env.trees.getDocCommentTree(p); 320 321 checker.scan(dc, p); 322 } 323 }; 324 325 TaskListener tl = new TaskListener() { 326 @Override @DefinedBy(Api.COMPILER_TREE) 327 public void started(TaskEvent e) { 328 switch (e.getKind()) { 329 case ANALYZE: 330 CompilationUnitTree tree; 331 while ((tree = todo.poll()) != null) 332 ds.scan(tree, null); 333 break; 334 } 335 } 336 337 @Override @DefinedBy(Api.COMPILER_TREE) 338 public void finished(TaskEvent e) { 339 switch (e.getKind()) { 340 case PARSE: 341 todo.add(e.getCompilationUnit()); 342 break; 343 } 344 } 345 346 Queue<CompilationUnitTree> todo = new LinkedList<>(); 347 }; 348 349 task.addTaskListener(tl); 350 } 351 } 352 353 public void scan(TreePath p) { 354 DocCommentTree dc = env.trees.getDocCommentTree(p); 355 checker.scan(dc, p); 356 } 357 358 public boolean shouldCheck(CompilationUnitTree unit) { 359 return env.shouldCheck(unit); 360 } 361 362 public void reportStats(PrintWriter out) { 363 env.messages.reportStats(out); 364 } 365 366 // </editor-fold> 367 368 Env env; 369 Checker checker; 370 371 public static boolean isValidOption(String opt) { 372 if (opt.equals(XMSGS_OPTION)) 373 return true; 374 if (opt.startsWith(XMSGS_CUSTOM_PREFIX)) 375 return Messages.Options.isValidOptions(opt.substring(XMSGS_CUSTOM_PREFIX.length())); 376 if (opt.startsWith(XCHECK_PACKAGE)) { 377 return Env.validatePackages(opt.substring(opt.indexOf(":") + 1)); 378 } 379 return false; 380 } 381 382 private String localize(String code, Object... args) { 383 Messages m = (env != null) ? env.messages : new Messages(null); 384 return m.localize(code, args); 385 } 386 387 // <editor-fold defaultstate="collapsed" desc="DeclScanner"> 388 389 static abstract class DeclScanner extends TreePathScanner<Void, Void> { 390 final Env env; 391 392 public DeclScanner(Env env) { 393 this.env = env; 394 } 395 396 abstract void visitDecl(Tree tree, Name name); 397 398 @Override @DefinedBy(Api.COMPILER_TREE) 399 public Void visitPackage(PackageTree tree, Void ignore) { 400 visitDecl(tree, null); 401 return super.visitPackage(tree, ignore); 402 } 403 404 @Override @DefinedBy(Api.COMPILER_TREE) 405 public Void visitClass(ClassTree tree, Void ignore) { 406 visitDecl(tree, tree.getSimpleName()); 407 return super.visitClass(tree, ignore); 408 } 409 410 @Override @DefinedBy(Api.COMPILER_TREE) 411 public Void visitMethod(MethodTree tree, Void ignore) { 412 visitDecl(tree, tree.getName()); 413 return null; 414 } 415 416 @Override @DefinedBy(Api.COMPILER_TREE) 417 public Void visitModule(ModuleTree tree, Void ignore) { 418 visitDecl(tree, null); 419 return super.visitModule(tree, ignore); 420 } 421 422 @Override @DefinedBy(Api.COMPILER_TREE) 423 public Void visitVariable(VariableTree tree, Void ignore) { 424 visitDecl(tree, tree.getName()); 425 return super.visitVariable(tree, ignore); 426 } 427 428 @Override @DefinedBy(Api.COMPILER_TREE) 429 public Void visitCompilationUnit(CompilationUnitTree node, Void p) { 430 if (!env.shouldCheck(node)) { 431 return null; 432 } 433 return super.visitCompilationUnit(node, p); 434 } 435 436 @Override @DefinedBy(Api.COMPILER_TREE) 437 public Void visitBlock(BlockTree tree, Void ignore) { 438 return null; 439 } 440 441 @Override @DefinedBy(Api.COMPILER_TREE) 442 public Void visitLambdaExpression(LambdaExpressionTree tree, Void ignore) { 443 return null; 444 } 445 446 } 447 448 // </editor-fold> 449 450 }