From a5a6ab22dc22b96841d3ee3eb954f0627eb66892 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Mon, 9 Feb 2015 22:15:37 +0200 Subject: [PATCH] Support for very basic inline tables --- .../toml/InlineTableConverter.java | 93 +++++++++++++++++++ .../java/com/moandjiezana/toml/Results.java | 10 +- .../com/moandjiezana/toml/TomlParser.java | 1 + .../moandjiezana/toml/ValueConverters.java | 3 +- .../moandjiezana/toml/InlineTableTest.java | 90 ++++++++++++++++++ ...should_support_array_of_inline_tables.toml | 3 + 6 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/moandjiezana/toml/InlineTableConverter.java create mode 100644 src/test/java/com/moandjiezana/toml/InlineTableTest.java create mode 100644 src/test/resources/com/moandjiezana/toml/should_support_array_of_inline_tables.toml diff --git a/src/main/java/com/moandjiezana/toml/InlineTableConverter.java b/src/main/java/com/moandjiezana/toml/InlineTableConverter.java new file mode 100644 index 0000000..a3a181c --- /dev/null +++ b/src/main/java/com/moandjiezana/toml/InlineTableConverter.java @@ -0,0 +1,93 @@ +package com.moandjiezana.toml; + +import static com.moandjiezana.toml.ValueConverterUtils.INVALID; + +import java.util.HashMap; + +class InlineTableConverter implements ValueConverter { + + static final InlineTableConverter INLINE_TABLE_PARSER = new InlineTableConverter(); + private static final ValueConverters CONVERTERS = new ValueConverters(); + + @Override + public boolean canConvert(String s) { + return s.startsWith("{"); + } + + @Override + public Object convert(String s) { + char[] chars = s.toCharArray(); + boolean inKey = true; + boolean pairHasKey = false; + boolean inValue = false; + boolean quoted = false; + boolean inString = false; + boolean terminated = false; + StringBuilder currentKey = new StringBuilder(); + StringBuilder current = new StringBuilder(); + HashMap results = new HashMap(); + + for (int i = 1; i < chars.length; i++) { + char c = chars[i]; + + if (terminated) { + if (Character.isWhitespace(c)) { + continue; + } + if (c == '#') { + break; + } + + return INVALID; + } + + if (c == '"') { + quoted = !quoted; + (inValue ? current : currentKey).append(c); + } else if (quoted) { + (inKey ? currentKey : current).append(c); + } else if (c == ',') { + Object converted = CONVERTERS.convert(current.toString().trim()); + + if (converted == INVALID) { + return INVALID; + } + + results.put(currentKey.toString().trim(), converted); + inKey = true; + pairHasKey = false; + inValue = false; + currentKey = new StringBuilder(); + current = new StringBuilder(); + } else if (c == '=') { + inKey = false; + pairHasKey = true; + inValue = true; + } else if (c == '}') { + terminated = true; + + if (current.toString().trim().length() == 0) { + continue; + } + + Object converted = CONVERTERS.convert(current.toString().trim()); + + if (converted == INVALID) { + return INVALID; + } + + results.put(currentKey.toString().trim(), converted); + } else { + (inKey ? currentKey : current).append(c); + } + } + + if (!terminated) { + return INVALID; + } + + return results; + } + + private InlineTableConverter() {} +} diff --git a/src/main/java/com/moandjiezana/toml/Results.java b/src/main/java/com/moandjiezana/toml/Results.java index 5d57565..97274d7 100644 --- a/src/main/java/com/moandjiezana/toml/Results.java +++ b/src/main/java/com/moandjiezana/toml/Results.java @@ -109,7 +109,15 @@ class Results { void addValue(String key, Object value) { Container currentTable = stack.peek(); - if (currentTable.accepts(key)) { + + if (value instanceof Map) { + startTable(key); + @SuppressWarnings("unchecked") + Map valueMap = (Map) value; + for (Map.Entry entry : valueMap.entrySet()) { + addValue(entry.getKey(), entry.getValue()); + } + } else if (currentTable.accepts(key)) { currentTable.put(key, value); } else { errors.duplicateKey(key, -1); diff --git a/src/main/java/com/moandjiezana/toml/TomlParser.java b/src/main/java/com/moandjiezana/toml/TomlParser.java index 2fa3ae9..2244a85 100644 --- a/src/main/java/com/moandjiezana/toml/TomlParser.java +++ b/src/main/java/com/moandjiezana/toml/TomlParser.java @@ -37,6 +37,7 @@ class TomlParser { continue; } + // TODO check that this works in multiline context if (isTableArray(line)) { String tableName = Keys.getTableArrayName(line); if (tableName != null) { diff --git a/src/main/java/com/moandjiezana/toml/ValueConverters.java b/src/main/java/com/moandjiezana/toml/ValueConverters.java index d4b8a96..aeb56e8 100644 --- a/src/main/java/com/moandjiezana/toml/ValueConverters.java +++ b/src/main/java/com/moandjiezana/toml/ValueConverters.java @@ -3,6 +3,7 @@ package com.moandjiezana.toml; import static com.moandjiezana.toml.ArrayConverter.ARRAY_PARSER; import static com.moandjiezana.toml.BooleanConverter.BOOLEAN_PARSER; import static com.moandjiezana.toml.DateConverter.DATE_PARSER; +import static com.moandjiezana.toml.InlineTableConverter.INLINE_TABLE_PARSER; import static com.moandjiezana.toml.LiteralStringConverter.LITERAL_STRING_PARSER; import static com.moandjiezana.toml.MultilineLiteralStringConverter.MULTILINE_LITERAL_STRING_CONVERTER; import static com.moandjiezana.toml.MultilineStringConverter.MULTILINE_STRING_PARSER; @@ -13,7 +14,7 @@ import static com.moandjiezana.toml.ValueConverterUtils.INVALID; class ValueConverters { private static final ValueConverter[] PARSERS = { - MULTILINE_STRING_PARSER, MULTILINE_LITERAL_STRING_CONVERTER, LITERAL_STRING_PARSER, STRING_PARSER, DATE_PARSER, NUMBER_PARSER, BOOLEAN_PARSER, ARRAY_PARSER + MULTILINE_STRING_PARSER, MULTILINE_LITERAL_STRING_CONVERTER, LITERAL_STRING_PARSER, STRING_PARSER, DATE_PARSER, NUMBER_PARSER, BOOLEAN_PARSER, ARRAY_PARSER, INLINE_TABLE_PARSER }; public Object convert(String value) { diff --git a/src/test/java/com/moandjiezana/toml/InlineTableTest.java b/src/test/java/com/moandjiezana/toml/InlineTableTest.java new file mode 100644 index 0000000..421b00f --- /dev/null +++ b/src/test/java/com/moandjiezana/toml/InlineTableTest.java @@ -0,0 +1,90 @@ +package com.moandjiezana.toml; + +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Calendar; +import java.util.TimeZone; + +import org.junit.Test; + +public class InlineTableTest { + + private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + + @Test + public void should_read_empty_inline_table() throws Exception { + Toml toml = new Toml().parse("key = {}"); + + assertNotNull(toml.getTable("key")); + } + + @Test + public void should_read_inline_table_with_strings() throws Exception { + Toml toml = new Toml().parse("name = { first = \"Tom\", last = \"Preston-Werner\"}"); + + assertEquals("Tom", toml.getTable("name").getString("first")); + assertEquals("Preston-Werner", toml.getString("name.last")); + } + + @Test + public void should_read_inline_table_with_integers() throws Exception { + Toml toml = new Toml().parse("point = { x = 1, y = 2 }"); + + assertEquals(1, toml.getTable("point").getLong("x").longValue()); + assertEquals(2, toml.getLong("point.y").longValue()); + } + + @Test + public void should_read_inline_table_with_floats() throws Exception { + Toml toml = new Toml().parse("point = { x = 1.5, y = 2.3 }"); + + assertEquals(1.5, toml.getTable("point").getDouble("x").doubleValue(), 0); + assertEquals(2.3, toml.getDouble("point.y").doubleValue(), 0); + } + + @Test + public void should_read_inline_table_with_booleans() throws Exception { + Toml toml = new Toml().parse("point = { x = false, y = true }"); + + assertTrue(toml.getTable("point").getBoolean("y")); + assertFalse(toml.getBoolean("point.x")); + } + + @Test + public void should_read_inline_table_with_dates() throws Exception { + Toml toml = new Toml().parse("point = { x = 2015-02-09T22:05:00Z, y = 2015-02-09T21:05:00Z }"); + + + Calendar x = Calendar.getInstance(UTC); + x.set(2015, Calendar.FEBRUARY, 9, 22, 5, 00); + x.set(Calendar.MILLISECOND, 0); + + Calendar y = Calendar.getInstance(UTC); + y.set(2015, Calendar.FEBRUARY, 9, 21, 5, 00); + y.set(Calendar.MILLISECOND, 0); + + assertEquals(x.getTime(), toml.getTable("point").getDate("x")); + assertEquals(y.getTime(), toml.getDate("point.y")); + } + + @Test + public void should_support_array_of_inline_tables() throws Exception { + Toml toml = new Toml().parse(getClass().getResourceAsStream("should_support_array_of_inline_tables.toml")); + + assertThat(toml.getList("points"), hasSize(4)); + assertEquals(1, toml.getLong("points[0].x").longValue()); + assertEquals(2, toml.getLong("points[0].y").longValue()); + assertEquals(3, toml.getLong("points[0].z").longValue()); + assertEquals(7, toml.getLong("points[1].x").longValue()); + assertEquals(8, toml.getLong("points[1].y").longValue()); + assertEquals(9, toml.getLong("points[1].z").longValue()); + assertEquals(2, toml.getLong("points[2].x").longValue()); + assertEquals(4, toml.getLong("points[2].y").longValue()); + assertEquals(8, toml.getLong("points[2].z").longValue()); + } +} diff --git a/src/test/resources/com/moandjiezana/toml/should_support_array_of_inline_tables.toml b/src/test/resources/com/moandjiezana/toml/should_support_array_of_inline_tables.toml new file mode 100644 index 0000000..58d6f86 --- /dev/null +++ b/src/test/resources/com/moandjiezana/toml/should_support_array_of_inline_tables.toml @@ -0,0 +1,3 @@ +points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ]