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 }