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