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