diff --git a/pom.xml b/pom.xml index 48e3855..7cb0187 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,12 @@ 1.4.1 test + + org.hamcrest + hamcrest-library + 1.3 + test + diff --git a/src/main/java/com/moandjiezana/toml/Toml.java b/src/main/java/com/moandjiezana/toml/Toml.java index 9d0d7da..5a96182 100644 --- a/src/main/java/com/moandjiezana/toml/Toml.java +++ b/src/main/java/com/moandjiezana/toml/Toml.java @@ -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 values = new HashMap(); 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()); } /** @@ -177,7 +181,7 @@ public class Toml { */ @SuppressWarnings("unchecked") public Toml getTable(String key) { - return new Toml((Map) get(key)); + return new Toml(null, (Map) get(key)); } /** @@ -193,8 +197,9 @@ public class Toml { } ArrayList tables = new ArrayList(); + for (Map table : tableArray) { - tables.add(new Toml(table)); + tables.add(new Toml(null, table)); } return tables; @@ -219,6 +224,7 @@ public class Toml { */ public T to(Class targetClass) { HashMap valuesCopy = new HashMap(values); + if (defaults != null) { for (Map.Entry 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(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) current).get(splitKey); - if (currentDefaults != null) { - currentDefaults = ((Map) currentDefaults).get(splitKey); - if (current instanceof Map && currentDefaults instanceof Map) { - for (Map.Entry entry : ((Map) currentDefaults).entrySet()) { - if (!((Map) current).containsKey(entry.getKey())) { - ((Map) 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 values) { + private Toml(Toml defaults, Map values) { this.values = values != null ? values : Collections.emptyMap(); - this.defaults = null; + this.defaults = defaults; } } diff --git a/src/test/java/com/moandjiezana/toml/TableArrayTest.java b/src/test/java/com/moandjiezana/toml/TableArrayTest.java index ca8b982..095e17c 100644 --- a/src/test/java/com/moandjiezana/toml/TableArrayTest.java +++ b/src/test/java/com/moandjiezana/toml/TableArrayTest.java @@ -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 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 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 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()); + } + } diff --git a/src/test/java/com/moandjiezana/toml/TomlDefaultsTest.java b/src/test/java/com/moandjiezana/toml/TomlDefaultsTest.java index 9acf2a6..cc20819 100644 --- a/src/test/java/com/moandjiezana/toml/TomlDefaultsTest.java +++ b/src/test/java/com/moandjiezana/toml/TomlDefaultsTest.java @@ -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)); } } diff --git a/src/test/java/com/moandjiezana/toml/testutils/ExtraPrimitives.java b/src/test/java/com/moandjiezana/toml/testutils/ExtraPrimitives.java index f0ddd7f..b5d0ab9 100644 --- a/src/test/java/com/moandjiezana/toml/testutils/ExtraPrimitives.java +++ b/src/test/java/com/moandjiezana/toml/testutils/ExtraPrimitives.java @@ -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 set; } diff --git a/src/test/resources/com/moandjiezana/toml/should_convert_extra_primitives.toml b/src/test/resources/com/moandjiezana/toml/should_convert_extra_primitives.toml index 33c2a30..359e027 100644 --- a/src/test/resources/com/moandjiezana/toml/should_convert_extra_primitives.toml +++ b/src/test/resources/com/moandjiezana/toml/should_convert_extra_primitives.toml @@ -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"