Skip to content

Commit 5c13fa7

Browse files
author
Niall Pemberton
committed
IO-167 Fix case-insensitive string handling - thanks to Benjamin Bentmann
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/io/trunk@661822 13f79535-47bb-0310-9956-ffa450edef68
1 parent d71a8b3 commit 5c13fa7

4 files changed

Lines changed: 124 additions & 19 deletions

File tree

src/java/org/apache/commons/io/FilenameUtils.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,8 +1146,6 @@ public static boolean wildcardMatch(String filename, String wildcardMatcher, IOC
11461146
if (caseSensitivity == null) {
11471147
caseSensitivity = IOCase.SENSITIVE;
11481148
}
1149-
filename = caseSensitivity.convertCase(filename);
1150-
wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);
11511149
String[] wcs = splitOnTokens(wildcardMatcher);
11521150
boolean anyChars = false;
11531151
int textIdx = 0;
@@ -1182,18 +1180,18 @@ public static boolean wildcardMatch(String filename, String wildcardMatcher, IOC
11821180
// matching text token
11831181
if (anyChars) {
11841182
// any chars then try to locate text token
1185-
textIdx = filename.indexOf(wcs[wcsIdx], textIdx);
1183+
textIdx = caseSensitivity.checkIndexOf(filename, textIdx, wcs[wcsIdx]);
11861184
if (textIdx == -1) {
11871185
// token not found
11881186
break;
11891187
}
1190-
int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);
1188+
int repeat = caseSensitivity.checkIndexOf(filename, textIdx + 1, wcs[wcsIdx]);
11911189
if (repeat >= 0) {
11921190
backtrack.push(new int[] {wcsIdx, repeat});
11931191
}
11941192
} else {
11951193
// matching from current position
1196-
if (!filename.startsWith(wcs[wcsIdx], textIdx)) {
1194+
if (!caseSensitivity.checkRegionMatches(filename, textIdx, wcs[wcsIdx])) {
11971195
// couldnt match token
11981196
break;
11991197
}

src/java/org/apache/commons/io/IOCase.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,33 +196,46 @@ public boolean checkEndsWith(String str, String end) {
196196
}
197197

198198
/**
199-
* Checks if one string contains another at a specific index using the case-sensitivity rule.
199+
* Checks if one string contains another starting at a specific index using the
200+
* case-sensitivity rule.
200201
* <p>
201-
* This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)}
202+
* This method mimics parts of {@link String#indexOf(String, int)}
202203
* but takes case-sensitivity into account.
203204
*
204205
* @param str the string to check, not null
205206
* @param strStartIndex the index to start at in str
206207
* @param search the start to search for, not null
207-
* @return true if equal using the case rules
208+
* @return the first index of the search String,
209+
* -1 if no match or <code>null</code> string input
208210
* @throws NullPointerException if either string is null
211+
* @since 2.0
209212
*/
210-
public boolean checkRegionMatches(String str, int strStartIndex, String search) {
211-
return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
213+
public int checkIndexOf(String str, int strStartIndex, String search) {
214+
int endIndex = str.length() - search.length();
215+
if (endIndex >= strStartIndex) {
216+
for (int i = strStartIndex; i <= endIndex; i++) {
217+
if (checkRegionMatches(str, i, search)) {
218+
return i;
219+
}
220+
}
221+
}
222+
return -1;
212223
}
213224

214225
/**
215-
* Converts the case of the input String to a standard format.
216-
* Subsequent operations can then use standard String methods.
226+
* Checks if one string contains another at a specific index using the case-sensitivity rule.
227+
* <p>
228+
* This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)}
229+
* but takes case-sensitivity into account.
217230
*
218-
* @param str the string to convert, null returns null
219-
* @return the lower-case version if case-insensitive
231+
* @param str the string to check, not null
232+
* @param strStartIndex the index to start at in str
233+
* @param search the start to search for, not null
234+
* @return true if equal using the case rules
235+
* @throws NullPointerException if either string is null
220236
*/
221-
String convertCase(String str) {
222-
if (str == null) {
223-
return null;
224-
}
225-
return sensitive ? str : str.toLowerCase();
237+
public boolean checkRegionMatches(String str, int strStartIndex, String search) {
238+
return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
226239
}
227240

228241
//-----------------------------------------------------------------------

src/test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.commons.io;
1818

1919
import java.io.File;
20+
import java.util.Locale;
2021

2122
import junit.framework.TestCase;
2223

@@ -181,4 +182,33 @@ public void testMatch2() {
181182
assertMatch("log.log.abc.log.abc.d", "*log?abc?d", true);
182183
}
183184

185+
public void testLocaleIndependence() {
186+
Locale orig = Locale.getDefault();
187+
188+
Locale[] locales = Locale.getAvailableLocales();
189+
190+
String[][] data = {
191+
{ "I", "i"},
192+
{ "i", "I"},
193+
{ "i", "\u0130"},
194+
{ "i", "\u0131"},
195+
{ "\u03A3", "\u03C2"},
196+
{ "\u03A3", "\u03C3"},
197+
{ "\u03C2", "\u03C3"},
198+
};
199+
200+
try {
201+
for (int i = 0; i < data.length; i++) {
202+
for (int j = 0; j < locales.length; j++) {
203+
Locale.setDefault(locales[j]);
204+
assertTrue("Test data corrupt: " + i, data[i][0].equalsIgnoreCase(data[i][1]));
205+
boolean match = FilenameUtils.wildcardMatch(data[i][0], data[i][1], IOCase.INSENSITIVE);
206+
assertTrue(Locale.getDefault().toString() + ": " + i, match);
207+
}
208+
}
209+
} finally {
210+
Locale.setDefault(orig);
211+
}
212+
}
213+
184214
}

src/test/org/apache/commons/io/IOCaseTestCase.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,70 @@ public void test_checkEndsWith_case() throws Exception {
245245
assertEquals(WINDOWS, IOCase.SYSTEM.checkEndsWith("ABC", "Bc"));
246246
}
247247

248+
//-----------------------------------------------------------------------
249+
public void test_checkIndexOf_functionality() throws Exception {
250+
251+
// start
252+
assertEquals(0, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "A"));
253+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 1, "A"));
254+
assertEquals(0, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "AB"));
255+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 1, "AB"));
256+
assertEquals(0, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "ABC"));
257+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 1, "ABC"));
258+
259+
// middle
260+
assertEquals(3, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "D"));
261+
assertEquals(3, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 3, "D"));
262+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 4, "D"));
263+
assertEquals(3, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "DE"));
264+
assertEquals(3, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 3, "DE"));
265+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 4, "DE"));
266+
assertEquals(3, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "DEF"));
267+
assertEquals(3, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 3, "DEF"));
268+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 4, "DEF"));
269+
270+
// end
271+
assertEquals(9, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "J"));
272+
assertEquals(9, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 8, "J"));
273+
assertEquals(9, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 9, "J"));
274+
assertEquals(8, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "IJ"));
275+
assertEquals(8, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 8, "IJ"));
276+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 9, "IJ"));
277+
assertEquals(7, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 6, "HIJ"));
278+
assertEquals(7, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 7, "HIJ"));
279+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 8, "HIJ"));
280+
281+
// not found
282+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABCDEFGHIJ", 0, "DED"));
283+
284+
// too long
285+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("DEF", 0, "ABCDEFGHIJ"));
286+
287+
try {
288+
IOCase.SENSITIVE.checkIndexOf("ABC", 0, null);
289+
fail();
290+
} catch (NullPointerException ex) {}
291+
try {
292+
IOCase.SENSITIVE.checkIndexOf(null, 0, "ABC");
293+
fail();
294+
} catch (NullPointerException ex) {}
295+
try {
296+
IOCase.SENSITIVE.checkIndexOf(null, 0, null);
297+
fail();
298+
} catch (NullPointerException ex) {}
299+
}
300+
301+
public void test_checkIndexOf_case() throws Exception {
302+
assertEquals(1, IOCase.SENSITIVE.checkIndexOf("ABC", 0, "BC"));
303+
assertEquals(-1, IOCase.SENSITIVE.checkIndexOf("ABC", 0, "Bc"));
304+
305+
assertEquals(1, IOCase.INSENSITIVE.checkIndexOf("ABC", 0, "BC"));
306+
assertEquals(1, IOCase.INSENSITIVE.checkIndexOf("ABC", 0, "Bc"));
307+
308+
assertEquals(1, IOCase.SYSTEM.checkIndexOf("ABC", 0, "BC"));
309+
assertEquals(WINDOWS ? 1 : -1, IOCase.SYSTEM.checkIndexOf("ABC", 0, "Bc"));
310+
}
311+
248312
//-----------------------------------------------------------------------
249313
public void test_checkRegionMatches_functionality() throws Exception {
250314
assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, ""));

0 commit comments

Comments
 (0)