001    package com.mockrunner.util.common;
002    
003    import java.util.ArrayList;
004    import java.util.List;
005    
006    import org.apache.commons.logging.Log;
007    import org.apache.commons.logging.LogFactory;
008    import org.apache.oro.text.regex.MalformedPatternException;
009    import org.apache.oro.text.regex.Pattern;
010    import org.apache.oro.text.regex.Perl5Compiler;
011    import org.apache.oro.text.regex.Perl5Matcher;
012    
013    import com.mockrunner.base.NestedApplicationException;
014    
015    /**
016     * Simple util class for <code>String</code> related methods.
017     */
018    public class StringUtil
019    {
020        private final static Log log = LogFactory.getLog(StringUtil.class);
021        
022        /**
023         * Compares two strings and returns the last
024         * index where the two string are equal. If
025         * the first characters of the two string do
026         * not match or if at least one of the two strings
027         * is empty, -1 is returned.
028         * @param string1 the first string
029         * @param string2 the second string
030         * @return the last index where the strings are equal
031         */
032        public static int compare(String string1, String string2)
033        {
034            int endIndex = Math.min(string1.length(), string2.length());
035            for(int ii = 0; ii < endIndex; ii++)
036            {
037                if(string1.charAt(ii) != string2.charAt(ii)) return ii - 1;
038            }
039            return endIndex - 1;
040        }
041        
042        /**
043         * Converts the character at the specified index to
044         * lowercase and returns the resulting string.
045         * @param string the string to convert
046         * @param index the index where the character is set to lowercase
047         * @return the converted string
048         * @throws IndexOutOfBoundsException if the index is out of
049         *         range
050         */
051        public static String lowerCase(String string, int index)
052        {
053            return lowerCase(string, index, -1);
054        }
055        
056        /**
057         * Converts the character in the specified index range to
058         * lowercase and returns the resulting string.
059         * If the provided endIndex is smaller or equal to startIndex,
060         * the endIndex is set to startIndex + 1.
061         * @param string the string to convert
062         * @param startIndex the index to start, inclusive
063         * @param endIndex the index to end, exclusive
064         * @return the converted string
065         * @throws IndexOutOfBoundsException if the index is out of
066         *         range
067         */
068        public static String lowerCase(String string, int startIndex, int endIndex)
069        {
070            StringBuffer buffer = new StringBuffer(string);
071            if(endIndex <= startIndex) endIndex = startIndex + 1;
072            for(int ii = startIndex; ii < endIndex; ii++)
073            {
074                char character = buffer.charAt(ii);
075                buffer.setCharAt(ii, Character.toLowerCase(character));
076            }
077            return buffer.toString();
078        }
079        
080        /**
081         * Appends the entries in the specified <code>List</code> as strings
082         * with a terminating <i>"\n"</i> after each row.
083         * @param buffer the buffer
084         * @param data the <code>List</code> with the data
085         */
086        public static void appendObjectsAsString(StringBuffer buffer, List data)
087        {
088            for(int ii = 0; ii < data.size(); ii++)
089            {
090                buffer.append(data.get(ii));
091                buffer.append("\n");
092            }
093        }
094        
095        /**
096         * Appends <i>number</i> tabs (\t) to the buffer.
097         * @param buffer the buffer
098         * @param number the number of tabs to append
099         */
100        public static void appendTabs(StringBuffer buffer, int number)
101        {
102            for(int ii = 0; ii < number; ii++)
103            {
104                buffer.append("\t");
105            }
106        }
107        
108        /**
109         * Splits a string into tokens. Similar to <code>StringTokenizer</code>
110         * except that empty tokens are recognized and added as <code>null</code>.
111         * With a delimiter of <i>";"</i> the string
112         * <i>"a;;b;c;;"</i> will split into
113         * <i>["a"] [null] ["b"] ["c"] [null]</i>.
114         * @param string the String
115         * @param delim the delimiter
116         * @param doTrim should each token be trimmed
117         * @return the array of tokens
118         */
119        public static String[] split(String string, String delim, boolean doTrim)
120        {
121            int pos = 0, begin = 0;
122            ArrayList resultList = new ArrayList();
123            while((-1 != (pos = string.indexOf(delim, begin))) && (begin < string.length()))
124            {
125                String token = string.substring(begin, pos);
126                if(doTrim) token = token.trim();
127                if(token.length() == 0) token = null;
128                resultList.add(token);
129                begin = pos + delim.length();
130            }
131            if(begin < string.length())
132            {
133                String token = string.substring(begin);
134                if(doTrim) token = token.trim();
135                if(token.length() == 0) token = null;
136                resultList.add(token);
137            }  
138            return (String[])resultList.toArray(new String[resultList.size()]);
139        }
140        
141        /**
142         * Returns how many times <code>string</code> contains
143         * <code>other</coder>.
144         * @param string the string to search
145         * @param other the string that is searched
146         * @return the number of occurences
147         */
148        public static int countMatches(String string, String other) 
149        {
150            if(null == string) return 0;
151            if(null == other) return 0;
152            if(0 >= string.length()) return 0;
153            if(0 >= other.length()) return 0;
154            int count = 0;
155            int index = 0;
156            while((index <= string.length() - other.length()) && (-1 != (index = string.indexOf(other, index))))
157            {
158                count++;
159                index += other.length();
160            }
161            return count;
162        }
163    
164        
165        /**
166         * Returns if the specified strings are equal, ignoring
167         * case, if <code>caseSensitive</code> is <code>false</code>.
168         * @param source the source String
169         * @param target the target String
170         * @param caseSensitive is the comparison case sensitive
171         * @return <code>true</code> if the strings matches
172         *         <code>false</code> otherwise
173         */
174        public static boolean matchesExact(String source, String target, boolean caseSensitive)
175        {
176            if(!caseSensitive)
177            {
178                source = source.toLowerCase();
179                target = target.toLowerCase();
180            }
181            return (source.equals(target));
182        }
183        
184        /**
185         * Returns if <code>source</code> contains <code>target</code>, 
186         * ignoring case, if <code>caseSensitive</code> is <code>false</code>.
187         * @param source the source String
188         * @param target the target String
189         * @param caseSensitive is the comparison case sensitive
190         * @return <code>true</code> if the strings matches
191         *         <code>false</code> otherwise
192         */
193        public static boolean matchesContains(String source, String target, boolean caseSensitive)
194        {
195            if(!caseSensitive)
196            {
197                source = source.toLowerCase();
198                target = target.toLowerCase();
199            }
200            return (-1 != source.indexOf(target));
201        }
202        
203        /**
204         * Returns if the regular expression <code>target</code> matches 
205         * <code>source</code>, ignoring case, if <code>caseSensitive</code> 
206         * is <code>false</code>.
207         * @param source the source String
208         * @param target the target String
209         * @param caseSensitive is the comparison case sensitive
210         * @return <code>true</code> if the strings matches
211         *         <code>false</code> otherwise
212         */
213        public static boolean matchesPerl5(String source, String target, boolean caseSensitive)
214        {
215            int mask = Perl5Compiler.CASE_INSENSITIVE_MASK;
216            if(caseSensitive)
217            {
218                mask = Perl5Compiler.DEFAULT_MASK;
219            }
220            try
221            {
222                Pattern pattern = new Perl5Compiler().compile(target, mask);
223                return (new Perl5Matcher().matches(source, pattern));
224            } 
225            catch(MalformedPatternException exc)
226            {
227                log.error("Malformed pattern", exc);
228                throw new NestedApplicationException(exc);
229            }
230        }
231    }