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.File; 022import java.io.FileWriter; 023import java.io.IOException; 024import java.io.PrintWriter; 025import java.util.Arrays; 026import java.util.Comparator; 027import com.sun.javadoc.ClassDoc; 028import com.sun.javadoc.RootDoc; 029import com.sun.javadoc.Tag; 030 031/** 032 * Doclet which is used to extract Anakia input files from the 033 * Javadoc of Check implementations, so the Check's docs are 034 * autogenerated. 035 * Attention: this is incomplete autogenerator of Check's documentation 036 * from the Check's javadoc. It is not used now, and should be removed from 037 * master branch till completed. 038 * @author lkuehne 039 */ 040public final class CheckDocsDoclet 041{ 042 /** javadoc command line option for dest dir. */ 043 private static final String DEST_DIR_OPT = "-d"; 044 045 /** Stop instances being created. */ 046 private CheckDocsDoclet() 047 { 048 } 049 050 /** 051 * Comparator that compares the {@link ClassDoc ClassDocs} of two checks 052 * by their check name. 053 */ 054 private static class ClassDocByCheckNameComparator implements 055 Comparator<ClassDoc> 056 { 057 /** {@inheritDoc} */ 058 public int compare(ClassDoc object1, ClassDoc object2) 059 { 060 final String checkName1 = getCheckName(object1); 061 final String checkName2 = getCheckName(object2); 062 return checkName1.compareTo(checkName2); 063 } 064 } 065 066 /** 067 * The first sentence of the check description. 068 * 069 * @param classDoc class doc of the check, e.g. EmptyStatement 070 * @return The first sentence of the check description. 071 */ 072 private static String getDescription(final ClassDoc classDoc) 073 { 074 final Tag[] tags = classDoc.firstSentenceTags(); 075 final StringBuffer buf = new StringBuffer(); 076 if (tags.length > 0) { 077 buf.append(tags[0].text()); 078 } 079 removeOpeningParagraphTag(buf); 080 return buf.toString(); 081 } 082 083 /** 084 * Removes an opening p tag from a StringBuffer. 085 * @param text the text to process 086 */ 087 private static void removeOpeningParagraphTag(final StringBuffer text) 088 { 089 final String openTag = "<p>"; 090 final int tagLen = openTag.length(); 091 if ((text.length() > tagLen) 092 && text.substring(0, tagLen).equals(openTag)) 093 { 094 text.delete(0, tagLen); 095 } 096 } 097 098 /** 099 * 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}