001 package com.mockrunner.util.common; 002 003 import java.lang.reflect.Array; 004 import java.util.ArrayList; 005 import java.util.Collection; 006 import java.util.Iterator; 007 import java.util.List; 008 import java.util.Map; 009 010 import org.apache.oro.text.regex.MalformedPatternException; 011 import org.apache.oro.text.regex.Pattern; 012 import org.apache.oro.text.regex.Perl5Compiler; 013 import org.apache.oro.text.regex.Perl5Matcher; 014 015 import com.mockrunner.base.NestedApplicationException; 016 017 /** 018 * Simple util class for <code>String</code> related methods. 019 */ 020 public class StringUtil 021 { 022 /** 023 * Returns if the specified string is <code>null</code> or 024 * the empty string. 025 * @param string the string 026 * @return <code>true</code> if the specified string is <code>null</code> or 027 * the empty string, <code>false</code> otherwise 028 */ 029 public static boolean isEmptyOrNull(String string) 030 { 031 return (null == string) || (0 >= string.length()); 032 } 033 034 /** 035 * Returns <code>null</code>, if the specified string is <code>null</code> or 036 * the empty string. Returns the specified string otherwise. 037 * @param string the string 038 * @return <code>null</code> if the specified string is <code>null</code> or 039 * the empty string, the specified string otherwise 040 */ 041 public static String emptyStringToNull(String string) 042 { 043 return isEmptyOrNull(string) ? null : string; 044 } 045 046 /** 047 * Replaces all occurrences of <code>match</code> in 048 * <code>source</code> with <code>replacement</code>. 049 * @param source the source string 050 * @param match the string that is searched 051 * @param replacement the replacement string 052 * @return the modified string 053 * @throws IllegalArgumentException if any argument is <code>null</code> or 054 * if <code>match</code> is the empty string 055 */ 056 public static String replaceAll(String source, String match, String replacement) 057 { 058 if(null == source || null == match || null == replacement) 059 { 060 throw new IllegalArgumentException("null strings not allowed"); 061 } 062 if(match.length() <= 0) 063 { 064 throw new IllegalArgumentException("match must not be empty"); 065 } 066 StringBuffer buffer = new StringBuffer(source.length() + 10); 067 int index = 0; 068 int newIndex = 0; 069 while((newIndex = source.indexOf(match, index)) >= 0) 070 { 071 buffer.append(source.substring(index, newIndex)); 072 buffer.append(replacement); 073 index = newIndex + match.length(); 074 } 075 buffer.append(source.substring(index)); 076 return buffer.toString(); 077 } 078 079 /** 080 * Compares two strings and returns the last 081 * index where the two string are equal. If 082 * the first characters of the two string do 083 * not match or if at least one of the two strings 084 * is empty, -1 is returned. 085 * @param string1 the first string 086 * @param string2 the second string 087 * @return the last index where the strings are equal 088 */ 089 public static int compare(String string1, String string2) 090 { 091 int endIndex = Math.min(string1.length(), string2.length()); 092 for(int ii = 0; ii < endIndex; ii++) 093 { 094 if(string1.charAt(ii) != string2.charAt(ii)) return ii - 1; 095 } 096 return endIndex - 1; 097 } 098 099 /** 100 * Converts the character at the specified index to 101 * lowercase and returns the resulting string. 102 * @param string the string to convert 103 * @param index the index where the character is set to lowercase 104 * @return the converted string 105 * @throws IndexOutOfBoundsException if the index is out of 106 * range 107 */ 108 public static String lowerCase(String string, int index) 109 { 110 return lowerCase(string, index, -1); 111 } 112 113 /** 114 * Converts the character in the specified index range to 115 * lowercase and returns the resulting string. 116 * If the provided endIndex is smaller or equal to startIndex, 117 * the endIndex is set to startIndex + 1. 118 * @param string the string to convert 119 * @param startIndex the index to start, inclusive 120 * @param endIndex the index to end, exclusive 121 * @return the converted string 122 * @throws IndexOutOfBoundsException if the index is out of 123 * range 124 */ 125 public static String lowerCase(String string, int startIndex, int endIndex) 126 { 127 StringBuffer buffer = new StringBuffer(string); 128 if(endIndex <= startIndex) endIndex = startIndex + 1; 129 for(int ii = startIndex; ii < endIndex; ii++) 130 { 131 char character = buffer.charAt(ii); 132 buffer.setCharAt(ii, Character.toLowerCase(character)); 133 } 134 return buffer.toString(); 135 } 136 137 /** 138 * Helper method for <code>toString()</code> implementations. 139 * Returns a string <code>"field name: value"</code>. Handles 140 * <code>null</code> values, collections and arrays. If the 141 * field is a collection or an array, the returned string will 142 * be:<br> 143 * <code>"field name 0: value0\nfield name 1: value1"</code> 144 * @param fieldName the field name 145 * @param field the field value 146 * @return a suitable string for <code>toString()</code> implementations 147 */ 148 public static String fieldToString(String fieldName, Object field) 149 { 150 StringBuffer buffer = new StringBuffer(); 151 if(null == field) 152 { 153 buffer.append(fieldName + ": " + "null"); 154 } 155 else if(field.getClass().isArray()) 156 { 157 arrayToString(fieldName, field, buffer); 158 } 159 else if(field instanceof Collection) 160 { 161 collectionToString(fieldName, field, buffer); 162 } 163 else if(field instanceof Map) 164 { 165 mapToString(fieldName, field, buffer); 166 } 167 else 168 { 169 buffer.append(fieldName + ": " + field.toString()); 170 } 171 return buffer.toString(); 172 } 173 174 private static void arrayToString(String fieldName, Object field, StringBuffer buffer) 175 { 176 int length = Array.getLength(field); 177 if(0 >= length) 178 { 179 buffer.append(fieldName + ": " + "empty"); 180 } 181 else 182 { 183 for(int ii = 0; ii < length; ii++) 184 { 185 buffer.append(fieldToString(fieldName + " " + ii, Array.get(field, ii))); 186 if(ii < length - 1) 187 { 188 buffer.append("\n"); 189 } 190 } 191 } 192 } 193 194 private static void collectionToString(String fieldName, Object field, StringBuffer buffer) 195 { 196 List list = new ArrayList((Collection)field); 197 if(0 >= list.size()) 198 { 199 buffer.append(fieldName + ": " + "empty"); 200 } 201 else 202 { 203 for(int ii = 0; ii < list.size(); ii++) 204 { 205 buffer.append(fieldToString(fieldName + " " + ii, list.get(ii))); 206 if(ii < list.size() - 1) 207 { 208 buffer.append("\n"); 209 } 210 } 211 } 212 } 213 214 private static void mapToString(String fieldName, Object field, StringBuffer buffer) 215 { 216 if(0 >= ((Map)field).size()) 217 { 218 buffer.append(fieldName + ": " + "empty"); 219 } 220 else 221 { 222 Iterator keys = ((Map)field).keySet().iterator(); 223 int ii = 0; 224 while(keys.hasNext()) 225 { 226 Object key = keys.next(); 227 Object value = ((Map)field).get(key); 228 buffer.append(fieldToString(fieldName + " " + key, value)); 229 if(ii < ((Map)field).size() - 1) 230 { 231 buffer.append("\n"); 232 } 233 ii++; 234 } 235 } 236 } 237 238 /** 239 * Appends the entries in the specified <code>List</code> as strings 240 * with a terminating <i>"\n"</i> after each row. 241 * @param buffer the buffer 242 * @param data the <code>List</code> with the data 243 */ 244 public static void appendObjectsAsString(StringBuffer buffer, List data) 245 { 246 for(int ii = 0; ii < data.size(); ii++) 247 { 248 buffer.append(data.get(ii)); 249 buffer.append("\n"); 250 } 251 } 252 253 /** 254 * Appends <i>number</i> tabs (\t) to the buffer. 255 * @param buffer the buffer 256 * @param number the number of tabs to append 257 */ 258 public static void appendTabs(StringBuffer buffer, int number) 259 { 260 for(int ii = 0; ii < number; ii++) 261 { 262 buffer.append("\t"); 263 } 264 } 265 266 /** 267 * Splits a string into tokens. Similar to <code>StringTokenizer</code> 268 * except that empty tokens are recognized and added as <code>null</code>. 269 * With a delimiter of <i>";"</i> the string 270 * <i>"a;;b;c;;"</i> will split into 271 * <i>["a"] [null] ["b"] ["c"] [null]</i>. 272 * @param string the String 273 * @param delim the delimiter 274 * @param doTrim should each token be trimmed 275 * @return the array of tokens 276 */ 277 public static String[] split(String string, String delim, boolean doTrim) 278 { 279 int pos = 0, begin = 0; 280 ArrayList resultList = new ArrayList(); 281 while((-1 != (pos = string.indexOf(delim, begin))) && (begin < string.length())) 282 { 283 String token = string.substring(begin, pos); 284 if(doTrim) token = token.trim(); 285 if(token.length() == 0) token = null; 286 resultList.add(token); 287 begin = pos + delim.length(); 288 } 289 if(begin < string.length()) 290 { 291 String token = string.substring(begin); 292 if(doTrim) token = token.trim(); 293 if(token.length() == 0) token = null; 294 resultList.add(token); 295 } 296 return (String[])resultList.toArray(new String[resultList.size()]); 297 } 298 299 /** 300 * Returns how many times <code>string</code> contains 301 * <code>other</code>. 302 * @param string the string to search 303 * @param other the string that is searched 304 * @return the number of occurences 305 */ 306 public static int countMatches(String string, String other) 307 { 308 if(null == string) return 0; 309 if(null == other) return 0; 310 if(0 >= string.length()) return 0; 311 if(0 >= other.length()) return 0; 312 int count = 0; 313 int index = 0; 314 while((index <= string.length() - other.length()) && (-1 != (index = string.indexOf(other, index)))) 315 { 316 count++; 317 index += other.length(); 318 } 319 return count; 320 } 321 322 323 /** 324 * Returns if the specified strings are equal, ignoring 325 * case, if <code>caseSensitive</code> is <code>false</code>. 326 * @param source the source String 327 * @param target the target String 328 * @param caseSensitive is the comparison case sensitive 329 * @return <code>true</code> if the strings matches 330 * <code>false</code> otherwise 331 */ 332 public static boolean matchesExact(String source, String target, boolean caseSensitive) 333 { 334 if(!caseSensitive) 335 { 336 source = source.toLowerCase(); 337 target = target.toLowerCase(); 338 } 339 return (source.equals(target)); 340 } 341 342 /** 343 * Returns if <code>source</code> contains <code>target</code>, 344 * ignoring case, if <code>caseSensitive</code> is <code>false</code>. 345 * @param source the source String 346 * @param target the target String 347 * @param caseSensitive is the comparison case sensitive 348 * @return <code>true</code> if the strings matches 349 * <code>false</code> otherwise 350 */ 351 public static boolean matchesContains(String source, String target, boolean caseSensitive) 352 { 353 if(!caseSensitive) 354 { 355 source = source.toLowerCase(); 356 target = target.toLowerCase(); 357 } 358 return (-1 != source.indexOf(target)); 359 } 360 361 /** 362 * Returns if the regular expression <code>target</code> matches 363 * <code>source</code>, ignoring case, if <code>caseSensitive</code> 364 * is <code>false</code>. 365 * @param source the source String 366 * @param target the target String 367 * @param caseSensitive is the comparison case sensitive 368 * @return <code>true</code> if the strings matches 369 * <code>false</code> otherwise 370 */ 371 public static boolean matchesPerl5(String source, String target, boolean caseSensitive) 372 { 373 int mask = Perl5Compiler.CASE_INSENSITIVE_MASK; 374 if(caseSensitive) 375 { 376 mask = Perl5Compiler.DEFAULT_MASK; 377 } 378 try 379 { 380 Pattern pattern = new Perl5Compiler().compile(target, mask); 381 return (new Perl5Matcher().matches(source, pattern)); 382 } 383 catch(MalformedPatternException exc) 384 { 385 throw new NestedApplicationException(exc); 386 } 387 } 388 }