Indexed navigation in compound keys and shallow merging of defaults

This commit is contained in:
moandji.ezana 2014-04-09 15:02:41 +02:00
parent 2f355b47f3
commit 0af30b90ac
6 changed files with 76 additions and 27 deletions

View file

@ -47,6 +47,12 @@
<version>1.4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>

View file

@ -18,6 +18,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.parboiled.Parboiled;
import org.parboiled.parserunners.RecoveringParseRunner;
@ -44,21 +46,23 @@ import com.google.gson.Gson;
*/
public class Toml {
private static Pattern ARRAY_INDEX_PATTERN = Pattern.compile("(.*)\\[(\\d+)\\]");
private Map<String, Object> values = new HashMap<String, Object>();
private final Toml defaults;
private final Gson gson = new Gson();
/**
* Creates Toml instance with no defaults.
*/
public Toml() {
this((Toml) null);
this(null);
}
/**
* @param defaults fallback values used when the requested key or table is not present.
*/
public Toml(Toml defaults) {
this.defaults = defaults;
this(defaults, new HashMap<String, Object>());
}
/**
@ -177,7 +181,7 @@ public class Toml {
*/
@SuppressWarnings("unchecked")
public Toml getTable(String key) {
return new Toml((Map<String, Object>) get(key));
return new Toml(null, (Map<String, Object>) get(key));
}
/**
@ -193,8 +197,9 @@ public class Toml {
}
ArrayList<Toml> tables = new ArrayList<Toml>();
for (Map<String, Object> table : tableArray) {
tables.add(new Toml(table));
tables.add(new Toml(null, table));
}
return tables;
@ -219,6 +224,7 @@ public class Toml {
*/
public <T> T to(Class<T> targetClass) {
HashMap<String, Object> valuesCopy = new HashMap<String, Object>(values);
if (defaults != null) {
for (Map.Entry<String, Object> entry : defaults.values.entrySet()) {
if (!valuesCopy.containsKey(entry.getKey())) {
@ -226,7 +232,7 @@ public class Toml {
}
}
}
Gson gson = new Gson();
String json = gson.toJson(valuesCopy);
return gson.fromJson(json, targetClass);
}
@ -235,32 +241,32 @@ public class Toml {
private Object get(String key) {
String[] split = key.split("\\.");
Object current = new HashMap<String, Object>(values);
Object currentDefaults = defaults != null ? defaults.values : null;
for (String splitKey : split) {
Matcher matcher = ARRAY_INDEX_PATTERN.matcher(splitKey);
int index = -1;
if (matcher.find()) {
splitKey = matcher.group(1);
index = Integer.parseInt(matcher.group(2), 10);
}
current = ((Map<String, Object>) current).get(splitKey);
if (currentDefaults != null) {
currentDefaults = ((Map<String, Object>) currentDefaults).get(splitKey);
if (current instanceof Map && currentDefaults instanceof Map) {
for (Map.Entry<String, Object> entry : ((Map<String, Object>) currentDefaults).entrySet()) {
if (!((Map<String, Object>) current).containsKey(entry.getKey())) {
((Map<String, Object>) current).put(entry.getKey(), entry.getValue());
}
}
}
}
if (current == null && currentDefaults != null) {
current = currentDefaults;
if (index > -1 && current != null) {
current = ((List<?>) current).get(index);
}
if (current == null) {
return null;
return defaults != null ? defaults.get(key) : null;
}
}
return current;
}
private Toml(Map<String, Object> values) {
private Toml(Toml defaults, Map<String, Object> values) {
this.values = values != null ? values : Collections.<String, Object>emptyMap();
this.defaults = null;
this.defaults = defaults;
}
}

View file

@ -13,7 +13,7 @@ public class TableArrayTest {
@Test
public void should_parse_table_array() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("products_table_array.toml").getFile()));
Toml toml = new Toml().parse(file("products_table_array"));
List<Toml> products = toml.getTables("products");
@ -32,7 +32,7 @@ public class TableArrayTest {
@Test
public void should_parse_nested_table_arrays() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("fruit_table_array.toml").getFile()));
Toml toml = new Toml().parse(file("fruit_table_array"));
List<Toml> fruits = toml.getTables("fruit");
@ -56,4 +56,23 @@ public class TableArrayTest {
assertEquals(3, toml.getTable("a").getTable("b").getTables("c").get(0).getLong("id").intValue());
}
@Test
public void should_navigate_array_with_compound_key() throws Exception {
Toml toml = new Toml().parse(file("fruit_table_array"));
List<Toml> appleVarieties = toml.getTables("fruit[0].variety");
Toml appleVariety = toml.getTable("fruit[0].variety[1]");
String bananaVariety = toml.getString("fruit[1].variety[0].name");
assertEquals(2, appleVarieties.size());
assertEquals("red delicious", appleVarieties.get(0).getString("name"));
assertEquals("granny smith", appleVariety.getString("name"));
assertEquals("plantain", bananaVariety);
}
private File file(String fileName) {
return new File(getClass().getResource(fileName + ".toml").getFile());
}
}

View file

@ -1,8 +1,11 @@
package com.moandjiezana.toml;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
@ -13,7 +16,7 @@ public class TomlDefaultsTest {
@Before
public void before() {
defaultToml = new Toml().parse("a = \"a\"\n[group]\na=\"a\"");
defaultToml = new Toml().parse("a = \"a\"\n [group]\n a=\"a\"\n [[array]]\n b=1 [[array]]\n b=2");
}
@Test
@ -52,10 +55,22 @@ public class TomlDefaultsTest {
}
@Test
public void should_fall_back_to_key_within_table() throws Exception {
Toml toml = new Toml(defaultToml).parse("[group]\nb=1");
public void should_fall_back_to_table_array() throws Exception {
Toml toml = new Toml(defaultToml).parse("");
assertThat(toml.getTables("array"), hasSize(2));
assertThat(toml.getLong("array[1].b"), Matchers.equalTo(2L));
}
@Test
public void should_perform_shallow_merge() throws Exception {
Toml toml = new Toml(defaultToml).parse("[group]\nb=1\n [[array]]\n b=0");
Toml toml2 = new Toml(defaultToml).parse("[[array]]\n b=1 [[array]]\n b=2 [[array]]\n b=3");
assertEquals(1, toml.getTable("group").getLong("b").intValue());
assertEquals("a", toml.getTable("group").getString("a"));
assertNull(toml.getTable("group").getString("a"));
assertThat(toml.getTables("array"), hasSize(1));
assertEquals(0, toml.getLong("array[0].b").intValue());
assertThat(toml2.getTables("array"), hasSize(3));
}
}

View file

@ -3,6 +3,7 @@ package com.moandjiezana.toml.testutils;
import java.lang.annotation.ElementType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import java.util.Set;
@ -17,5 +18,6 @@ public class ExtraPrimitives {
public Character character;
public ElementType elementType;
public URL url;
public URI uri;
public Set<String> set;
}

View file

@ -5,6 +5,7 @@ anInteger=7
character="u"
elementType="CONSTRUCTOR"
url="http://www.example.com"
uri="http://www.test.com"
set=["a", "b"]
[group]
key="value"