1/* 2 * Copyright (c) 1998, 2013, 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 build.tools.commentchecker; 27 28import java.io.*; 29import java.util.StringTokenizer; 30 31/** 32 * CommentChecker is a utility which verifies that there aren't 33 * "/*" or "/**" tokens inside any comment blocks in one or more 34 * Java source files. Although it is legal to have beginning 35 * comment delimiters inside of a comment block (JLS 3.7), there 36 * have been errors where a dropped end-comment delimiter in a 37 * method'd doc-comment effectively "erased" that method. We're 38 * therefore restricting beginning comment delimiters inside of 39 * JDK source (at least the Swing team is for their portion). 40 * 41 * To scan a few files, run CommentChecker as follows: 42 * 43 * java CommentChecker file1.java file2.java ... 44 * 45 * There are too many Java files in the JDK base for most shells 46 * to support listing in a single command, so CommentChecker also 47 * supports cpio and tar-style filename passing, where "-" 48 * indicates that the list of files is read from stdin: 49 * 50 * find . -name SCCS -prune -o -name '*.java' -print | \ 51 * java CommentChecker - 52 * 53 * @author Thomas Ball 54 */ 55public class CommentChecker { 56 57 static int errors = 0; 58 59 // Turn on this flag and recompile to dump this tool's state changes. 60 final static boolean verbose = false; 61 62 static void check(String fileName) { 63 BufferedReader in = null; 64 boolean inComment = false; 65 boolean inLineComment = false; 66 boolean inQuote = false; 67 boolean inEscape = false; 68 int lastChar = -1; 69 int lineNumber = 1; 70 71 try { 72 in = new BufferedReader(new FileReader(fileName)); 73 while (true) { 74 int ch = in.read(); 75 if (ch == -1) { 76 if (inQuote || inComment) { 77 error(fileName + ": premature EOF."); 78 } 79 return; 80 } 81 82 if (verbose) { 83 System.out.print((char)ch); 84 } 85 86 switch (ch) { 87 case '\n': 88 if (inQuote && !inComment) { 89 error(fileName + ":" + lineNumber + 90 " dangling quote."); 91 inQuote = false; 92 } 93 if (inLineComment) { 94 inLineComment = false; 95 if (verbose) { 96 System.out.println("\ninLineComment=false"); 97 } 98 } 99 lineNumber++; 100 break; 101 102 case '\"': 103 if (!inComment && !inLineComment && !inEscape && 104 !(!inQuote && lastChar == '\'')) { 105 inQuote = !inQuote; 106 if (verbose) { 107 System.out.println("\ninQuote=" + inQuote); 108 } 109 } 110 break; 111 112 case '/': 113 if (!inQuote && lastChar == '*') { 114 inComment = false; 115 if (verbose) { 116 System.out.println("\ninComment=false"); 117 } 118 } 119 if (!inQuote && lastChar == '/') { 120 inLineComment = true; 121 if (verbose) { 122 System.out.println("\ninLineComment=true"); 123 } 124 } 125 break; 126 127 case '*': 128 if (!inQuote && lastChar == '/') { 129 if (inComment) { 130 error(fileName + ":" + lineNumber + 131 " nested comment."); 132 } 133 inComment = true; 134 if (verbose) { 135 System.out.println("\ninComment=true"); 136 } 137 } 138 break; 139 } 140 141 lastChar = ch; 142 143 // Watch for escaped characters, such as '\"'. 144 if (ch == '\\' && !inEscape) { 145 inEscape = true; 146 if (verbose) { 147 System.out.println("\ninEscape set"); 148 } 149 } else { 150 inEscape = false; 151 } 152 } 153 } catch (FileNotFoundException fnfe) { 154 error(fileName + " not found."); 155 } catch (IOException ioe) { 156 error(fileName + ": " + ioe); 157 } finally { 158 if (in != null) { 159 try { 160 in.close(); 161 } catch (IOException e) { 162 error(fileName + ": " + e); 163 } 164 } 165 } 166 } 167 168 static void error(String description) { 169 System.err.println(description); 170 errors++; 171 } 172 173 static void exit() { 174 if (errors != 1) { 175 System.out.println("There were " + errors + " errors."); 176 } else { 177 System.out.println("There was 1 error."); 178 } 179 System.exit(errors); 180 } 181 182 public static void main(String[] args) { 183 if (args.length == 0) { 184 System.err.println("usage: java CommentChecker [-] file.java ..."); 185 System.exit(1); 186 } 187 188 if (args.length == 1 && args[0].equals("-")) { 189 /* read filenames in one per line from stdin, ala cpio. 190 * This is good for checking the whole JDK in one pass: 191 * 192 * cpio . -name SCCS -prune -o -name '*.java' -print | \ 193 * java CommentChecker - 194 */ 195 try { 196 BufferedReader br = 197 new BufferedReader(new InputStreamReader(System.in)); 198 while (true) { 199 String fileName = br.readLine(); 200 if (fileName == null) { 201 break; 202 } 203 check(fileName); 204 } 205 br.close(); 206 } catch (Exception e) { 207 error("error reading System.in: " + e); 208 } 209 } else { 210 for (int i = 0; i < args.length; i++) { 211 check(args[i]); 212 } 213 } 214 215 exit(); 216 } 217} 218