View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2014  Oliver Burn
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package com.puppycrawl.tools.checkstyle.doclets;
20  
21  import java.io.File;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.io.PrintWriter;
25  import java.util.Arrays;
26  import java.util.Comparator;
27  import com.sun.javadoc.ClassDoc;
28  import com.sun.javadoc.RootDoc;
29  import com.sun.javadoc.Tag;
30  
31  /**
32   * Doclet which is used to extract Anakia input files from the
33   * Javadoc of Check implementations, so the Check's docs are
34   * autogenerated.
35   * Attention: this is incomplete autogenerator of Check's documentation
36   * from the Check's javadoc. It is not used now, and should be removed from
37   * master branch till completed.
38   * @author lkuehne
39   */
40  public final class CheckDocsDoclet
41  {
42      /** javadoc command line option for dest dir. */
43      private static final String DEST_DIR_OPT = "-d";
44  
45      /** Stop instances being created. */
46      private CheckDocsDoclet()
47      {
48      }
49  
50      /**
51       * Comparator that compares the {@link ClassDoc ClassDocs} of two checks
52       * by their check name.
53       */
54      private static class ClassDocByCheckNameComparator implements
55          Comparator<ClassDoc>
56      {
57          /** {@inheritDoc} */
58          public int compare(ClassDoc object1, ClassDoc object2)
59          {
60              final String checkName1 = getCheckName(object1);
61              final String checkName2 = getCheckName(object2);
62              return checkName1.compareTo(checkName2);
63          }
64      }
65  
66      /**
67       * The first sentence of the check description.
68       *
69       * @param classDoc class doc of the check, e.g. EmptyStatement
70       * @return The first sentence of the check description.
71       */
72      private static String getDescription(final ClassDoc classDoc)
73      {
74          final Tag[] tags = classDoc.firstSentenceTags();
75          final StringBuffer buf = new StringBuffer();
76          if (tags.length > 0) {
77              buf.append(tags[0].text());
78          }
79          removeOpeningParagraphTag(buf);
80          return buf.toString();
81      }
82  
83      /**
84       * Removes an opening p tag from a StringBuffer.
85       * @param text the text to process
86       */
87      private static void removeOpeningParagraphTag(final StringBuffer text)
88      {
89          final String openTag = "<p>";
90          final int tagLen = openTag.length();
91          if ((text.length() > tagLen)
92                  && text.substring(0, tagLen).equals(openTag))
93          {
94              text.delete(0, tagLen);
95          }
96      }
97  
98      /**
99       * Returns the official name of a check.
100      *
101      * @param classDoc the the check's documentation as extracted by javadoc
102      * @return the check name, e.g. "IllegalImport" for
103      * the "c.p.t.c.c.i.IllegalImportCheck" class.
104      */
105     private static String getCheckName(final ClassDoc classDoc)
106     {
107         final String strippedClassName = classDoc.typeName();
108         final String checkName;
109         if (strippedClassName.endsWith("Check")) {
110             checkName = strippedClassName.substring(
111                     0, strippedClassName.length() - "Check".length());
112         }
113         else {
114             checkName = strippedClassName;
115         }
116         return checkName;
117     }
118 
119     /**
120      * Writes the opening tags of an xdoc.
121      * @param printWriter you guessed it ... the target to print to :)
122      * @param title the title to use for the document.
123      */
124     private static void writeXdocsHeader(
125             final PrintWriter printWriter,
126             final String title)
127     {
128         printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
129         printWriter.println("<document>");
130         printWriter.println("<properties>");
131         printWriter.println("<title>" + title + "</title>");
132         printWriter.println("<author "
133                 + "email=\"checkstyle-devel@lists.sourceforge.net"
134                 + "\">Checkstyle Development Team</author>");
135         printWriter.println("</properties>");
136         printWriter.println("<body>");
137         printWriter.flush();
138     }
139 
140     /**
141      * Writes the closing tags of an xdoc document.
142      * @param printWriter you guessed it ... the target to print to :)
143      */
144     private static void writeXdocsFooter(final PrintWriter printWriter)
145     {
146         printWriter.println("</body>");
147         printWriter.println("</document>");
148         printWriter.flush();
149     }
150 
151     /**
152      * Doclet entry point.
153      * @param root parsed javadoc of all java files passed to the javadoc task
154      * @return true (TODO: semantics of the return value is not clear to me)
155      * @throws IOException if there are problems writing output
156      */
157     public static boolean start(RootDoc root) throws IOException
158     {
159         final ClassDoc[] classDocs = root.classes();
160 
161         final File destDir = new File(getDestDir(root.options()));
162 
163         final File checksIndexFile = new File(destDir, "availablechecks.xml");
164         final PrintWriter fileWriter = new PrintWriter(
165                 new FileWriter(checksIndexFile));
166         writeXdocsHeader(fileWriter, "Available Checks");
167 
168         fileWriter.println("<p>Checkstyle provides many checks that you can"
169                 + " apply to your source code. Below is an alphabetical"
170                 + " reference, the site navigation menu provides a reference"
171                 + " organized by functionality.</p>");
172         fileWriter.println("<table>");
173 
174         Arrays.sort(classDocs, new ClassDocByCheckNameComparator());
175 
176         for (final ClassDoc classDoc : classDocs) {
177 
178             // TODO: introduce a "CheckstyleModule" interface
179             // so we can do better in the next line...
180             if (classDoc.typeName().endsWith("Check")
181                     && !classDoc.isAbstract())
182             {
183                 String pageName = getPageName(classDoc);
184 
185                 // allow checks to override pageName when
186                 // java package hierarchy is not reflected in doc structure
187                 final Tag[] docPageTags = classDoc.tags("checkstyle-docpage");
188                 if ((docPageTags != null) && (docPageTags.length > 0)) {
189                     pageName = docPageTags[0].text();
190                 }
191 
192                 final String descr = getDescription(classDoc);
193                 final String checkName = getCheckName(classDoc);
194 
195 
196                 fileWriter.println("<tr>"
197                         + "<td><a href=\""
198                         + "config_" + pageName + ".html#" + checkName
199                         + "\">" + checkName + "</a></td><td>"
200                         + descr
201                         + "</td></tr>");
202             }
203         }
204 
205         fileWriter.println("</table>");
206         writeXdocsFooter(fileWriter);
207         fileWriter.close();
208         return true;
209     }
210 
211     /**
212      * Calculates the human readable page name for a doc page.
213      *
214      * @param classDoc the doc page.
215      * @return the human readable page name for the doc page.
216      */
217     private static String getPageName(ClassDoc classDoc)
218     {
219         final String packageName = classDoc.containingPackage().name();
220         final String pageName =
221                 packageName.substring(packageName.lastIndexOf('.') + 1);
222         if ("checks".equals(pageName)) {
223             return "misc";
224         }
225         return pageName;
226     }
227 
228     /**
229      * Return the destination directory for this Javadoc run.
230      * @param options Javadoc commandline options
231      * @return the dest dir specified on the command line (or ant task)
232      */
233     public static String getDestDir(String[][] options)
234     {
235         for (final String[] opt : options) {
236             if (DEST_DIR_OPT.equalsIgnoreCase(opt[0])) {
237                 return opt[1];
238             }
239         }
240         return null; // TODO: throw exception here ???
241     }
242 
243     /**
244      * Returns option length (how many parts are in option).
245      * @param option option name to process
246      * @return option length (how many parts are in option).
247      */
248     public static int optionLength(String option)
249     {
250         if (DEST_DIR_OPT.equals(option)) {
251             return 2;
252         }
253         return 0;
254     }
255 
256 }