diff --git a/pom.xml b/pom.xml index c88c384..48e3855 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 org.sonatype.oss @@ -40,6 +41,12 @@ gson 2.2.2 + + org.easytesting + fest-reflect + 1.4.1 + test + diff --git a/src/main/java/com/moandjiezana/toml/Toml.java b/src/main/java/com/moandjiezana/toml/Toml.java index 2fe38d3..5b0fc4e 100644 --- a/src/main/java/com/moandjiezana/toml/Toml.java +++ b/src/main/java/com/moandjiezana/toml/Toml.java @@ -3,6 +3,7 @@ package com.moandjiezana.toml; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -16,8 +17,20 @@ import org.parboiled.support.ParsingResult; import com.google.gson.Gson; /** + *

Provides access to the keys and tables in a TOML data source.

* - * All getters can fall back to default values if they have been provided and will return null if no matching key exists. + *

All getters can fall back to default values if they have been provided. + * Getters for simple values (String, Date, etc.) will return null if no matching key exists. + * {@link #getList(String, Class)}, {@link #getTable(String)} and {@link #getTables(String)} return empty values if there is no matching key.

+ * + *

Example usage:

+ *
+ * Toml toml = new Toml().parse(getTomlFile());
+ * String name = toml.getString("name");
+ * Long port = toml.getLong("server.ip"); // compound key. Is equivalent to:
+ * Long port2 = toml.getTable("server").getLong("ip");
+ * MyConfig config = toml.to(MyConfig.class);
+ * 
* */ public class Toml { @@ -25,23 +38,50 @@ public class Toml { private Map values = new HashMap(); private final Toml defaults; + /** + * Creates Toml instance with no defaults. + */ public Toml() { this((Toml) null); } + /** + * @param defaults fallback values used when the requested key or table is not present. + */ public Toml(Toml defaults) { this.defaults = defaults; } + /** + * Populates the current Toml instance with values from tomlString. + * + * @param file + * @return this instance + * @throws IllegalStateException If file contains invalid TOML + */ public Toml parse(File file) { + Scanner scanner = null; try { - return parse(new Scanner(file).useDelimiter("\\Z").next()); + scanner = new Scanner(file); + + return parse(scanner.useDelimiter("\\Z").next()); } catch (FileNotFoundException e) { throw new RuntimeException(e); + } finally { + if (scanner != null) { + scanner.close(); + } } } - public Toml parse(String tomlString) { + /** + * Populates the current Toml instance with values from tomlString. + * + * @param tomlString + * @return this instance + * @throws IllegalStateException If tomlString is not valid TOML + */ + public Toml parse(String tomlString) throws IllegalStateException { TomlParser parser = Parboiled.createParser(TomlParser.class); ParsingResult result = new RecoveringParseRunner(parser.Toml()).run(tomlString); // ParsingResult parsingResult = new ReportingParseRunner(parser.Toml()).run(tomlString); @@ -67,7 +107,13 @@ public class Toml { @SuppressWarnings("unchecked") public List getList(String key, Class itemClass) { - return (List) get(key); + List list = (List) get(key); + + if (list == null) { + return Collections.emptyList(); + } + + return list; } public Boolean getBoolean(String key) { @@ -82,21 +128,59 @@ public class Toml { return (Double) get(key); } + /** + * If no value is found for key, an empty Toml instance is returned. + * + * @param key + */ @SuppressWarnings("unchecked") public Toml getTable(String key) { return new Toml((Map) get(key)); } + /** + * If no value is found for key, an empty list is returned. + * @param key + */ @SuppressWarnings("unchecked") public List getTables(String key) { + List> tableArray = (List>) get(key); + + if (tableArray == null) { + return Collections.emptyList(); + } + ArrayList tables = new ArrayList(); - for (Map table : (List>) get(key)) { + for (Map table : tableArray) { tables.add(new Toml(table)); } return tables; } + /** + *

Populates an instance of targetClass with the values of this Toml instance. + * The target's field names must match keys or tables. + * Keys not present in targetClass will be ignored.

+ * + *

Tables are recursively converted to custom classes.

+ * + * @param targetClass + */ + 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())) { + valuesCopy.put(entry.getKey(), entry.getValue()); + } + } + } + Gson gson = new Gson(); + String json = gson.toJson(valuesCopy); + return gson.fromJson(json, targetClass); + } + @SuppressWarnings("unchecked") private Object get(String key) { String[] split = key.split("\\."); @@ -126,21 +210,7 @@ public class Toml { } private Toml(Map values) { - this.values = values; + this.values = values != null ? values : Collections.emptyMap(); this.defaults = null; } - - 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())) { - valuesCopy.put(entry.getKey(), entry.getValue()); - } - } - } - Gson gson = new Gson(); - String json = gson.toJson(valuesCopy); - return gson.fromJson(json, targetClass); - } } diff --git a/src/test/java/com/moandjiezana/toml/TomlTest.java b/src/test/java/com/moandjiezana/toml/TomlTest.java index f600058..f768dd9 100644 --- a/src/test/java/com/moandjiezana/toml/TomlTest.java +++ b/src/test/java/com/moandjiezana/toml/TomlTest.java @@ -8,8 +8,11 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.util.Calendar; +import java.util.List; +import java.util.Map; import java.util.TimeZone; +import org.fest.reflect.core.Reflection; import org.junit.Ignore; import org.junit.Test; @@ -129,6 +132,13 @@ public class TomlTest { assertNull(toml.getString("a")); } + @Test + public void should_return_empty_list_if_no_value_for_key() throws Exception { + Toml toml = new Toml().parse(""); + + assertTrue(toml.getList("a", String.class).isEmpty()); + } + @Test public void should_return_null_when_no_value_for_multi_key() throws Exception { Toml toml = new Toml().parse(""); @@ -136,6 +146,21 @@ public class TomlTest { assertNull(toml.getString("group.key")); } + @Test + public void should_return_empty_toml_when_no_value_for_table() throws Exception { + Toml toml = new Toml().parse("[a]").getTable("b"); + + assertTrue(Reflection.field("values").ofType(Map.class).in(toml).get().isEmpty()); + assertNull(toml.getString("x")); + } + + @Test + public void should_return_empty_list_when_no_value_for_table_array() throws Exception { + List tomls = new Toml().parse("[a]").getTables("b"); + + assertTrue(tomls.isEmpty()); + } + @Test public void should_load_from_file() throws Exception { Toml toml = new Toml().parse(new File(getClass().getResource("should_load_from_file.toml").getFile()));