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