Merge branch 'fix-LANG-1171'
LANG-1171: Add compare methods in StringUtils This closes #110 from github.
This commit is contained in:
commit
849578d3a8
|
@ -22,6 +22,7 @@
|
|||
<body>
|
||||
|
||||
<release version="3.5" date="tba" description="tba">
|
||||
<action issue="LANG-1171" type="add" dev="lguibert">Add compare methods in StringUtils</action>
|
||||
<action issue="LANG-1174" type="add" dev="britter" due-to="Punkratz312">Add sugar to RandomUtils</action>
|
||||
<action issue="LANG-1057" type="update" dev="chas" due-to="Otávio Santana">Replace StringBuilder with String concatenation for better optimization</action>
|
||||
<action issue="LANG-1075" type="update" dev="chas">Deprecate SystemUtils.FILE_SEPARATOR and SystemUtils.PATH_SEPARATOR</action>
|
||||
|
|
|
@ -35,7 +35,7 @@ import java.util.regex.Pattern;
|
|||
* - checks if a String contains text</li>
|
||||
* <li><b>Trim/Strip</b>
|
||||
* - removes leading and trailing whitespace</li>
|
||||
* <li><b>Equals</b>
|
||||
* <li><b>Equals/Compare</b>
|
||||
* - compares two strings null-safe</li>
|
||||
* <li><b>startsWith</b>
|
||||
* - check if a String starts with a prefix null-safe</li>
|
||||
|
@ -830,6 +830,184 @@ public class StringUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// Compare
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Compare two Strings lexicographically, as per {@link String#compareTo(String)}, returning :</p>
|
||||
* <ul>
|
||||
* <li>{@code int = 0}, if {@code str1} is equal to {@code str2} (or both {@code null})</li>
|
||||
* <li>{@code int < 0}, if {@code str1} is less than {@code str2}</li>
|
||||
* <li>{@code int > 0}, if {@code str1} is greater than {@code str2}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This is a {@code null} safe version of :</p>
|
||||
* <blockquote><pre>str1.compareTo(str2)</pre></blockquote>
|
||||
*
|
||||
* <p>{@code null} value is considered less than non-{@code null} value.
|
||||
* Two {@code null} references are considered equal.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.compare(null, null) = 0
|
||||
* StringUtils.compare(null , "a") < 0
|
||||
* StringUtils.compare("a", null) > 0
|
||||
* StringUtils.compare("abc", "abc") = 0
|
||||
* StringUtils.compare("a", "b") < 0
|
||||
* StringUtils.compare("b", "a") > 0
|
||||
* StringUtils.compare("a", "B") > 0
|
||||
* StringUtils.compare("ab", "abc") < 0
|
||||
* </pre>
|
||||
*
|
||||
* @see #compare(String, String, boolean)
|
||||
* @see String#compareTo(String)
|
||||
* @param str1 the String to compare from
|
||||
* @param str2 the String to compare to
|
||||
* @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou greater than {@code str2}
|
||||
* @since 3.5
|
||||
*/
|
||||
public static int compare(final String str1, final String str2) {
|
||||
return compare(str1, str2, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Compare two Strings lexicographically, as per {@link String#compareTo(String)}, returning :</p>
|
||||
* <ul>
|
||||
* <li>{@code int = 0}, if {@code str1} is equal to {@code str2} (or both {@code null})</li>
|
||||
* <li>{@code int < 0}, if {@code str1} is less than {@code str2}</li>
|
||||
* <li>{@code int > 0}, if {@code str1} is greater than {@code str2}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This is a {@code null} safe version of :</p>
|
||||
* <blockquote><pre>str1.compareTo(str2)</pre></blockquote>
|
||||
*
|
||||
* <p>{@code null} inputs are handled according to the {@code nullIsLess} parameter.
|
||||
* Two {@code null} references are considered equal.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.compare(null, null, *) = 0
|
||||
* StringUtils.compare(null , "a", true) < 0
|
||||
* StringUtils.compare(null , "a", false) > 0
|
||||
* StringUtils.compare("a", null, true) > 0
|
||||
* StringUtils.compare("a", null, false) < 0
|
||||
* StringUtils.compare("abc", "abc", *) = 0
|
||||
* StringUtils.compare("a", "b", *) < 0
|
||||
* StringUtils.compare("b", "a", *) > 0
|
||||
* StringUtils.compare("a", "B", *) > 0
|
||||
* StringUtils.compare("ab", "abc", *) < 0
|
||||
* </pre>
|
||||
*
|
||||
* @see String#compareTo(String)
|
||||
* @param str1 the String to compare from
|
||||
* @param str2 the String to compare to
|
||||
* @param nullIsLess whether consider {@code null} value less than non-{@code null} value
|
||||
* @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou greater than {@code str2}
|
||||
* @since 3.5
|
||||
*/
|
||||
public static int compare(final String str1, final String str2, final boolean nullIsLess) {
|
||||
if (str1 == str2) {
|
||||
return 0;
|
||||
}
|
||||
if (str1 == null) {
|
||||
return nullIsLess ? -1 : 1;
|
||||
}
|
||||
if (str2 == null) {
|
||||
return nullIsLess ? 1 : - 1;
|
||||
}
|
||||
return str1.compareTo(str2);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Compare two Strings lexicographically, ignoring case differences,
|
||||
* as per {@link String#compareToIgnoreCase(String)}, returning :</p>
|
||||
* <ul>
|
||||
* <li>{@code int = 0}, if {@code str1} is equal to {@code str2} (or both {@code null})</li>
|
||||
* <li>{@code int < 0}, if {@code str1} is less than {@code str2}</li>
|
||||
* <li>{@code int > 0}, if {@code str1} is greater than {@code str2}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This is a {@code null} safe version of :</p>
|
||||
* <blockquote><pre>str1.compareToIgnoreCase(str2)</pre></blockquote>
|
||||
*
|
||||
* <p>{@code null} value is considered less than non-{@code null} value.
|
||||
* Two {@code null} references are considered equal.
|
||||
* Comparison is case insensitive.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.compareIgnoreCase(null, null) = 0
|
||||
* StringUtils.compareIgnoreCase(null , "a") < 0
|
||||
* StringUtils.compareIgnoreCase("a", null) > 0
|
||||
* StringUtils.compareIgnoreCase("abc", "abc") = 0
|
||||
* StringUtils.compareIgnoreCase("abc", "ABC") = 0
|
||||
* StringUtils.compareIgnoreCase("a", "b") < 0
|
||||
* StringUtils.compareIgnoreCase("b", "a") > 0
|
||||
* StringUtils.compareIgnoreCase("a", "B") < 0
|
||||
* StringUtils.compareIgnoreCase("A", "b") < 0
|
||||
* StringUtils.compareIgnoreCase("ab", "ABC") < 0
|
||||
* </pre>
|
||||
*
|
||||
* @see #compareIgnoreCase(String, String, boolean)
|
||||
* @see String#compareToIgnoreCase(String)
|
||||
* @param str1 the String to compare from
|
||||
* @param str2 the String to compare to
|
||||
* @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou greater than {@code str2},
|
||||
* ignoring case differences.
|
||||
* @since 3.5
|
||||
*/
|
||||
public static int compareIgnoreCase(final String str1, final String str2) {
|
||||
return compareIgnoreCase(str1, str2, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Compare two Strings lexicographically, ignoring case differences,
|
||||
* as per {@link String#compareToIgnoreCase(String)}, returning :</p>
|
||||
* <ul>
|
||||
* <li>{@code int = 0}, if {@code str1} is equal to {@code str2} (or both {@code null})</li>
|
||||
* <li>{@code int < 0}, if {@code str1} is less than {@code str2}</li>
|
||||
* <li>{@code int > 0}, if {@code str1} is greater than {@code str2}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This is a {@code null} safe version of :</p>
|
||||
* <blockquote><pre>str1.compareToIgnoreCase(str2)</pre></blockquote>
|
||||
*
|
||||
* <p>{@code null} inputs are handled according to the {@code nullIsLess} parameter.
|
||||
* Two {@code null} references are considered equal.
|
||||
* Comparison is case insensitive.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.compareIgnoreCase(null, null, *) = 0
|
||||
* StringUtils.compareIgnoreCase(null , "a", true) < 0
|
||||
* StringUtils.compareIgnoreCase(null , "a", false) > 0
|
||||
* StringUtils.compareIgnoreCase("a", null, true) > 0
|
||||
* StringUtils.compareIgnoreCase("a", null, false) < 0
|
||||
* StringUtils.compareIgnoreCase("abc", "abc", *) = 0
|
||||
* StringUtils.compareIgnoreCase("abc", "ABC", *) = 0
|
||||
* StringUtils.compareIgnoreCase("a", "b", *) < 0
|
||||
* StringUtils.compareIgnoreCase("b", "a", *) > 0
|
||||
* StringUtils.compareIgnoreCase("a", "B", *) < 0
|
||||
* StringUtils.compareIgnoreCase("A", "b", *) < 0
|
||||
* StringUtils.compareIgnoreCase("ab", "abc", *) < 0
|
||||
* </pre>
|
||||
*
|
||||
* @see String#compareToIgnoreCase(String)
|
||||
* @param str1 the String to compare from
|
||||
* @param str2 the String to compare to
|
||||
* @param nullIsLess whether consider {@code null} value less than non-{@code null} value
|
||||
* @return < 0, 0, > 0, if {@code str1} is respectively less, equal ou greater than {@code str2},
|
||||
* ignoring case differences.
|
||||
* @since 3.5
|
||||
*/
|
||||
public static int compareIgnoreCase(final String str1, final String str2, final boolean nullIsLess) {
|
||||
if (str1 == str2) {
|
||||
return 0;
|
||||
}
|
||||
if (str1 == null) {
|
||||
return nullIsLess ? -1 : 1;
|
||||
}
|
||||
if (str2 == null) {
|
||||
return nullIsLess ? 1 : - 1;
|
||||
}
|
||||
return str1.compareToIgnoreCase(str2);
|
||||
}
|
||||
|
||||
// IndexOf
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
|
|
|
@ -576,6 +576,75 @@ public class StringUtilsEqualsIndexOfTest {
|
|||
assertFalse(StringUtils.equalsIgnoreCase("abcd","abcd "));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@Test
|
||||
public void testCompare_StringString() {
|
||||
assertTrue(StringUtils.compare(null, null) == 0);
|
||||
assertTrue(StringUtils.compare(null, "a") < 0);
|
||||
assertTrue(StringUtils.compare("a", null) > 0);
|
||||
assertTrue(StringUtils.compare("abc", "abc") == 0);
|
||||
assertTrue(StringUtils.compare("a", "b") < 0);
|
||||
assertTrue(StringUtils.compare("b", "a") > 0);
|
||||
assertTrue(StringUtils.compare("a", "B") > 0);
|
||||
assertTrue(StringUtils.compare("abc", "abd") < 0);
|
||||
assertTrue(StringUtils.compare("ab", "abc") < 0);
|
||||
assertTrue(StringUtils.compare("ab", "ab ") < 0);
|
||||
assertTrue(StringUtils.compare("abc", "ab ") > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompare_StringStringBoolean() {
|
||||
assertTrue(StringUtils.compare(null, null, false) == 0);
|
||||
assertTrue(StringUtils.compare(null, "a", true) < 0);
|
||||
assertTrue(StringUtils.compare(null, "a", false) > 0);
|
||||
assertTrue(StringUtils.compare("a", null, true) > 0);
|
||||
assertTrue(StringUtils.compare("a", null, false) < 0);
|
||||
assertTrue(StringUtils.compare("abc", "abc", false) == 0);
|
||||
assertTrue(StringUtils.compare("a", "b", false) < 0);
|
||||
assertTrue(StringUtils.compare("b", "a", false) > 0);
|
||||
assertTrue(StringUtils.compare("a", "B", false) > 0);
|
||||
assertTrue(StringUtils.compare("abc", "abd", false) < 0);
|
||||
assertTrue(StringUtils.compare("ab", "abc", false) < 0);
|
||||
assertTrue(StringUtils.compare("ab", "ab ", false) < 0);
|
||||
assertTrue(StringUtils.compare("abc", "ab ", false) > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareIgnoreCase_StringString() {
|
||||
assertTrue(StringUtils.compareIgnoreCase(null, null) == 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase(null, "a") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", null) > 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "abc") == 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "ABC") == 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", "b") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("b", "a") > 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", "B") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("A", "b") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "ABD") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("ab", "ABC") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("ab", "AB ") < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "AB ") > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareIgnoreCase_StringStringBoolean() {
|
||||
assertTrue(StringUtils.compareIgnoreCase(null, null, false) == 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase(null, "a", true) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase(null, "a", false) > 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", null, true) > 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", null, false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "abc", false) == 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "ABC", false) == 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", "b", false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("b", "a", false) > 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("a", "B", false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("A", "b", false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "ABD", false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("ab", "ABC", false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("ab", "AB ", false) < 0);
|
||||
assertTrue(StringUtils.compareIgnoreCase("abc", "AB ", false) > 0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@Test
|
||||
public void testIndexOf_char() {
|
||||
|
|
|
@ -2373,22 +2373,35 @@ public class StringUtilsTest {
|
|||
@Test
|
||||
public void testStringUtilsCharSequenceContract() {
|
||||
final Class<StringUtils> c = StringUtils.class;
|
||||
// Methods that are expressly excluded from testStringUtilsCharSequenceContract()
|
||||
final String[] excludeMethods = {
|
||||
"public static int org.apache.commons.lang3.StringUtils.compare(java.lang.String,java.lang.String)",
|
||||
"public static int org.apache.commons.lang3.StringUtils.compare(java.lang.String,java.lang.String,boolean)",
|
||||
"public static int org.apache.commons.lang3.StringUtils.compareIgnoreCase(java.lang.String,java.lang.String)",
|
||||
"public static int org.apache.commons.lang3.StringUtils.compareIgnoreCase(java.lang.String,java.lang.String,boolean)"
|
||||
};
|
||||
final Method[] methods = c.getMethods();
|
||||
|
||||
for (final Method m : methods) {
|
||||
String methodStr = m.toString();
|
||||
if (m.getReturnType() == String.class || m.getReturnType() == String[].class) {
|
||||
// Assume this is mutable and ensure the first parameter is not CharSequence.
|
||||
// It may be String or it may be something else (String[], Object, Object[]) so
|
||||
// don't actively test for that.
|
||||
final Class<?>[] params = m.getParameterTypes();
|
||||
if (params.length > 0 && (params[0] == CharSequence.class || params[0] == CharSequence[].class)) {
|
||||
fail("The method " + m + " appears to be mutable in spirit and therefore must not accept a CharSequence");
|
||||
if (!ArrayUtils.contains(excludeMethods, methodStr)) {
|
||||
fail("The method \"" + methodStr + "\" appears to be mutable in spirit and therefore must not accept a CharSequence");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Assume this is immutable in spirit and ensure the first parameter is not String.
|
||||
// As above, it may be something other than CharSequence.
|
||||
final Class<?>[] params = m.getParameterTypes();
|
||||
if (params.length > 0 && (params[0] == String.class || params[0] == String[].class)) {
|
||||
fail("The method " + m + " appears to be immutable in spirit and therefore must not accept a String");
|
||||
if (!ArrayUtils.contains(excludeMethods, methodStr)) {
|
||||
fail("The method \"" + methodStr + "\" appears to be immutable in spirit and therefore must not accept a String");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue