-
Notifications
You must be signed in to change notification settings - Fork 24.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make boolean conversion strict #22200
Changes from 2 commits
b5d642b
e705d96
9fb7ea1
db3fd5e
c1b9df1
cbb3822
c09b301
665554d
1ee2db4
f7337ba
206adfa
fcfce6c
8de2eb4
6144f36
22aabf2
978f047
33af9f1
55b7e2b
0577acc
4c90110
f4a2f64
0f91b13
61f3912
bdc029e
fc8f39a
4523e55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,34 +19,28 @@ | |
|
||
package org.elasticsearch.common; | ||
|
||
public class Booleans { | ||
public final class Booleans { | ||
private Booleans() { | ||
throw new AssertionError("No instances intended"); | ||
} | ||
|
||
/** | ||
* Returns <code>false</code> if text is in <tt>false</tt>, <tt>0</tt>, <tt>off</tt>, <tt>no</tt>; else, true | ||
* Parses a char[] representation of a boolean value to <code>boolean</code>. | ||
* | ||
* @return <code>true</code> iff the sequence of chars is "true", <code>false</code> iff the sequence of chars is "false" or the | ||
* provided default value iff either text is <code>null</code> or length == 0. | ||
* @throws IllegalArgumentException if the string cannot be parsed to boolean. | ||
*/ | ||
public static boolean parseBoolean(char[] text, int offset, int length, boolean defaultValue) { | ||
// TODO: the leniency here is very dangerous: a simple typo will be misinterpreted and the user won't know. | ||
// We should remove it and cutover to https://github.com/rmuir/booleanparser | ||
if (text == null || length == 0) { | ||
return defaultValue; | ||
} else { | ||
return parseBoolean(new String(text, offset, length)); | ||
} | ||
if (length == 1) { | ||
return text[offset] != '0'; | ||
} | ||
if (length == 2) { | ||
return !(text[offset] == 'n' && text[offset + 1] == 'o'); | ||
} | ||
if (length == 3) { | ||
return !(text[offset] == 'o' && text[offset + 1] == 'f' && text[offset + 2] == 'f'); | ||
} | ||
if (length == 5) { | ||
return !(text[offset] == 'f' && text[offset + 1] == 'a' && text[offset + 2] == 'l' && text[offset + 3] == 's' && text[offset + 4] == 'e'); | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* returns true if the a sequence of chars is one of "true","false","on","off","yes","no","0","1" | ||
* returns true iff the sequence of chars is one of "true","false". | ||
* | ||
* @param text sequence to check | ||
* @param offset offset to start | ||
|
@@ -56,77 +50,61 @@ public static boolean isBoolean(char[] text, int offset, int length) { | |
if (text == null || length == 0) { | ||
return false; | ||
} | ||
if (length == 1) { | ||
return text[offset] == '0' || text[offset] == '1'; | ||
} | ||
if (length == 2) { | ||
return (text[offset] == 'n' && text[offset + 1] == 'o') || (text[offset] == 'o' && text[offset + 1] == 'n'); | ||
} | ||
if (length == 3) { | ||
return (text[offset] == 'o' && text[offset + 1] == 'f' && text[offset + 2] == 'f') || | ||
(text[offset] == 'y' && text[offset + 1] == 'e' && text[offset + 2] == 's'); | ||
} | ||
if (length == 4) { | ||
return (text[offset] == 't' && text[offset + 1] == 'r' && text[offset + 2] == 'u' && text[offset + 3] == 'e'); | ||
} | ||
if (length == 5) { | ||
return (text[offset] == 'f' && text[offset + 1] == 'a' && text[offset + 2] == 'l' && text[offset + 3] == 's' && text[offset + 4] == 'e'); | ||
} | ||
return false; | ||
return isBoolean(new String(text, offset, length)); | ||
} | ||
|
||
public static boolean isBoolean(String value) { | ||
return isExplicitFalse(value) || isExplicitTrue(value); | ||
} | ||
|
||
/*** | ||
/** | ||
* Parses a string representation of a boolean value to <code>boolean</code>. | ||
* | ||
* @return true/false | ||
* throws exception if string cannot be parsed to boolean | ||
* @return <code>true</code> iff the provided value is "true". <code>false</code> iff the provided value is "false". | ||
* @throws IllegalArgumentException if the string cannot be parsed to boolean. | ||
*/ | ||
public static Boolean parseBooleanExact(String value) { | ||
boolean isFalse = isExplicitFalse(value); | ||
if (isFalse) { | ||
public static boolean parseBoolean(String value) { | ||
if (isExplicitFalse(value)) { | ||
return false; | ||
} | ||
boolean isTrue = isExplicitTrue(value); | ||
if (isTrue) { | ||
if (isExplicitTrue(value)) { | ||
return true; | ||
} | ||
|
||
throw new IllegalArgumentException("Failed to parse value [" + value + "] cannot be parsed to boolean [ true/1/on/yes OR false/0/off/no ]"); | ||
throw new IllegalArgumentException("Failed to parse value [" + value + "] as boolean (only 'true' and 'false' are allowed)"); | ||
} | ||
|
||
public static Boolean parseBoolean(String value, Boolean defaultValue) { | ||
if (value == null) { // only for the null case we do that here! | ||
return defaultValue; | ||
} | ||
return parseBoolean(value, false); | ||
} | ||
/** | ||
* Returns <code>true</code> iff the value is neither of the following: | ||
* <tt>false</tt>, <tt>0</tt>, <tt>off</tt>, <tt>no</tt> | ||
* otherwise <code>false</code> | ||
* | ||
* @param value text to parse. | ||
* @param defaultValue The default value to return if the provided value is <code>null</code>. | ||
* @return see {@link #parseBoolean(String)} | ||
*/ | ||
public static boolean parseBoolean(String value, boolean defaultValue) { | ||
if (value == null) { | ||
if (value == null || value.length() == 0) { | ||
return defaultValue; | ||
} | ||
return parseBoolean(value); | ||
} | ||
|
||
public static Boolean parseBoolean(String value, Boolean defaultValue) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a case where we need a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, there are some places indeed. For example, we use it when parsing REST request parameters in |
||
if (value == null || value.length() == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've used |
||
return defaultValue; | ||
} | ||
return !(value.equals("false") || value.equals("0") || value.equals("off") || value.equals("no")); | ||
return parseBoolean(value); | ||
} | ||
|
||
/** | ||
* Returns <code>true</code> iff the value is either of the following: | ||
* <tt>false</tt>, <tt>0</tt>, <tt>off</tt>, <tt>no</tt> | ||
* otherwise <code>false</code> | ||
* @return <code>true</code> iff the value is <tt>false</tt>, otherwise <code>false</code>. | ||
*/ | ||
public static boolean isExplicitFalse(String value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it could be renamed to isFalse() / isTrue() now |
||
return value != null && (value.equals("false") || value.equals("0") || value.equals("off") || value.equals("no")); | ||
return value != null && value.equals("false"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
/** | ||
* Returns <code>true</code> iff the value is either of the following: | ||
* <tt>true</tt>, <tt>1</tt>, <tt>on</tt>, <tt>yes</tt> | ||
* otherwise <code>false</code> | ||
* @return <code>true</code> iff the value is <tt>true</tt>, otherwise <code>false</code> | ||
*/ | ||
public static boolean isExplicitTrue(String value) { | ||
return value != null && (value.equals("true") || value.equals("1") || value.equals("on") || value.equals("yes")); | ||
return value != null && value.equals("true"); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -196,7 +196,13 @@ public long paramAsLong(String key, long defaultValue) { | |
|
||
@Override | ||
public boolean paramAsBoolean(String key, boolean defaultValue) { | ||
return Booleans.parseBoolean(param(key), defaultValue); | ||
String rawParam = param(key); | ||
// treat the sheer presence of a parameter as "true" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe "Treat empty string as |
||
if (rawParam != null && rawParam.length() == 0) { | ||
return true; | ||
} else { | ||
return Booleans.parseBoolean(rawParam, defaultValue); | ||
} | ||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,6 @@ | |
package org.elasticsearch.common; | ||
|
||
import org.elasticsearch.test.ESTestCase; | ||
import org.hamcrest.Matchers; | ||
|
||
import java.util.Locale; | ||
|
||
|
@@ -29,58 +28,58 @@ | |
|
||
public class BooleansTests extends ESTestCase { | ||
public void testIsBoolean() { | ||
String[] booleans = new String[]{"true", "false", "on", "off", "yes", "no", "0", "1"}; | ||
String[] notBooleans = new String[]{"11", "00", "sdfsdfsf", "F", "T"}; | ||
String[] booleans = new String[]{"true", "false"}; | ||
String[] notBooleans = new String[]{"11", "00", "sdfsdfsf", "F", "T", "on", "off", "yes", "no", "0", "1"}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +++++++++++ |
||
assertThat(Booleans.isBoolean(null, 0, 1), is(false)); | ||
|
||
for (String b : booleans) { | ||
String t = "prefix" + b + "suffix"; | ||
assertThat("failed to recognize [" + b + "] as boolean", Booleans.isBoolean(t.toCharArray(), "prefix".length(), b.length()), Matchers.equalTo(true)); | ||
assertThat("failed to recognize [" + b + "] as boolean", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Booleans.isBoolean(t.toCharArray(), "prefix".length(), b.length()), is(true)); | ||
} | ||
|
||
for (String nb : notBooleans) { | ||
String t = "prefix" + nb + "suffix"; | ||
assertThat("recognized [" + nb + "] as boolean", Booleans.isBoolean(t.toCharArray(), "prefix".length(), nb.length()), Matchers.equalTo(false)); | ||
assertThat("recognized [" + nb + "] as boolean", | ||
Booleans.isBoolean(t.toCharArray(), "prefix".length(), nb.length()), is(false)); | ||
} | ||
} | ||
|
||
public void testParseBoolean() { | ||
assertThat(Booleans.parseBoolean(randomFrom("true", "on", "yes", "1"), randomBoolean()), is(true)); | ||
assertThat(Booleans.parseBoolean(randomFrom("false", "off", "no", "0"), randomBoolean()), is(false)); | ||
assertThat(Booleans.parseBoolean(randomFrom("true", "on", "yes").toUpperCase(Locale.ROOT), randomBoolean()), is(true)); | ||
assertThat(Booleans.parseBoolean(null, false), is(false)); | ||
assertThat(Booleans.parseBoolean(null, true), is(true)); | ||
|
||
assertThat(Booleans.parseBoolean(randomFrom("true", "on", "yes", "1"), randomFrom(Boolean.TRUE, Boolean.FALSE, null)), is(true)); | ||
assertThat(Booleans.parseBoolean(randomFrom("false", "off", "no", "0"), randomFrom(Boolean.TRUE, Boolean.FALSE, null)), is(false)); | ||
assertThat(Booleans.parseBoolean(randomFrom("true", "on", "yes").toUpperCase(Locale.ROOT),randomFrom(Boolean.TRUE, Boolean.FALSE, null)), is(true)); | ||
assertThat(Booleans.parseBoolean("true", randomFrom(Boolean.TRUE, Boolean.FALSE, null)), is(true)); | ||
assertThat(Booleans.parseBoolean("false", randomFrom(Boolean.TRUE, Boolean.FALSE, null)), is(false)); | ||
expectThrows(IllegalArgumentException.class, | ||
() -> Booleans.parseBoolean("true".toUpperCase(Locale.ROOT),randomFrom(Boolean.TRUE, Boolean.FALSE, null))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/ |
||
assertThat(Booleans.parseBoolean(null, Boolean.FALSE), is(false)); | ||
assertThat(Booleans.parseBoolean(null, Boolean.TRUE), is(true)); | ||
assertThat(Booleans.parseBoolean(null, null), nullValue()); | ||
|
||
char[] chars = randomFrom("true", "on", "yes", "1").toCharArray(); | ||
char[] chars = "true".toCharArray(); | ||
assertThat(Booleans.parseBoolean(chars, 0, chars.length, randomBoolean()), is(true)); | ||
chars = randomFrom("false", "off", "no", "0").toCharArray(); | ||
chars = "false".toCharArray(); | ||
assertThat(Booleans.parseBoolean(chars,0, chars.length, randomBoolean()), is(false)); | ||
chars = randomFrom("true", "on", "yes").toUpperCase(Locale.ROOT).toCharArray(); | ||
assertThat(Booleans.parseBoolean(chars,0, chars.length, randomBoolean()), is(true)); | ||
final char[] TRUE = "true".toUpperCase(Locale.ROOT).toCharArray(); | ||
expectThrows(IllegalArgumentException.class, () -> Booleans.parseBoolean(TRUE, 0, TRUE.length, randomBoolean())); | ||
} | ||
|
||
public void testParseBooleanExact() { | ||
assertThat(Booleans.parseBooleanExact(randomFrom("true", "on", "yes", "1")), is(true)); | ||
assertThat(Booleans.parseBooleanExact(randomFrom("false", "off", "no", "0")), is(false)); | ||
assertThat(Booleans.parseBoolean("true"), is(true)); | ||
assertThat(Booleans.parseBoolean("false"), is(false)); | ||
try { | ||
Booleans.parseBooleanExact(randomFrom("fred", "foo", "barney", null)); | ||
fail("Expected exception while parsing invalid boolean value "); | ||
Booleans.parseBoolean(randomFrom("fred", "foo", "barney", null, "on", "yes", "1", "off", "no", "0", "True", "False")); | ||
fail("Expected exception while parsing invalid boolean value"); | ||
} catch (Exception ex) { | ||
assertTrue(ex instanceof IllegalArgumentException); | ||
} | ||
} | ||
|
||
public void testIsExplicit() { | ||
assertThat(Booleans.isExplicitFalse(randomFrom("true", "on", "yes", "1", "foo", null)), is(false)); | ||
assertThat(Booleans.isExplicitFalse(randomFrom("false", "off", "no", "0")), is(true)); | ||
assertThat(Booleans.isExplicitTrue(randomFrom("true", "on", "yes", "1")), is(true)); | ||
assertThat(Booleans.isExplicitTrue(randomFrom("false", "off", "no", "0", "foo", null)), is(false)); | ||
assertThat(Booleans.isExplicitFalse(randomFrom("true", "on", "yes", "1", "foo", null, "False")), is(false)); | ||
assertThat(Booleans.isExplicitFalse("false"), is(true)); | ||
assertThat(Booleans.isExplicitTrue("true"), is(true)); | ||
assertThat(Booleans.isExplicitTrue(randomFrom("false", "off", "no", "0", "foo", null, "True")), is(false)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
"Failed to parse value [" + value + "] as only [true] or [false] are allowed."
is a easier to read.