From f76deb682c0fe2c7ae0bb2234036ca37fc94dbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Mela?= Date: Tue, 10 Jul 2018 20:58:41 +0200 Subject: [PATCH] Fixed support for keys that look like numbers longer than an int. Previously such config keys would still be parsed with `Integer#parseInt`, resulting in a `NumberFormatException`. Now the number-like keys are parsed to a Long (no measures made but performance expected hit expected to be negligible) if their numbers of digits is shorter than the number of digits in `Long.MAX_VALUE`. One could have also tried to parse numbers exactly up to `Long.MAX_VALUE`, but added usability doesn't seem to match the required complexity - in fact, one could also argue that the current loop-based checking if a key looks like a number is a bit of an overkill, but I didn't want to dig into the original reason for why a regexp was not used, instead focusing on fixing an apparent bug in the most straightforward way. --- .../config/impl/SimpleConfigObject.java | 22 +++++----- config/src/test/resources/test12.conf | 43 +++++++++++++++++++ config/src/test/scala/Rendering.scala | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 config/src/test/resources/test12.conf diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java b/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java index cf5f5ea07..08f5e6011 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java @@ -421,20 +421,20 @@ public AbstractConfigValue modifyChild(String key, AbstractConfigValue v) { // this is only Serializable to chill out a findbugs warning static final private class RenderComparator implements java.util.Comparator, Serializable { private static final long serialVersionUID = 1L; + private static final int MAX_DIGITS_IN_LONG = Long.toString(Long.MAX_VALUE).length(); - private static boolean isAllDigits(String s) { + private static boolean safeToParseAsLong(String s) { int length = s.length(); // empty string doesn't count as a number - if (length == 0) + // string longer than "max number of digits in a long" cannot be parsed as a long + if (length == 0 || length >= MAX_DIGITS_IN_LONG) return false; for (int i = 0; i < length; ++i) { char c = s.charAt(i); - if (Character.isDigit(c)) - continue; - else + if (!Character.isDigit(c)) return false; } return true; @@ -446,13 +446,13 @@ private static boolean isAllDigits(String s) { // (numeric indices) appear in order. @Override public int compare(String a, String b) { - boolean aDigits = isAllDigits(a); - boolean bDigits = isAllDigits(b); - if (aDigits && bDigits) { - return Integer.compare(Integer.parseInt(a), Integer.parseInt(b)); - } else if (aDigits) { + boolean aParsable = safeToParseAsLong(a); + boolean bParsable = safeToParseAsLong(b); + if (aParsable && bParsable) { + return Long.compare(Long.parseLong(a), Long.parseLong(b)); + } else if (aParsable) { return -1; - } else if (bDigits) { + } else if (bParsable) { return 1; } else { return a.compareTo(b); diff --git a/config/src/test/resources/test12.conf b/config/src/test/resources/test12.conf new file mode 100644 index 000000000..b7aff622b --- /dev/null +++ b/config/src/test/resources/test12.conf @@ -0,0 +1,43 @@ +// this checks sorting map keys, where keys that look like numbers are treated differently +// specifically tests very long numbers which fit neither in an Integer nor in a Long + +"10" = "42" +sth = 42 +"1" = "42" +"12" = "42" +"123" = "42" +"1234" = "42" +"12345" = "42" +"123456" = "42" +"1234567" = "42" +"12345678" = "42" +"123456789" = "42" +"1234567890" = "42" +"12345678901" = "42" +"123456789012" = "42" +"1234567890123" = "42" +"12345678901234" = "42" +"123456789012345" = "42" +"1234567890123456" = "42" +"12345678901234567" = "42" +"123456789012345678" = "42" +"1234567890123456789" = "42" +"12345678901234567891" = "42" +"123456789012345678912" = "42" +"1234567890123456789123" = "42" +"12345678901234567891234" = "42" +"123456789012345678912345" = "42" +"1234567890123456789123456" = "42" +"12345678901234567891234567" = "42" +"123456789012345678912345678" = "42" +"1234567890123456789123456789" = "42" +"12345678901234567891234567890" = "42" +"123456789012345678912345678901" = "42" +"1234567890123456789123456789012" = "42" +"12345678901234567891234567890123" = "42" +"123456789012345678912345678901234" = "42" +"1234567890123456789123456789012345" = "42" +"12345678901234567891234567890123456" = "42" +"123456789012345678912345678901234567" = "42" +"1234567890123456789123456789012345678" = "42" +"12345678901234567891234567890123456789" = "42" \ No newline at end of file diff --git a/config/src/test/scala/Rendering.scala b/config/src/test/scala/Rendering.scala index 0d8be469e..e1ca3cf6d 100644 --- a/config/src/test/scala/Rendering.scala +++ b/config/src/test/scala/Rendering.scala @@ -33,6 +33,7 @@ object RenderExample extends App { render("test01") render("test06") render("test05") + render("test12") } object RenderOptions extends App {