001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
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////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.doclets;
020
021import java.io.FileNotFoundException;
022import java.io.FileOutputStream;
023import java.io.PrintStream;
024
025import com.sun.javadoc.ClassDoc;
026import com.sun.javadoc.DocErrorReporter;
027import com.sun.javadoc.FieldDoc;
028import com.sun.javadoc.RootDoc;
029
030/**
031 * Doclet which is used to write property file with short descriptions
032 * (first sentences) of TokenTypes' constants.
033 * Request: 724871
034 * For ide plugins (like the eclipse plugin) it would be useful to have
035 * programmatic access to the first sentence of the TokenType constants,
036 * so they can use them in their configuration gui.
037 * @author o_sukhodolsky
038 */
039public final class TokenTypesDoclet
040{
041    /** Command line option to specify file to write output of the doclet. */
042    private static final String DEST_FILE_OPT = "-destfile";
043
044    /** Stop instances being created. */
045    private TokenTypesDoclet()
046    {
047    }
048
049    /**
050     * The doclet's starter method.
051     * @param root <code>RootDoc</code> given to the doclet
052     * @exception FileNotFoundException will be thrown if the doclet
053     *            will be unable to write to the specified file.
054     * @return true if the given <code>RootDoc</code> is processed.
055     */
056    public static boolean start(RootDoc root) throws FileNotFoundException
057    {
058        final String fileName = getDestFileName(root.options());
059        final FileOutputStream fos = new FileOutputStream(fileName);
060        PrintStream ps = null;
061        try {
062            ps = new PrintStream(fos);
063            final ClassDoc[] classes = root.classes();
064            if ((classes.length != 1)
065                || !"TokenTypes".equals(classes[0].name()))
066            {
067                final String message =
068                    "The doclet should be used for TokenTypes only";
069                throw new IllegalArgumentException(message);
070            }
071
072            final FieldDoc[] fields = classes[0].fields();
073            for (final FieldDoc field : fields) {
074                if (field.isStatic() && field.isPublic() && field.isFinal()
075                    && "int".equals((field.type().qualifiedTypeName())))
076                {
077                    if (field.firstSentenceTags().length != 1) {
078                        final String message = "Should be only one tag.";
079                        throw new IllegalArgumentException(message);
080                    }
081                    ps.println(field.name() + "="
082                        + field.firstSentenceTags()[0].text());
083                }
084            }
085        }
086        finally {
087            if (ps != null) {
088                ps.close();
089            }
090        }
091
092        return true;
093    }
094
095    /**
096     * Returns option length (how many parts are in option).
097     * @param option option name to process
098     * @return option length (how many parts are in option).
099     */
100    public static int optionLength(String option)
101    {
102        if (DEST_FILE_OPT.equals(option)) {
103            return 2;
104        }
105        return 0;
106    }
107
108    /**
109     * Checks that only valid options was specified.
110     * @param options all parsed options
111     * @param reporter the reporter to report errors.
112     * @return true if only valid options was specified
113     */
114    public static boolean validOptions(String[][] options,
115                                       DocErrorReporter reporter)
116    {
117        boolean foundDestFileOption = false;
118        for (final String[] opt : options) {
119            if (DEST_FILE_OPT.equals(opt[0])) {
120                if (foundDestFileOption) {
121                    reporter.printError("Only one -destfile option allowed.");
122                    return false;
123                }
124                foundDestFileOption = true;
125            }
126        }
127        if (!foundDestFileOption) {
128            final String message =
129                "Usage: javadoc -destfile file -doclet TokenTypesDoclet ...";
130            reporter.printError(message);
131        }
132        return foundDestFileOption;
133    }
134
135    /**
136     * Reads destination file name.
137     * @param options all specified options.
138     * @return destination file name
139     */
140    private static String getDestFileName(String[][] options)
141    {
142        String fileName = null;
143        for (final String[] opt : options) {
144            if (DEST_FILE_OPT.equals(opt[0])) {
145                fileName = opt[1];
146            }
147        }
148        return fileName;
149    }
150}