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