diff --git a/src/main/java/com/typesafe/config/Config.java b/src/main/java/com/typesafe/config/Config.java index 6a5a2bb5..b5763a0a 100644 --- a/src/main/java/com/typesafe/config/Config.java +++ b/src/main/java/com/typesafe/config/Config.java @@ -27,6 +27,8 @@ public final class Config { /** * Parses a duration string. If no units are specified in the string, it is * assumed to be in milliseconds. The returned duration is in nanoseconds. + * The purpose of this function is to implement the duration-related methods + * in the ConfigObject interface. * * @param input * the string to parse @@ -45,18 +47,23 @@ public final class Config { String numberString = s.substring(0, s.length() - unitString.length()).trim(); TimeUnit units = null; + if (unitString.length() > 2 && !unitString.endsWith("s")) + unitString = unitString + "s"; + // note that this is deliberately case-sensitive - if (unitString == "" || unitString == "ms" || unitString == "milliseconds") { + if (unitString.equals("") || unitString.equals("ms") || unitString.equals("milliseconds")) { units = TimeUnit.MILLISECONDS; - } else if (unitString == "us" || unitString == "microseconds") { + } else if (unitString.equals("us") || unitString.equals("microseconds")) { units = TimeUnit.MICROSECONDS; - } else if (unitString == "ns" || unitString == "nanoseconds") { + } else if (unitString.equals("ns") || unitString.equals("nanoseconds")) { units = TimeUnit.NANOSECONDS; - } else if (unitString == "d" || unitString == "days") { + } else if (unitString.equals("d") || unitString.equals("days")) { units = TimeUnit.DAYS; - } else if (unitString == "s" || unitString == "seconds") { + } else if (unitString.equals("h") || unitString.equals("hours")) { + units = TimeUnit.HOURS; + } else if (unitString.equals("s") || unitString.equals("seconds")) { units = TimeUnit.SECONDS; - } else if (unitString == "m" || unitString == "minutes") { + } else if (unitString.equals("m") || unitString.equals("minutes")) { units = TimeUnit.MINUTES; } else { throw new ConfigException.BadValue(originForException, @@ -92,7 +99,9 @@ public final class Config { /** * Parses a memory-size string. If no units are specified in the string, it - * is assumed to be in bytes. The returned value is in bytes. + * is assumed to be in bytes. The returned value is in bytes. The purpose of + * this function is to implement the memory-size-related methods in the + * ConfigObject interface. * * @param input * the string to parse @@ -107,22 +116,29 @@ public final class Config { public static long parseMemorySize(String input, ConfigOrigin originForException, String pathForException) { String s = input.trim(); - String unitString = getUnits(s); + String unitStringMaybePlural = getUnits(s); + String unitString; + if (unitStringMaybePlural.endsWith("s")) + unitString = unitStringMaybePlural.substring(0, + unitStringMaybePlural.length() - 1); + else + unitString = unitStringMaybePlural; String unitStringLower = unitString.toLowerCase(); - String numberString = s.substring(0, s.length() - unitString.length()) + String numberString = s.substring(0, + s.length() - unitStringMaybePlural.length()) .trim(); MemoryUnit units = null; // the short abbreviations are case-insensitive but you can't write the // long form words in all caps. - if (unitString == "" || unitStringLower == "b" - || unitString == "bytes") { + if (unitString.equals("") || unitStringLower.equals("b") + || unitString.equals("byte")) { units = MemoryUnit.BYTES; - } else if (unitStringLower == "k" || unitString == "kilobytes") { + } else if (unitStringLower.equals("k") || unitString.equals("kilobyte")) { units = MemoryUnit.KILOBYTES; - } else if (unitStringLower == "m" || unitString == "megabytes") { + } else if (unitStringLower.equals("m") || unitString.equals("megabyte")) { units = MemoryUnit.MEGABYTES; - } else if (unitStringLower == "g" || unitString == "gigabytes") { + } else if (unitStringLower.equals("g") || unitString.equals("gigabyte")) { units = MemoryUnit.GIGABYTES; } else { throw new ConfigException.BadValue(originForException, diff --git a/src/main/java/com/typesafe/config/ConfigObject.java b/src/main/java/com/typesafe/config/ConfigObject.java index dd2fc047..c051d65e 100644 --- a/src/main/java/com/typesafe/config/ConfigObject.java +++ b/src/main/java/com/typesafe/config/ConfigObject.java @@ -8,6 +8,11 @@ import java.util.Set; * objects. Implementations of Config should be immutable (at least from the * perspective of anyone using this interface). * + * The getters all have the same semantics; they throw ConfigException.Missing + * if the value is entirely unset, and ConfigException.WrongType if you ask for + * a type that the value can't be converted to. ConfigException.Null is a + * subclass of ConfigException.WrongType thrown if the value is null. + * * TODO add OrNull variants of all these getters? */ public interface ConfigObject extends ConfigValue { @@ -42,8 +47,8 @@ public interface ConfigObject extends ConfigValue { /** * Get value as a duration in nanoseconds. If the value is already a number - * it's taken as milliseconds. If it's a string, it's parsed understanding - * unit suffixes. + * it's taken as milliseconds and converted to nanoseconds. If it's a + * string, it's parsed understanding unit suffixes. */ Long getNanoseconds(String path); diff --git a/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala b/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala new file mode 100644 index 00000000..1457a449 --- /dev/null +++ b/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala @@ -0,0 +1,42 @@ +package com.typesafe.config.impl + +import org.junit.Assert._ +import org.junit._ +import com.typesafe.config._ +import java.util.concurrent.TimeUnit + +class UnitParserTest extends TestUtils { + + @Test + def parseDuration() { + val oneSecs = List("1s", "1 s", "1seconds", "1 seconds", " 1s ", " 1 s ", + "1second", + "1000", "1000ms", "1000 ms", "1000 milliseconds", " 1000 milliseconds ", + "1000millisecond", + "1000000us", "1000000 us", "1000000 microseconds", "1000000microsecond", + "1000000000ns", "1000000000 ns", "1000000000 nanoseconds", "1000000000nanosecond", + "0.01666666666666666666666m", "0.01666666666666666666666 minutes", "0.01666666666666666666666 minute", + "0.00027777777777777777777h", "0.00027777777777777777777 hours", "0.00027777777777777777777hour", + "1.1574074074074073e-05d", "1.1574074074074073e-05 days", "1.1574074074074073e-05day") + val oneSecInNanos = TimeUnit.SECONDS.toNanos(1) + for (s <- oneSecs) { + val result = Config.parseDuration(s, fakeOrigin(), "test") + assertEquals(oneSecInNanos, result) + } + } + + @Test + def parseMemorySize() { + val oneMegs = List("1048576", "1048576b", "1048576bytes", "1048576byte", + "1048576 b", "1048576 bytes", + " 1048576 b ", " 1048576 bytes ", + "1048576B", + "1024k", "1024K", "1024 kilobytes", "1024 kilobyte", + "1m", "1M", "1 M", "1 megabytes", "1 megabyte", + "0.0009765625g", "0.0009765625G", "0.0009765625 gigabytes", "0.0009765625 gigabyte") + for (s <- oneMegs) { + val result = Config.parseMemorySize(s, fakeOrigin(), "test") + assertEquals(1024 * 1024, result) + } + } +}