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    }