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