001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.doclets;
021
022import java.io.FileNotFoundException;
023import java.io.FileOutputStream;
024import java.io.OutputStreamWriter;
025import java.io.PrintWriter;
026import java.io.Writer;
027import java.nio.charset.StandardCharsets;
028
029import com.sun.javadoc.ClassDoc;
030import com.sun.javadoc.DocErrorReporter;
031import com.sun.javadoc.FieldDoc;
032import com.sun.javadoc.RootDoc;
033
034/**
035 * Doclet which is used to write property file with short descriptions
036 * (first sentences) of TokenTypes' constants.
037 * Request: 724871
038 * For ide plugins (like the eclipse plugin) it would be useful to have
039 * programmatic access to the first sentence of the TokenType constants,
040 * so they can use them in their configuration gui.
041 * @author o_sukhodolsky
042 */
043public final class TokenTypesDoclet {
044    /** Command line option to specify file to write output of the doclet. */
045    private static final String DEST_FILE_OPT = "-destfile";
046
047    /** Stop instances being created. */
048    private TokenTypesDoclet() {
049    }
050
051    /**
052     * The doclet's starter method.
053     * @param root {@code RootDoc} given to the doclet
054     * @return true if the given {@code RootDoc} is processed.
055     * @exception FileNotFoundException will be thrown if the doclet
056     *            will be unable to write to the specified file.
057     */
058    public static boolean start(RootDoc root)
059            throws FileNotFoundException {
060        final String fileName = getDestFileName(root.options());
061        final FileOutputStream fos = new FileOutputStream(fileName);
062        final Writer osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
063        final PrintWriter writer = new PrintWriter(osw, false);
064
065        try {
066            final ClassDoc[] classes = root.classes();
067            final FieldDoc[] fields = classes[0].fields();
068            for (final FieldDoc field : fields) {
069                if (field.isStatic() && field.isPublic() && field.isFinal()
070                    && "int".equals(field.type().qualifiedTypeName())) {
071                    if (field.firstSentenceTags().length != 1) {
072                        final String message = "Should be only one tag.";
073                        throw new IllegalArgumentException(message);
074                    }
075                    writer.println(field.name() + "="
076                            + field.firstSentenceTags()[0].text());
077                }
078            }
079        }
080        finally {
081            writer.close();
082        }
083
084        return true;
085    }
086
087    /**
088     * Returns option length (how many parts are in option).
089     * @param option option name to process
090     * @return option length (how many parts are in option).
091     */
092    public static int optionLength(String option) {
093        int length = 0;
094        if (DEST_FILE_OPT.equals(option)) {
095            length = 2;
096        }
097        return length;
098    }
099
100    /**
101     * Checks that only valid options was specified.
102     * @param options all parsed options
103     * @param reporter the reporter to report errors.
104     * @return true if only valid options was specified
105     */
106    public static boolean checkOptions(String[][] options, DocErrorReporter reporter) {
107        boolean foundDestFileOption = false;
108        boolean onlyOneDestFileOption = true;
109        for (final String[] opt : options) {
110            if (DEST_FILE_OPT.equals(opt[0])) {
111                if (foundDestFileOption) {
112                    reporter.printError("Only one -destfile option allowed.");
113                    onlyOneDestFileOption = false;
114                    break;
115                }
116                foundDestFileOption = true;
117            }
118        }
119        if (!foundDestFileOption) {
120            reporter.printError("Usage: javadoc -destfile file -doclet TokenTypesDoclet ...");
121        }
122        return onlyOneDestFileOption && foundDestFileOption;
123    }
124
125    /**
126     * Reads destination file name.
127     * @param options all specified options.
128     * @return destination file name
129     */
130    private static String getDestFileName(String[]... options) {
131        String fileName = null;
132        for (final String[] opt : options) {
133            if (DEST_FILE_OPT.equals(opt[0])) {
134                fileName = opt[1];
135            }
136        }
137        return fileName;
138    }
139}