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