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