jjg@1455: /* jjg@1506: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. jjg@1455: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@1455: * jjg@1455: * This code is free software; you can redistribute it and/or modify it jjg@1455: * under the terms of the GNU General Public License version 2 only, as jjg@1455: * published by the Free Software Foundation. Oracle designates this jjg@1455: * particular file as subject to the "Classpath" exception as provided jjg@1455: * by Oracle in the LICENSE file that accompanied this code. jjg@1455: * jjg@1455: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@1455: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@1455: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@1455: * version 2 for more details (a copy is included in the LICENSE file that jjg@1455: * accompanied this code). jjg@1455: * jjg@1455: * You should have received a copy of the GNU General Public License version jjg@1455: * 2 along with this work; if not, write to the Free Software Foundation, jjg@1455: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@1455: * jjg@1455: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@1455: * or visit www.oracle.com if you need additional information or have any jjg@1455: * questions. jjg@1455: */ jjg@1455: jjg@1455: package com.sun.tools.doclint; jjg@1455: jjg@1455: import java.io.File; jjg@1455: import java.io.IOException; jjg@1455: import java.io.PrintWriter; jjg@1455: import java.util.ArrayList; jjg@2033: import java.util.HashSet; jjg@2033: import java.util.LinkedList; jjg@1455: import java.util.List; jjg@2033: import java.util.Queue; jjg@2033: import java.util.Set; jjg@1455: jjg@1455: import javax.lang.model.element.Name; jjg@2033: import javax.tools.JavaFileObject; jjg@1455: import javax.tools.StandardLocation; jjg@1455: jjg@1455: import com.sun.source.doctree.DocCommentTree; jjg@1455: import com.sun.source.tree.ClassTree; jjg@1455: import com.sun.source.tree.CompilationUnitTree; jjg@1455: import com.sun.source.tree.MethodTree; jjg@1455: import com.sun.source.tree.Tree; jjg@1455: import com.sun.source.tree.VariableTree; jjg@1455: import com.sun.source.util.JavacTask; jjg@1455: import com.sun.source.util.Plugin; jjg@1455: import com.sun.source.util.TaskEvent; jjg@1455: import com.sun.source.util.TaskListener; jjg@1455: import com.sun.source.util.TreePath; jjg@1455: import com.sun.source.util.TreePathScanner; jjg@1455: import com.sun.tools.javac.api.JavacTaskImpl; jjg@1455: import com.sun.tools.javac.api.JavacTool; jjg@1455: import com.sun.tools.javac.file.JavacFileManager; jjg@1455: import com.sun.tools.javac.main.JavaCompiler; jjg@1455: import com.sun.tools.javac.util.Context; jjg@1455: jjg@1455: /** jjg@1455: * Multi-function entry point for the doc check utility. jjg@1455: * jjg@1455: * This class can be invoked in the following ways: jjg@1455: * jjg@1455: * jjg@1455: *

This is NOT part of any supported API. jjg@1455: * If you write code that depends on this, you do so at your own jjg@1455: * risk. This code and its internal interfaces are subject to change jjg@1455: * or deletion without notice.

jjg@1455: */ jjg@1455: public class DocLint implements Plugin { jjg@1455: jjg@1455: public static final String XMSGS_OPTION = "-Xmsgs"; jjg@1455: public static final String XMSGS_CUSTOM_PREFIX = "-Xmsgs:"; jjg@1455: private static final String STATS = "-stats"; jjg@1668: public static final String XIMPLICIT_HEADERS = "-XimplicitHeaders:"; jjg@1455: jjg@1455: // jjg@1455: public static void main(String... args) { jjg@1796: DocLint dl = new DocLint(); jjg@1455: try { jjg@1796: dl.run(args); jjg@1455: } catch (BadArgs e) { jjg@1455: System.err.println(e.getMessage()); jjg@1455: System.exit(1); jjg@1455: } catch (IOException e) { jjg@1796: System.err.println(dl.localize("dc.main.ioerror", e.getLocalizedMessage())); jjg@1455: System.exit(2); jjg@1455: } jjg@1455: } jjg@1455: jjg@1455: // jjg@1455: jjg@1455: // jjg@1455: jjg@1796: public class BadArgs extends Exception { jjg@1455: private static final long serialVersionUID = 0; jjg@1455: BadArgs(String code, Object... args) { jjg@1796: super(localize(code, args)); jjg@1455: this.code = code; jjg@1455: this.args = args; jjg@1455: } jjg@1455: jjg@1455: final String code; jjg@1455: final Object[] args; jjg@1455: } jjg@1455: jjg@1455: /** jjg@1455: * Simple API entry point. jjg@1455: */ jjg@1455: public void run(String... args) throws BadArgs, IOException { jjg@1455: PrintWriter out = new PrintWriter(System.out); jjg@1455: try { jjg@1455: run(out, args); jjg@1455: } finally { jjg@1455: out.flush(); jjg@1455: } jjg@1455: } jjg@1455: jjg@1455: public void run(PrintWriter out, String... args) throws BadArgs, IOException { jjg@1455: env = new Env(); jjg@1455: processArgs(args); jjg@1455: jjg@1455: if (needHelp) jjg@1455: showHelp(out); jjg@1455: jjg@1455: if (javacFiles.isEmpty()) { jjg@1455: if (!needHelp) jjg@1796: out.println(localize("dc.main.no.files.given")); jjg@1455: } jjg@1455: jjg@1455: JavacTool tool = JavacTool.create(); jjg@1455: jjg@1455: JavacFileManager fm = new JavacFileManager(new Context(), false, null); jjg@1455: fm.setSymbolFileEnabled(false); jjg@1455: fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, javacBootClassPath); jjg@1455: fm.setLocation(StandardLocation.CLASS_PATH, javacClassPath); jjg@1455: fm.setLocation(StandardLocation.SOURCE_PATH, javacSourcePath); jjg@1455: jjg@1455: JavacTask task = tool.getTask(out, fm, null, javacOpts, null, jjg@1455: fm.getJavaFileObjectsFromFiles(javacFiles)); jjg@1455: Iterable units = task.parse(); jjg@1455: ((JavacTaskImpl) task).enter(); jjg@1455: jjg@1455: env.init(task); jjg@1455: checker = new Checker(env); jjg@1455: jjg@1455: DeclScanner ds = new DeclScanner() { jjg@1455: @Override jjg@1455: void visitDecl(Tree tree, Name name) { jjg@1455: TreePath p = getCurrentPath(); jjg@1455: DocCommentTree dc = env.trees.getDocCommentTree(p); jjg@1455: jjg@1455: checker.scan(dc, p); jjg@1455: } jjg@1455: }; jjg@1455: jjg@1455: ds.scan(units, null); jjg@1455: jjg@1455: reportStats(out); jjg@1455: jjg@1455: Context ctx = ((JavacTaskImpl) task).getContext(); jjg@1455: JavaCompiler c = JavaCompiler.instance(ctx); jjg@1455: c.printCount("error", c.errorCount()); jjg@1455: c.printCount("warn", c.warningCount()); jjg@1455: } jjg@1455: jjg@1455: void processArgs(String... args) throws BadArgs { jjg@1913: javacOpts = new ArrayList<>(); jjg@1913: javacFiles = new ArrayList<>(); jjg@1455: jjg@1455: if (args.length == 0) jjg@1455: needHelp = true; jjg@1455: jjg@1455: for (int i = 0; i < args.length; i++) { jjg@1455: String arg = args[i]; jjg@1455: if (arg.matches("-Xmax(errs|warns)") && i + 1 < args.length) { jjg@1455: if (args[++i].matches("[0-9]+")) { jjg@1455: javacOpts.add(arg); jjg@1455: javacOpts.add(args[i]); jjg@1455: } else { jjg@1455: throw new BadArgs("dc.bad.value.for.option", arg, args[i]); jjg@1455: } jjg@1455: } else if (arg.equals(STATS)) { jjg@1455: env.messages.setStatsEnabled(true); jjg@1506: } else if (arg.equals("-bootclasspath") && i + 1 < args.length) { jjg@1455: javacBootClassPath = splitPath(args[++i]); jjg@1506: } else if (arg.equals("-classpath") && i + 1 < args.length) { jjg@1455: javacClassPath = splitPath(args[++i]); vromero@1885: } else if (arg.equals("-cp") && i + 1 < args.length) { vromero@1885: javacClassPath = splitPath(args[++i]); jjg@1506: } else if (arg.equals("-sourcepath") && i + 1 < args.length) { jjg@1455: javacSourcePath = splitPath(args[++i]); jjg@1455: } else if (arg.equals(XMSGS_OPTION)) { jjg@1455: env.messages.setOptions(null); jjg@1455: } else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) { jjg@1455: env.messages.setOptions(arg.substring(arg.indexOf(":") + 1)); jjg@1455: } else if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help") jjg@1455: || arg.equals("-?") || arg.equals("-usage")) { jjg@1455: needHelp = true; jjg@1455: } else if (arg.startsWith("-")) { jjg@1455: throw new BadArgs("dc.bad.option", arg); jjg@1455: } else { jjg@1455: while (i < args.length) jjg@1455: javacFiles.add(new File(args[i++])); jjg@1455: } jjg@1455: } jjg@1455: } jjg@1455: jjg@1455: void showHelp(PrintWriter out) { jjg@1796: String msg = localize("dc.main.usage"); jjg@1796: for (String line: msg.split("\n")) jjg@1796: out.println(line); jjg@1455: } jjg@1455: jjg@1455: List splitPath(String path) { jjg@1913: List files = new ArrayList<>(); jjg@1506: for (String f: path.split(File.pathSeparator)) { jjg@1455: if (f.length() > 0) jjg@1455: files.add(new File(f)); jjg@1455: } jjg@1455: return files; jjg@1455: } jjg@1455: jjg@1455: List javacBootClassPath; jjg@1455: List javacClassPath; jjg@1455: List javacSourcePath; jjg@1455: List javacOpts; jjg@1455: List javacFiles; jjg@1455: boolean needHelp = false; jjg@1455: jjg@1455: // jjg@1455: jjg@1455: // jjg@1455: jjg@1455: @Override jjg@1455: public String getName() { jjg@1455: return "doclint"; jjg@1455: } jjg@1455: jjg@1455: @Override mchung@1458: public void init(JavacTask task, String... args) { jjg@1455: init(task, args, true); jjg@1455: } jjg@1455: jjg@1455: // jjg@1455: jjg@1455: // jjg@1455: jjg@1455: public void init(JavacTask task, String[] args, boolean addTaskListener) { jjg@1455: env = new Env(); jjg@1455: for (int i = 0; i < args.length; i++) { jjg@1455: String arg = args[i]; jjg@1455: if (arg.equals(XMSGS_OPTION)) { jjg@1455: env.messages.setOptions(null); jjg@1455: } else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) { jjg@1455: env.messages.setOptions(arg.substring(arg.indexOf(":") + 1)); jjg@1668: } else if (arg.matches(XIMPLICIT_HEADERS + "[1-6]")) { jjg@1668: char ch = arg.charAt(arg.length() - 1); jjg@1668: env.setImplicitHeaders(Character.digit(ch, 10)); jjg@1455: } else jjg@1455: throw new IllegalArgumentException(arg); jjg@1455: } jjg@1455: env.init(task); jjg@1455: jjg@1455: checker = new Checker(env); jjg@1455: jjg@1455: if (addTaskListener) { jjg@1455: final DeclScanner ds = new DeclScanner() { jjg@1455: @Override jjg@1455: void visitDecl(Tree tree, Name name) { jjg@1455: TreePath p = getCurrentPath(); jjg@1455: DocCommentTree dc = env.trees.getDocCommentTree(p); jjg@1455: jjg@1455: checker.scan(dc, p); jjg@1455: } jjg@1455: }; jjg@1455: jjg@1455: TaskListener tl = new TaskListener() { jjg@1455: @Override jjg@1455: public void started(TaskEvent e) { jjg@2033: switch (e.getKind()) { jjg@2033: case ANALYZE: jjg@2033: CompilationUnitTree tree; jjg@2033: while ((tree = todo.poll()) != null) jjg@2033: ds.scan(tree, null); jjg@2033: break; jjg@2033: } jjg@1455: } jjg@1455: jjg@1455: @Override jjg@1455: public void finished(TaskEvent e) { jjg@1455: switch (e.getKind()) { jjg@2033: case PARSE: jjg@2033: todo.add(e.getCompilationUnit()); jjg@2033: break; jjg@1455: } jjg@1455: } jjg@2033: jjg@2033: Queue todo = new LinkedList(); jjg@1455: }; jjg@1455: jjg@1455: task.addTaskListener(tl); jjg@1455: } jjg@1455: } jjg@1455: jjg@1455: public void scan(TreePath p) { jjg@1455: DocCommentTree dc = env.trees.getDocCommentTree(p); jjg@1455: checker.scan(dc, p); jjg@1455: } jjg@1455: jjg@1455: public void reportStats(PrintWriter out) { jjg@1455: env.messages.reportStats(out); jjg@1455: } jjg@1455: jjg@1455: // jjg@1455: jjg@1455: Env env; jjg@1455: Checker checker; jjg@1455: jjg@1455: public static boolean isValidOption(String opt) { jjg@1455: if (opt.equals(XMSGS_OPTION)) jjg@1455: return true; jjg@1455: if (opt.startsWith(XMSGS_CUSTOM_PREFIX)) jjg@1455: return Messages.Options.isValidOptions(opt.substring(XMSGS_CUSTOM_PREFIX.length())); jjg@1455: return false; jjg@1455: } jjg@1455: jjg@1796: private String localize(String code, Object... args) { jjg@1796: Messages m = (env != null) ? env.messages : new Messages(null); jjg@1796: return m.localize(code, args); jjg@1796: } jjg@1796: jjg@1455: // jjg@1455: jjg@1455: static abstract class DeclScanner extends TreePathScanner { jjg@1455: abstract void visitDecl(Tree tree, Name name); jjg@1455: jjg@1455: @Override jjg@1895: public Void visitCompilationUnit(CompilationUnitTree tree, Void ignore) { jjg@1895: if (tree.getPackageName() != null) { jjg@1895: visitDecl(tree, null); jjg@1895: } jjg@1895: return super.visitCompilationUnit(tree, ignore); jjg@1895: } jjg@1895: jjg@1895: @Override jjg@1455: public Void visitClass(ClassTree tree, Void ignore) { jjg@1455: visitDecl(tree, tree.getSimpleName()); jjg@1455: return super.visitClass(tree, ignore); jjg@1455: } jjg@1455: jjg@1455: @Override jjg@1455: public Void visitMethod(MethodTree tree, Void ignore) { jjg@1455: visitDecl(tree, tree.getName()); jjg@1455: //return super.visitMethod(tree, ignore); jjg@1455: return null; jjg@1455: } jjg@1455: jjg@1455: @Override jjg@1455: public Void visitVariable(VariableTree tree, Void ignore) { jjg@1455: visitDecl(tree, tree.getName()); jjg@1455: return super.visitVariable(tree, ignore); jjg@1455: } jjg@1455: } jjg@1455: jjg@1455: // jjg@1455: jjg@1455: }