mirror of
https://github.com/lightbend/config.git
synced 2025-02-23 01:30:34 +08:00
Try to convert objects with numeric keys to lists
This is mostly intended to provide a way to specify lists on the command line via Java properties, like -Dfoo.0 -Dfoo.1 Fixes #69
This commit is contained in:
parent
145a9be612
commit
338150f281
@ -3,6 +3,13 @@
|
||||
*/
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/**
|
||||
@ -74,6 +81,49 @@ final class DefaultTransformer {
|
||||
// no-op STRING to STRING
|
||||
break;
|
||||
}
|
||||
} else if (requested == ConfigValueType.LIST && value.valueType() == ConfigValueType.OBJECT) {
|
||||
// attempt to convert an array-like (numeric indices) object to a
|
||||
// list. This would be used with .properties syntax for example:
|
||||
// -Dfoo.0=bar -Dfoo.1=baz
|
||||
// To ensure we still throw type errors for objects treated
|
||||
// as lists in most cases, we'll refuse to convert if the object
|
||||
// does not contain any numeric keys. This means we don't allow
|
||||
// empty objects here though :-/
|
||||
AbstractConfigObject o = (AbstractConfigObject) value;
|
||||
Map<Integer, AbstractConfigValue> values = new HashMap<Integer, AbstractConfigValue>();
|
||||
for (String key : o.keySet()) {
|
||||
int i;
|
||||
try {
|
||||
i = Integer.parseInt(key, 10);
|
||||
if (i < 0)
|
||||
continue;
|
||||
values.put(i, o.get(key));
|
||||
} catch (NumberFormatException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!values.isEmpty()) {
|
||||
ArrayList<Map.Entry<Integer, AbstractConfigValue>> entryList = new ArrayList<Map.Entry<Integer, AbstractConfigValue>>(
|
||||
values.entrySet());
|
||||
// sort by numeric index
|
||||
Collections.sort(entryList,
|
||||
new Comparator<Map.Entry<Integer, AbstractConfigValue>>() {
|
||||
@Override
|
||||
public int compare(Map.Entry<Integer, AbstractConfigValue> a,
|
||||
Map.Entry<Integer, AbstractConfigValue> b) {
|
||||
// Integer.compare was added in 1.7 so not using
|
||||
// it here yet
|
||||
return Integer.valueOf(a.getKey()).compareTo(b.getKey());
|
||||
}
|
||||
});
|
||||
// drop the indices (we allow gaps in the indices, for better or
|
||||
// worse)
|
||||
ArrayList<AbstractConfigValue> list = new ArrayList<AbstractConfigValue>();
|
||||
for (Map.Entry<Integer, AbstractConfigValue> entry : entryList) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
return new SimpleConfigList(value.origin(), list);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
|
@ -9,6 +9,7 @@ import java.util.Properties
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigException
|
||||
|
||||
class PropertiesTest extends TestUtils {
|
||||
@Test
|
||||
@ -92,4 +93,63 @@ class PropertiesTest extends TestUtils {
|
||||
assertEquals("foo", conf.getString("a.b"))
|
||||
assertEquals("foo", conf.getString("x.y.z"))
|
||||
}
|
||||
|
||||
@Test
|
||||
def makeListWithNumericKeys() {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val props = new Properties()
|
||||
props.setProperty("a.0", "0")
|
||||
props.setProperty("a.1", "1")
|
||||
props.setProperty("a.2", "2")
|
||||
props.setProperty("a.3", "3")
|
||||
props.setProperty("a.4", "4")
|
||||
|
||||
val conf = ConfigFactory.parseProperties(props, ConfigParseOptions.defaults())
|
||||
assertEquals(Seq(0, 1, 2, 3, 4), conf.getIntList("a").asScala.toSeq)
|
||||
}
|
||||
|
||||
@Test
|
||||
def makeListWithNumericKeysWithGaps() {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val props = new Properties()
|
||||
props.setProperty("a.1", "0")
|
||||
props.setProperty("a.2", "1")
|
||||
props.setProperty("a.4", "2")
|
||||
|
||||
val conf = ConfigFactory.parseProperties(props, ConfigParseOptions.defaults())
|
||||
assertEquals(Seq(0, 1, 2), conf.getIntList("a").asScala.toSeq)
|
||||
}
|
||||
|
||||
@Test
|
||||
def makeListWithNumericKeysWithNoise() {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val props = new Properties()
|
||||
props.setProperty("a.-1", "-1")
|
||||
props.setProperty("a.foo", "-2")
|
||||
props.setProperty("a.0", "0")
|
||||
props.setProperty("a.1", "1")
|
||||
props.setProperty("a.2", "2")
|
||||
props.setProperty("a.3", "3")
|
||||
props.setProperty("a.4", "4")
|
||||
|
||||
val conf = ConfigFactory.parseProperties(props, ConfigParseOptions.defaults())
|
||||
assertEquals(Seq(0, 1, 2, 3, 4), conf.getIntList("a").asScala.toSeq)
|
||||
}
|
||||
|
||||
@Test
|
||||
def noNumericKeysAsListFails() {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val props = new Properties()
|
||||
props.setProperty("a.bar", "0")
|
||||
|
||||
val conf = ConfigFactory.parseProperties(props, ConfigParseOptions.defaults())
|
||||
val e = intercept[ConfigException.WrongType] {
|
||||
conf.getList("a")
|
||||
}
|
||||
assertTrue("expected exception thrown", e.getMessage.contains("LIST"))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user