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.api; 020 021import com.google.common.collect.Lists; 022import com.google.common.collect.Maps; 023 024import java.io.Closeable; 025import java.io.File; 026import java.io.FileInputStream; 027import java.io.IOException; 028import java.io.InputStreamReader; 029import java.io.LineNumberReader; 030import java.io.UnsupportedEncodingException; 031import java.util.List; 032import java.util.concurrent.ConcurrentMap; 033import java.util.regex.Pattern; 034import java.util.regex.PatternSyntaxException; 035 036import org.apache.commons.beanutils.ConversionException; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039 040/** 041 * Contains utility methods. 042 * 043 * @author Oliver Burn 044 * @version 1.0 045 */ 046public final class Utils 047{ 048 /** Map of all created regular expressions **/ 049 private static final ConcurrentMap<String, Pattern> CREATED_RES = 050 Maps.newConcurrentMap(); 051 /** Shared instance of logger for exception logging. */ 052 private static final Log EXCEPTION_LOG = 053 LogFactory.getLog("com.puppycrawl.tools.checkstyle.ExceptionLog"); 054 055 ///CLOVER:OFF 056 /** stop instances being created **/ 057 private Utils() 058 { 059 } 060 ///CLOVER:ON 061 062 /** 063 * Accessor for shared instance of logger which should be 064 * used to log all exceptions occurred during <code>FileSetCheck</code> 065 * work (<code>debug()</code> should be used). 066 * @return shared exception logger. 067 */ 068 public static Log getExceptionLogger() 069 { 070 return EXCEPTION_LOG; 071 } 072 073 /** 074 * Returns whether the specified string contains only whitespace up to the 075 * specified index. 076 * 077 * @param index index to check up to 078 * @param line the line to check 079 * @return whether there is only whitespace 080 */ 081 public static boolean whitespaceBefore(int index, String line) 082 { 083 for (int i = 0; i < index; i++) { 084 if (!Character.isWhitespace(line.charAt(i))) { 085 return false; 086 } 087 } 088 return true; 089 } 090 091 /** 092 * Returns the length of a string ignoring all trailing whitespace. It is a 093 * pity that there is not a trim() like method that only removed the 094 * trailing whitespace. 095 * @param line the string to process 096 * @return the length of the string ignoring all trailing whitespace 097 **/ 098 public static int lengthMinusTrailingWhitespace(String line) 099 { 100 int len = line.length(); 101 for (int i = len - 1; i >= 0; i--) { 102 if (!Character.isWhitespace(line.charAt(i))) { 103 break; 104 } 105 len--; 106 } 107 return len; 108 } 109 110 /** 111 * Returns the length of a String prefix with tabs expanded. 112 * Each tab is counted as the number of characters is takes to 113 * jump to the next tab stop. 114 * @param string the input String 115 * @param toIdx index in string (exclusive) where the calculation stops 116 * @param tabWidth the distance between tab stop position. 117 * @return the length of string.substring(0, toIdx) with tabs expanded. 118 */ 119 public static int lengthExpandedTabs(String string, 120 int toIdx, 121 int tabWidth) 122 { 123 int len = 0; 124 for (int idx = 0; idx < toIdx; idx++) { 125 if (string.charAt(idx) == '\t') { 126 len = (len / tabWidth + 1) * tabWidth; 127 } 128 else { 129 len++; 130 } 131 } 132 return len; 133 } 134 135 /** 136 * This is a factory method to return an Pattern object for the specified 137 * regular expression. It calls {@link #getPattern(String, int)} with the 138 * compile flags defaults to 0. 139 * @return an Pattern object for the supplied pattern 140 * @param pattern the regular expression pattern 141 * @throws PatternSyntaxException an invalid pattern was supplied 142 **/ 143 public static Pattern getPattern(String pattern) 144 throws PatternSyntaxException 145 { 146 return getPattern(pattern, 0); 147 } 148 149 /** 150 * This is a factory method to return an Pattern object for the specified 151 * regular expression and compile flags. 152 * @return an Pattern object for the supplied pattern 153 * @param pattern the regular expression pattern 154 * @param compileFlags the compilation flags 155 * @throws PatternSyntaxException an invalid pattern was supplied 156 **/ 157 public static Pattern getPattern(String pattern, int compileFlags) 158 throws PatternSyntaxException 159 { 160 final String key = pattern + ":flags-" + compileFlags; 161 Pattern retVal = CREATED_RES.get(key); 162 if (retVal == null) { 163 retVal = Pattern.compile(pattern, compileFlags); 164 CREATED_RES.putIfAbsent(key, retVal); 165 } 166 return retVal; 167 } 168 169 /** 170 * Loads the contents of a file in a String array. 171 * @return the lines in the file 172 * @param fileName the name of the file to load 173 * @throws IOException error occurred 174 * @deprecated consider using {@link FileText} instead 175 **/ 176 @Deprecated 177 public static String[] getLines(String fileName) 178 throws IOException 179 { 180 return getLines( 181 fileName, 182 System.getProperty("file.encoding", "UTF-8")); 183 } 184 185 /** 186 * Loads the contents of a file in a String array using 187 * the named charset. 188 * @return the lines in the file 189 * @param fileName the name of the file to load 190 * @param charsetName the name of a supported charset 191 * @throws IOException error occurred 192 * @deprecated consider using {@link FileText} instead 193 **/ 194 @Deprecated 195 public static String[] getLines(String fileName, String charsetName) 196 throws IOException 197 { 198 final List<String> lines = Lists.newArrayList(); 199 final FileInputStream fr = new FileInputStream(fileName); 200 LineNumberReader lnr = null; 201 try { 202 lnr = new LineNumberReader(new InputStreamReader(fr, charsetName)); 203 } 204 catch (final UnsupportedEncodingException ex) { 205 fr.close(); 206 final String message = "unsupported charset: " + ex.getMessage(); 207 throw new UnsupportedEncodingException(message); 208 } 209 try { 210 while (true) { 211 final String l = lnr.readLine(); 212 if (l == null) { 213 break; 214 } 215 lines.add(l); 216 } 217 } 218 finally { 219 Utils.closeQuietly(lnr); 220 } 221 return lines.toArray(new String[lines.size()]); 222 } 223 224 /** 225 * Helper method to create a regular expression. 226 * @param pattern the pattern to match 227 * @return a created regexp object 228 * @throws ConversionException if unable to create Pattern object. 229 **/ 230 public static Pattern createPattern(String pattern) 231 throws ConversionException 232 { 233 Pattern retVal = null; 234 try { 235 retVal = getPattern(pattern); 236 } 237 catch (final PatternSyntaxException e) { 238 throw new ConversionException( 239 "Failed to initialise regexp expression " + pattern, e); 240 } 241 return retVal; 242 } 243 244 /** 245 * @return the base class name from a fully qualified name 246 * @param type the fully qualified name. Cannot be null 247 */ 248 public static String baseClassname(String type) 249 { 250 final int i = type.lastIndexOf("."); 251 return (i == -1) ? type : type.substring(i + 1); 252 } 253 254 /** 255 * Create a stripped down version of a filename. 256 * @param basedir the prefix to strip off the original filename 257 * @param fileName the original filename 258 * @return the filename where an initial prefix of basedir is stripped 259 */ 260 public static String getStrippedFileName( 261 final String basedir, final String fileName) 262 { 263 final String stripped; 264 if ((basedir == null) || !fileName.startsWith(basedir)) { 265 stripped = fileName; 266 } 267 else { 268 // making the assumption that there is text after basedir 269 final int skipSep = basedir.endsWith(File.separator) ? 0 : 1; 270 stripped = fileName.substring(basedir.length() + skipSep); 271 } 272 return stripped; 273 } 274 275 /** 276 * Closes the supplied {@link Closeable} object ignoring an 277 * {@link IOException} if it is thrown. Honestly, what are you going to 278 * do if you cannot close a file. 279 * @param shutting the object to be closed. 280 */ 281 public static void closeQuietly(Closeable shutting) 282 { 283 if (null != shutting) { 284 try { 285 shutting.close(); 286 } 287 catch (IOException e) { 288 ; // ignore 289 } 290 } 291 } 292}