1/* 2 * Copyright (c) 2012, 2017, 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; 35 36import javax.lang.model.element.Name; 37import javax.tools.StandardLocation; 38 39import com.sun.source.doctree.DocCommentTree; 40import com.sun.source.tree.ClassTree; 41import com.sun.source.tree.CompilationUnitTree; 42import com.sun.source.tree.ModuleTree; 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 if (javacBootClassPath != null) { 151 fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, javacBootClassPath); 152 } 153 if (javacClassPath != null) { 154 fm.setLocation(StandardLocation.CLASS_PATH, javacClassPath); 155 } 156 if (javacSourcePath != null) { 157 fm.setLocation(StandardLocation.SOURCE_PATH, javacSourcePath); 158 } 159 160 JavacTask task = tool.getTask(out, fm, null, javacOpts, null, 161 fm.getJavaFileObjectsFromFiles(javacFiles)); 162 Iterable<? extends CompilationUnitTree> units = task.parse(); 163 ((JavacTaskImpl) task).enter(); 164 165 env.init(task); 166 checker = new Checker(env); 167 168 DeclScanner ds = new DeclScanner(env) { 169 @Override 170 void visitDecl(Tree tree, Name name) { 171 TreePath p = getCurrentPath(); 172 DocCommentTree dc = env.trees.getDocCommentTree(p); 173 174 checker.scan(dc, p); 175 } 176 }; 177 178 ds.scan(units, null); 179 180 reportStats(out); 181 182 Context ctx = ((JavacTaskImpl) task).getContext(); 183 JavaCompiler c = JavaCompiler.instance(ctx); 184 c.printCount("error", c.errorCount()); 185 c.printCount("warn", c.warningCount()); 186 } 187 188 void processArgs(String... args) throws BadArgs { 189 javacOpts = new ArrayList<>(); 190 javacFiles = new ArrayList<>(); 191 192 if (args.length == 0) 193 needHelp = true; 194 195 for (int i = 0; i < args.length; i++) { 196 String arg = args[i]; 197 if (arg.matches("-Xmax(errs|warns)") && i + 1 < args.length) { 198 if (args[++i].matches("[0-9]+")) { 199 javacOpts.add(arg); 200 javacOpts.add(args[i]); 201 } else { 202 throw new BadArgs("dc.bad.value.for.option", arg, args[i]); 203 } 204 } else if ((arg.equals("-target") || arg.equals("-source")) && i + 1 < args.length) { 205 javacOpts.add(arg); 206 javacOpts.add(args[++i]); 207 } else if (arg.equals(STATS)) { 208 env.messages.setStatsEnabled(true); 209 } else if (arg.equals("-bootclasspath") && i + 1 < args.length) { 210 javacBootClassPath = splitPath(args[++i]); 211 } else if (arg.equals("-classpath") && i + 1 < args.length) { 212 javacClassPath = splitPath(args[++i]); 213 } else if (arg.equals("-cp") && i + 1 < args.length) { 214 javacClassPath = splitPath(args[++i]); 215 } else if (arg.equals("-sourcepath") && i + 1 < args.length) { 216 javacSourcePath = splitPath(args[++i]); 217 } else if (arg.equals(XMSGS_OPTION)) { 218 env.messages.setOptions(null); 219 } else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) { 220 env.messages.setOptions(arg.substring(arg.indexOf(":") + 1)); 221 } else if (arg.startsWith(XCUSTOM_TAGS_PREFIX)) { 222 env.setCustomTags(arg.substring(arg.indexOf(":") + 1)); 223 } else if (arg.startsWith(XHTML_VERSION_PREFIX)) { 224 String argsVersion = arg.substring(arg.indexOf(":") + 1); 225 HtmlVersion htmlVersion = HtmlVersion.getHtmlVersion(argsVersion); 226 if (htmlVersion != null) { 227 env.setHtmlVersion(htmlVersion); 228 } else { 229 throw new BadArgs("dc.bad.value.for.option", arg, argsVersion); 230 } 231 } else if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help") 232 || arg.equals("-?") || arg.equals("-usage")) { 233 needHelp = true; 234 } else if (arg.startsWith("-")) { 235 throw new BadArgs("dc.bad.option", arg); 236 } else { 237 while (i < args.length) 238 javacFiles.add(new File(args[i++])); 239 } 240 } 241 } 242 243 void showHelp(PrintWriter out) { 244 String msg = localize("dc.main.usage"); 245 for (String line: msg.split("\n")) 246 out.println(line); 247 } 248 249 List<File> splitPath(String path) { 250 List<File> files = new ArrayList<>(); 251 for (String f: path.split(File.pathSeparator)) { 252 if (f.length() > 0) 253 files.add(new File(f)); 254 } 255 return files; 256 } 257 258 List<File> javacBootClassPath; 259 List<File> javacClassPath; 260 List<File> javacSourcePath; 261 List<String> javacOpts; 262 List<File> javacFiles; 263 boolean needHelp = false; 264 265 // </editor-fold> 266 267 // <editor-fold defaultstate="collapsed" desc="javac Plugin"> 268 269 @Override @DefinedBy(Api.COMPILER_TREE) 270 public String getName() { 271 return "doclint"; 272 } 273 274 @Override @DefinedBy(Api.COMPILER_TREE) 275 public void init(JavacTask task, String... args) { 276 init(task, args, true); 277 } 278 279 // </editor-fold> 280 281 // <editor-fold defaultstate="collapsed" desc="Embedding API"> 282 283 public void init(JavacTask task, String[] args, boolean addTaskListener) { 284 env = new Env(); 285 for (String arg : args) { 286 if (arg.equals(XMSGS_OPTION)) { 287 env.messages.setOptions(null); 288 } else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) { 289 env.messages.setOptions(arg.substring(arg.indexOf(":") + 1)); 290 } else if (arg.matches(XIMPLICIT_HEADERS + "[1-6]")) { 291 char ch = arg.charAt(arg.length() - 1); 292 env.setImplicitHeaders(Character.digit(ch, 10)); 293 } else if (arg.startsWith(XCUSTOM_TAGS_PREFIX)) { 294 env.setCustomTags(arg.substring(arg.indexOf(":") + 1)); 295 } else if (arg.startsWith(XHTML_VERSION_PREFIX)) { 296 String argsVersion = arg.substring(arg.indexOf(":") + 1); 297 HtmlVersion htmlVersion = HtmlVersion.getHtmlVersion(argsVersion); 298 if (htmlVersion != null) { 299 env.setHtmlVersion(htmlVersion); 300 } else { 301 throw new IllegalArgumentException(argsVersion); 302 } 303 } else if (arg.startsWith(XCHECK_PACKAGE)) { 304 env.setCheckPackages(arg.substring(arg.indexOf(":") + 1)); 305 } else 306 throw new IllegalArgumentException(arg); 307 } 308 env.init(task); 309 310 checker = new Checker(env); 311 312 if (addTaskListener) { 313 final DeclScanner ds = new DeclScanner(env) { 314 @Override 315 void visitDecl(Tree tree, Name name) { 316 TreePath p = getCurrentPath(); 317 DocCommentTree dc = env.trees.getDocCommentTree(p); 318 319 checker.scan(dc, p); 320 } 321 }; 322 323 TaskListener tl = new TaskListener() { 324 @Override @DefinedBy(Api.COMPILER_TREE) 325 public void started(TaskEvent e) { 326 switch (e.getKind()) { 327 case ANALYZE: 328 CompilationUnitTree tree; 329 while ((tree = todo.poll()) != null) 330 ds.scan(tree, null); 331 break; 332 } 333 } 334 335 @Override @DefinedBy(Api.COMPILER_TREE) 336 public void finished(TaskEvent e) { 337 switch (e.getKind()) { 338 case PARSE: 339 todo.add(e.getCompilationUnit()); 340 break; 341 } 342 } 343 344 Queue<CompilationUnitTree> todo = new LinkedList<>(); 345 }; 346 347 task.addTaskListener(tl); 348 } 349 } 350 351 public void scan(TreePath p) { 352 DocCommentTree dc = env.trees.getDocCommentTree(p); 353 checker.scan(dc, p); 354 } 355 356 public boolean shouldCheck(CompilationUnitTree unit) { 357 return env.shouldCheck(unit); 358 } 359 360 public void reportStats(PrintWriter out) { 361 env.messages.reportStats(out); 362 } 363 364 // </editor-fold> 365 366 Env env; 367 Checker checker; 368 369 public static boolean isValidOption(String opt) { 370 if (opt.equals(XMSGS_OPTION)) 371 return true; 372 if (opt.startsWith(XMSGS_CUSTOM_PREFIX)) 373 return Messages.Options.isValidOptions(opt.substring(XMSGS_CUSTOM_PREFIX.length())); 374 if (opt.startsWith(XCHECK_PACKAGE)) { 375 return Env.validatePackages(opt.substring(opt.indexOf(":") + 1)); 376 } 377 return false; 378 } 379 380 private String localize(String code, Object... args) { 381 Messages m = (env != null) ? env.messages : new Messages(null); 382 return m.localize(code, args); 383 } 384 385 // <editor-fold defaultstate="collapsed" desc="DeclScanner"> 386 387 static abstract class DeclScanner extends TreePathScanner<Void, Void> { 388 final Env env; 389 390 public DeclScanner(Env env) { 391 this.env = env; 392 } 393 394 abstract void visitDecl(Tree tree, Name name); 395 396 @Override @DefinedBy(Api.COMPILER_TREE) 397 public Void visitPackage(PackageTree tree, Void ignore) { 398 visitDecl(tree, null); 399 return super.visitPackage(tree, ignore); 400 } 401 @Override @DefinedBy(Api.COMPILER_TREE) 402 public Void visitClass(ClassTree tree, Void ignore) { 403 visitDecl(tree, tree.getSimpleName()); 404 return super.visitClass(tree, ignore); 405 } 406 407 @Override @DefinedBy(Api.COMPILER_TREE) 408 public Void visitMethod(MethodTree tree, Void ignore) { 409 visitDecl(tree, tree.getName()); 410 //return super.visitMethod(tree, ignore); 411 return null; 412 } 413 414 @Override @DefinedBy(Api.COMPILER_TREE) 415 public Void visitModule(ModuleTree tree, Void ignore) { 416 visitDecl(tree, null); 417 return super.visitModule(tree, ignore); 418 } 419 420 @Override @DefinedBy(Api.COMPILER_TREE) 421 public Void visitVariable(VariableTree tree, Void ignore) { 422 visitDecl(tree, tree.getName()); 423 return super.visitVariable(tree, ignore); 424 } 425 426 @Override @DefinedBy(Api.COMPILER_TREE) 427 public Void visitCompilationUnit(CompilationUnitTree node, Void p) { 428 if (!env.shouldCheck(node)) { 429 return null; 430 } 431 return super.visitCompilationUnit(node, p); 432 } 433 434 } 435 436 // </editor-fold> 437 438} 439