diff --git a/README.md b/README.md index 1f9a2c0..4325ef8 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Long c = toml.getLong("c"); // returns null ### Table arrays -Table arrays are mapped to `List`s with `Toml#getTables(String)`. Custom classes are also supported. +Table arrays are mapped to `List`s with `Toml#getTables(String)`. Custom classes and nested table arrays are supported. ```` [[products]] diff --git a/src/main/java/com/moandjiezana/toml/TomlParser.java b/src/main/java/com/moandjiezana/toml/TomlParser.java index 2c02383..5dd4a1e 100644 --- a/src/main/java/com/moandjiezana/toml/TomlParser.java +++ b/src/main/java/com/moandjiezana/toml/TomlParser.java @@ -267,9 +267,11 @@ class TomlParser extends BaseParser { return true; } - for (String splitKey : split) { + for (int i = 0; i < split.length; i++) { + String splitKey = split[i]; + if (!newTable.containsKey(splitKey)) { - if (array) { + if (array && isLast(split, i)) { ArrayList> newTableList = new ArrayList>(); newTable.put(splitKey, newTableList); } else { @@ -277,16 +279,21 @@ class TomlParser extends BaseParser { } } Object currentValue = newTable.get(splitKey); - if ((array && !(currentValue instanceof List)) || (!array && !(currentValue instanceof Map))) { + if (!(currentValue instanceof List) && !(currentValue instanceof Map)) { results().errors.append("Could not create table ").append(name).append(": key already has a value!\n"); return true; } if (currentValue instanceof List) { - Map newTableListItem = new HashMap(); - currentValue = ((List>) currentValue).add(newTableListItem); - currentValue = newTableListItem; + List> currentList = (List>) currentValue; + if (array && isLast(split, i)) { + Map newTableListItem = new HashMap(); + currentList.add(newTableListItem); + currentValue = newTableListItem; + } else { + currentValue = currentList.get(currentList.size() - 1); + } } newTable = (Map) currentValue; @@ -311,6 +318,10 @@ class TomlParser extends BaseParser { values.put(name, value); } + private boolean isLast(String[] array, int index) { + return index == array.length - 1; + } + private TomlParser.Results results() { return (Results) peek(getContext().getValueStack().size() - 1); } diff --git a/src/test/java/com/moandjiezana/toml/TableArrayTest.java b/src/test/java/com/moandjiezana/toml/TableArrayTest.java index 570aaa1..ca8b982 100644 --- a/src/test/java/com/moandjiezana/toml/TableArrayTest.java +++ b/src/test/java/com/moandjiezana/toml/TableArrayTest.java @@ -29,4 +29,31 @@ public class TableArrayTest { assertEquals(284758393L, products.get(2).getLong("sku").longValue()); assertEquals("gray", products.get(2).getString("color")); } + + @Test + public void should_parse_nested_table_arrays() throws Exception { + Toml toml = new Toml().parse(new File(getClass().getResource("fruit_table_array.toml").getFile())); + + List fruits = toml.getTables("fruit"); + + assertEquals(2, fruits.size()); + + Toml apple = fruits.get(0); + assertEquals("apple", apple.getString("name")); + assertEquals("red", apple.getTable("physical").getString("color")); + assertEquals("round", apple.getTable("physical").getString("shape")); + assertEquals(2, apple.getTables("variety").size()); + + Toml banana = fruits.get(1); + assertEquals("banana", banana.getString("name")); + assertEquals(1, banana.getTables("variety").size()); + assertEquals("plantain", banana.getTables("variety").get(0).getString("name")); + } + + @Test + public void should_create_array_ancestors_as_tables() throws Exception { + Toml toml = new Toml().parse("[[a.b.c]]\n id=3"); + + assertEquals(3, toml.getTable("a").getTable("b").getTables("c").get(0).getLong("id").intValue()); + } } diff --git a/src/test/resources/com/moandjiezana/toml/fruit_table_array.toml b/src/test/resources/com/moandjiezana/toml/fruit_table_array.toml new file mode 100644 index 0000000..ced9e2a --- /dev/null +++ b/src/test/resources/com/moandjiezana/toml/fruit_table_array.toml @@ -0,0 +1,18 @@ +[[fruit]] + name = "apple" + + [fruit.physical] + color = "red" + shape = "round" + + [[fruit.variety]] + name = "red delicious" + + [[fruit.variety]] + name = "granny smith" + +[[fruit]] + name = "banana" + + [[fruit.variety]] + name = "plantain"