From b0e933fb28b6180a084656bf48ddb502b6bb25c9 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 00:37:20 +0200 Subject: [PATCH 01/12] Prepared README for release --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7a5bcb4..68c18d5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # toml4j -toml4j is a [TOML 0.3.1](https://github.com/toml-lang/toml/tree/v0.3.1) parser for Java that uses the [Parboiled](http://www.parboiled.org) PEG parser. +toml4j is a [TOML 0.3.1](https://github.com/toml-lang/toml/tree/v0.3.1) parser for Java. -[![Build Status](https://travis-ci.org/mwanji/toml4j.svg?branch=wip)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j?branch=wip) +[![Build Status](https://travis-ci.org/mwanji/toml4j.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j) For the bleeding-edge version integrating the latest specs, see the [work-in-progress branch](https://github.com/mwanji/toml4j/tree/wip). @@ -14,10 +14,12 @@ Add the following dependency to your POM (or equivalent for other dependency man com.moandjiezana.toml toml4j - 0.2.0 + 0.3.1 ```` +Requires Java 1.6. + ## Quick start ````java From 3cec32eb8ec22a2861207916a42be20c922deb80 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 00:47:10 +0200 Subject: [PATCH 02/12] Added Maven Central to badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68c18d5..1aee02c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ toml4j is a [TOML 0.3.1](https://github.com/toml-lang/toml/tree/v0.3.1) parser for Java. -[![Build Status](https://travis-ci.org/mwanji/toml4j.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j) +[![Maven Central](https://img.shields.io/maven-central/v/com.moandjiezana.toml/toml4j.svg)](https://search.maven.org/#search|gav|1|g%3A%22com.moandjiezana.toml%22%20AND%20a%3A%22toml4j%22) [![Build Status](https://shields.io/travis/mwanji/toml4j.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j) For the bleeding-edge version integrating the latest specs, see the [work-in-progress branch](https://github.com/mwanji/toml4j/tree/wip). From 8a16f487f5320bc1ce671149c630845c337ad85e Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 00:49:00 +0200 Subject: [PATCH 03/12] Fixed Travis CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aee02c..6138335 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ toml4j is a [TOML 0.3.1](https://github.com/toml-lang/toml/tree/v0.3.1) parser for Java. -[![Maven Central](https://img.shields.io/maven-central/v/com.moandjiezana.toml/toml4j.svg)](https://search.maven.org/#search|gav|1|g%3A%22com.moandjiezana.toml%22%20AND%20a%3A%22toml4j%22) [![Build Status](https://shields.io/travis/mwanji/toml4j.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j) +[![Maven Central](https://img.shields.io/maven-central/v/com.moandjiezana.toml/toml4j.svg)](https://search.maven.org/#search|gav|1|g%3A%22com.moandjiezana.toml%22%20AND%20a%3A%22toml4j%22) [![Build Status](https://img.shields.io/travis/mwanji/toml4j.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j) For the bleeding-edge version integrating the latest specs, see the [work-in-progress branch](https://github.com/mwanji/toml4j/tree/wip). From 7805561133cc2ec235d5611baab77418d03503b0 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 01:06:37 +0200 Subject: [PATCH 04/12] [maven-release-plugin] prepare release toml4j-0.3.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b399907..637eb10 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.moandjiezana.toml toml4j - 1.0.0-SNAPSHOT + 0.3.1 toml4j A parser for TOML http://moandjiezana.com/toml/toml4j From e425f81011285aa22c54dc4b711b4af655a2ec13 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 01:06:46 +0200 Subject: [PATCH 05/12] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 637eb10..c3e60bf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.moandjiezana.toml toml4j - 0.3.1 + 0.3.2-SNAPSHOT toml4j A parser for TOML http://moandjiezana.com/toml/toml4j From 28c57069c631ea15a39216e6064b38a84cb99c78 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 01:07:59 +0200 Subject: [PATCH 06/12] Fixed Javadoc for Java 8 --- src/main/java/com/moandjiezana/toml/Toml.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/moandjiezana/toml/Toml.java b/src/main/java/com/moandjiezana/toml/Toml.java index 740e40f..91b00a4 100644 --- a/src/main/java/com/moandjiezana/toml/Toml.java +++ b/src/main/java/com/moandjiezana/toml/Toml.java @@ -33,13 +33,13 @@ import com.google.gson.JsonElement; * {@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 { @@ -67,7 +67,7 @@ public class Toml { /** * Populates the current Toml instance with values from file. * - * @param file + * @param file The File to be read * @return this instance * @throws IllegalStateException If file contains invalid TOML */ @@ -82,7 +82,7 @@ public class Toml { /** * Populates the current Toml instance with values from inputStream. * - * @param inputStream + * @param inputStream Closed after it has been read. * @return this instance * @throws IllegalStateException If file contains invalid TOML */ @@ -93,7 +93,7 @@ public class Toml { /** * Populates the current Toml instance with values from reader. * - * @param reader + * @param reader Closed after it has been read. * @return this instance * @throws IllegalStateException If file contains invalid TOML */ @@ -122,7 +122,7 @@ public class Toml { /** * Populates the current Toml instance with values from tomlString. * - * @param tomlString + * @param tomlString String to be read. * @return this instance * @throws IllegalStateException If tomlString is not valid TOML */ @@ -169,9 +169,8 @@ public class Toml { } /** - * If no value is found for key, an empty Toml instance is returned. - * - * @param key + * @param key A table name, not including square brackets. + * @return A new Toml instance. Empty if no value is found for key. */ @SuppressWarnings("unchecked") public Toml getTable(String key) { @@ -179,8 +178,8 @@ public class Toml { } /** - * If no value is found for key, an empty list is returned. - * @param key + * @param key Name of array of tables, not including square brackets. + * @return An empty List if no value is found for key. */ @SuppressWarnings("unchecked") public List getTables(String key) { @@ -214,7 +213,9 @@ public class Toml { *
  • TOML array to {@link Set}
  • * * - * @param targetClass + * @param targetClass Class to deserialize TOML to. + * @param type of targetClass. + * @return A new instance of targetClass. */ public T to(Class targetClass) { return to(targetClass, DEFAULT_GSON); From 51fcf76e4e75c95f62ec738efedf870483cc0347 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 01:14:07 +0200 Subject: [PATCH 07/12] [maven-release-plugin] prepare release toml4j-0.3.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c3e60bf..637eb10 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.moandjiezana.toml toml4j - 0.3.2-SNAPSHOT + 0.3.1 toml4j A parser for TOML http://moandjiezana.com/toml/toml4j From 98701694c430a084b7de8708b396ed1784511356 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Tue, 16 Dec 2014 01:14:16 +0200 Subject: [PATCH 08/12] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 637eb10..c3e60bf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.moandjiezana.toml toml4j - 0.3.1 + 0.3.2-SNAPSHOT toml4j A parser for TOML http://moandjiezana.com/toml/toml4j From db950ab05410768d4c6e2b0f5781efef4c9d11d6 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Wed, 14 Jan 2015 09:46:36 +0200 Subject: [PATCH 09/12] Added support for quoted keys https://github.com/toml-lang/toml/pull/283 --- README.md | 11 +++ src/main/java/com/moandjiezana/toml/Keys.java | 33 +++++++++ .../java/com/moandjiezana/toml/Results.java | 2 +- src/main/java/com/moandjiezana/toml/Toml.java | 9 +-- .../com/moandjiezana/toml/QuotedKeysTest.java | 67 +++++++++++++++++++ 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/moandjiezana/toml/Keys.java create mode 100644 src/test/java/com/moandjiezana/toml/QuotedKeysTest.java diff --git a/README.md b/README.md index 6138335..df15357 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,9 @@ name = "Mwanji Ezana" [address] street = "123 A Street" city = "AnyVille" + +[contacts] + "email address" = me@example.com ```` ````java @@ -62,6 +65,7 @@ class Address { class User { String name; Address address; + Map contacts; } ```` @@ -70,10 +74,13 @@ User user = new Toml().parse(tomlFile).to(User.class); assert user.name.equals("Mwanji Ezana"); assert user.address.street.equals("123 A Street"); +assert user.contacts.get("\"email address\"").equals("me@example.com"); ```` Any keys not found in both the TOML and the class are ignored. Fields may be private. +Quoted keys cannot be mapped directly to a Java object, but they can be used as keys within a `Map`. + All TOML primitives can be mapped, as well as a number of Java-specific types: * A TOML Number can be converted to any primitive type (or the wrapper equivalent), `BigInteger` or `BigDecimal` @@ -102,8 +109,11 @@ You can also navigate values within a table with a compound key of the form `tab Non-existent keys return null. +When retrieving quoted keys, the quotes must be used and the key must be spelled exactly the same way, including whitespace. + ```` title = "TOML Example" +"sub title" = "Now with quoted keys" [database] ports = [ 8001, 8001, 8002 ] @@ -136,6 +146,7 @@ title = "TOML Example" Toml toml = new Toml().parse(getTomlFile()); String title = toml.getString("title"); +String subTitle = toml.getString("\"sub title\""); Boolean enabled = toml.getBoolean("database.enabled"); List ports = toml.getList("database.ports", Long.class); String password = toml.getString("database.credentials.password"); diff --git a/src/main/java/com/moandjiezana/toml/Keys.java b/src/main/java/com/moandjiezana/toml/Keys.java new file mode 100644 index 0000000..7478b8f --- /dev/null +++ b/src/main/java/com/moandjiezana/toml/Keys.java @@ -0,0 +1,33 @@ +package com.moandjiezana.toml; + +import java.util.ArrayList; +import java.util.List; + +class Keys { + + static String[] split(String key) { + List splitKey = new ArrayList(); + StringBuilder current = new StringBuilder(); + char[] chars = key.toCharArray(); + boolean quoted = false; + + for (char c : chars) { + if (c == '"') { + quoted = !quoted; + } + if (c != '.' || quoted) { + current.append(c); + } else { + splitKey.add(current.toString()); + current = new StringBuilder(); + } + } + + splitKey.add(current.toString()); + + return splitKey.toArray(new String[0]); + } + + private Keys() {} + +} diff --git a/src/main/java/com/moandjiezana/toml/Results.java b/src/main/java/com/moandjiezana/toml/Results.java index 84e5a53..73ad666 100644 --- a/src/main/java/com/moandjiezana/toml/Results.java +++ b/src/main/java/com/moandjiezana/toml/Results.java @@ -75,7 +75,7 @@ class Results { stack.pop(); } - String[] tableParts = tableName.split("\\."); + String[] tableParts = Keys.split(tableName); for (int i = 0; i < tableParts.length; i++) { String tablePart = tableParts[i]; Container currentContainer = stack.peek(); diff --git a/src/main/java/com/moandjiezana/toml/Toml.java b/src/main/java/com/moandjiezana/toml/Toml.java index 91b00a4..a95b443 100644 --- a/src/main/java/com/moandjiezana/toml/Toml.java +++ b/src/main/java/com/moandjiezana/toml/Toml.java @@ -246,13 +246,14 @@ public class Toml { @SuppressWarnings("unchecked") private Object get(String key) { - String[] split = key.split("\\."); + if (values.containsKey(key)) { + return values.get(key); + } + + String[] split = Keys.split(key); Object current = new HashMap(values); for (int i = 0; i < split.length; i++) { - if (i == 0 && values.containsKey(key)) { - return values.get(key); - } String keyWithDot = join(Arrays.copyOfRange(split, i, split.length)); if (current instanceof Map && ((Map) current).containsKey(keyWithDot)) { diff --git a/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java b/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java new file mode 100644 index 0000000..4be4d80 --- /dev/null +++ b/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java @@ -0,0 +1,67 @@ +package com.moandjiezana.toml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Map; + +import org.junit.Test; + +public class QuotedKeysTest { + + @Test + public void should_accept_quoted_key_for_value() throws Exception { + Toml toml = new Toml().parse("\"127.0.0.1\" = \"localhost\" \n \"character encoding\" = \"UTF-8\" \n \"ʎǝʞ\" = \"value\""); + + assertEquals("localhost", toml.getString("\"127.0.0.1\"")); + assertEquals("UTF-8", toml.getString("\"character encoding\"")); + assertEquals("value", toml.getString("\"ʎǝʞ\"")); + } + + @Test + public void should_accept_quoted_key_for_table_name() throws Exception { + Toml toml = new Toml().parse("[\"abc def\"]\n val = 1"); + + assertEquals(1L, toml.getTable("\"abc def\"").getLong("val").longValue()); + } + + @Test + public void should_accept_partially_quoted_table_name() throws Exception { + Toml toml = new Toml().parse("[dog.\"tater.man\"] \n type = \"pug0\" \n[dog.tater] \n type = \"pug1\"\n[dog.tater.man] \n type = \"pug2\""); + Toml dogs = toml.getTable("dog"); + + assertEquals("pug0", dogs.getTable("\"tater.man\"").getString("type")); + assertEquals("pug1", dogs.getTable("tater").getString("type")); + assertEquals("pug2", dogs.getTable("tater").getTable("man").getString("type")); + assertEquals("pug0", toml.getString("dog.\"tater.man\".type")); + assertEquals("pug2", toml.getString("dog.tater.man.type")); + } + + @Test + @SuppressWarnings("unchecked") + public void should_conserve_quoted_key_in_map() throws Exception { + Toml toml = new Toml().parse("[dog.\"tater.man\"] \n type = \"pug0\" \n[dog.tater] \n type = \"pug1\"\n[dog.tater.man] \n type = \"pug2\""); + Toml dogs = toml.getTable("dog"); + + Map> map = dogs.to(Map.class); + + assertEquals("pug0", map.get("\"tater.man\"").get("type")); + assertEquals("pug1", map.get("tater").get("type")); + assertEquals("pug2", ((Map) map.get("tater").get("man")).get("type")); + } + + @Test + public void should_convert() throws Exception { + Quoted quoted = new Toml().parse("\"ʎǝʞ\" = \"value\" \n[map] \n \"ʎǝʞ\" = \"value\"").to(Quoted.class); + + assertNull(quoted.ʎǝʞ); + assertEquals("value", quoted.map.get("\"ʎǝʞ\"")); + } + + private static class Quoted { + + String ʎǝʞ; + + Map map; + } +} From 8fd7e6691ccff3ea3cf412195aecfc287f88592f Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Wed, 14 Jan 2015 10:07:26 +0200 Subject: [PATCH 10/12] Changed badges to match wip branch [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df15357..13fc33a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ toml4j is a [TOML 0.3.1](https://github.com/toml-lang/toml/tree/v0.3.1) parser for Java. -[![Maven Central](https://img.shields.io/maven-central/v/com.moandjiezana.toml/toml4j.svg)](https://search.maven.org/#search|gav|1|g%3A%22com.moandjiezana.toml%22%20AND%20a%3A%22toml4j%22) [![Build Status](https://img.shields.io/travis/mwanji/toml4j.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j) +[![Maven Central](https://img.shields.io/maven-central/v/com.moandjiezana.toml/toml4j.svg)](https://search.maven.org/#search|gav|1|g%3A%22com.moandjiezana.toml%22%20AND%20a%3A%22toml4j%22) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build Status](https://img.shields.io/travis/mwanji/toml4j/wip.svg)](https://travis-ci.org/mwanji/toml4j) [![Coverage Status](https://img.shields.io/coveralls/mwanji/toml4j.svg)](https://coveralls.io/r/mwanji/toml4j?branch=wip) For the bleeding-edge version integrating the latest specs, see the [work-in-progress branch](https://github.com/mwanji/toml4j/tree/wip). From 62b2718a170d4f7eea858996ffbc24930897c3e6 Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Thu, 15 Jan 2015 11:02:22 +0200 Subject: [PATCH 11/12] Matched README to wip branch --- README.md | 63 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 13fc33a..f3a69a8 100644 --- a/README.md +++ b/README.md @@ -10,32 +10,32 @@ For the bleeding-edge version integrating the latest specs, see the [work-in-pro Add the following dependency to your POM (or equivalent for other dependency managers): -````xml +```xml com.moandjiezana.toml toml4j 0.3.1 -```` +``` Requires Java 1.6. ## Quick start -````java +```java Toml toml = new Toml().parse(getTomlFile()); String someValue = toml.getString("someKey"); Date someDate = toml.getDate("someTable.someDate"); MyClass myClass = toml.to(MyClass.class); -```` +``` ## Usage A `com.moandjiezana.toml.Toml` instance is populated by calling one of `parse(File)`, `parse(InputStream)`, `parse(Reader)` or `parse(String)`. -````java +```java Toml toml = new Toml().parse("a=1"); -```` +``` An exception is thrown if the source is not valid TOML. @@ -45,7 +45,7 @@ The data can then be accessed either by converting the Toml instance to your own `Toml#to(Class)` maps a Toml instance to the given class. -```` +```toml name = "Mwanji Ezana" [address] @@ -54,9 +54,9 @@ name = "Mwanji Ezana" [contacts] "email address" = me@example.com -```` +``` -````java +```java class Address { String street; String city; @@ -67,28 +67,31 @@ class User { Address address; Map contacts; } -```` +``` -````java +```java User user = new Toml().parse(tomlFile).to(User.class); assert user.name.equals("Mwanji Ezana"); assert user.address.street.equals("123 A Street"); assert user.contacts.get("\"email address\"").equals("me@example.com"); -```` +``` Any keys not found in both the TOML and the class are ignored. Fields may be private. Quoted keys cannot be mapped directly to a Java object, but they can be used as keys within a `Map`. -All TOML primitives can be mapped, as well as a number of Java-specific types: +TOML primitives can be mapped to a number of Java types: -* A TOML Number can be converted to any primitive type (or the wrapper equivalent), `BigInteger` or `BigDecimal` -* A TOML string can be converted to a `String`, enum, `java.net.URI` or `java.net.URL` -* A single-letter TOML string can be converted to a `char` or `Character` -* Multiline and literal TOML strings can be converted to `String` -* A TOML array can be converted to a `List`, `Set` or array. The generic type can be anything that can be converted. -* A TOML table can be converted to a custom class or to a `Map`. The generic type of the value can be anything that can be converted. +TOML | Java +---- | ---- +Integer | `int`, `long` (or wrapper), `java.math.BigInteger` +Float | `float`, `double` (or wrapper), `java.math.BigDecimal` +String | `String`, enum, `java.net.URI`, `java.net.URL` +One-letter String | `char`, `Character` +Multiline and Literal Strings | `String` +Array | `List`, `Set`, array. The generic type can be anything that can be converted. +Table | Custom class, `Map` Custom classes, Maps and collections thereof can be nested to any level. See [TomlToClassTest#should_convert_fruit_table_array()](src/test/java/com/moandjiezana/toml/TomlToClassTest.java) for an example. @@ -109,9 +112,9 @@ You can also navigate values within a table with a compound key of the form `tab Non-existent keys return null. -When retrieving quoted keys, the quotes must be used and the key must be spelled exactly the same way, including whitespace. +When retrieving quoted keys, the quotes must be used and the key must be spelled exactly the same way, including quotes and whitespace. -```` +```toml title = "TOML Example" "sub title" = "Now with quoted keys" @@ -140,9 +143,9 @@ title = "TOML Example" location = "Geneva" [[networks.operators]] location = "Paris" -```` +``` -````java +```java Toml toml = new Toml().parse(getTomlFile()); String title = toml.getString("title"); @@ -159,13 +162,13 @@ Toml network1 = toml.getTable("networks[0]"); String network2Name = toml.getString("networks[1].name"); // "Level 2" List network3Operators = toml.getTables("networks[2].operators"); String network3Operator2Location = toml.getString("networks[2].operators[1].location"); // "Paris" -```` +``` ### Defaults The constructor can be given a set of default values that will be used as fallbacks. For tables and table arrays, a shallow merge is performed. -```` +```toml # defaults a = 2 b = 3 @@ -173,9 +176,9 @@ b = 3 [table] c = 4 d = 5 -```` +``` -```` +```toml a = 1 [table] @@ -183,9 +186,9 @@ a = 1 [[array]] d = 3 -```` +``` -````java +```java Toml defaults = new Toml().parse(getDefaultsFile()); Toml toml = new Toml(defaults).parse(getTomlFile()); @@ -195,7 +198,7 @@ Long c = toml.getLong("c"); // returns null Long tableC = toml.getLong("table.c"); // returns 2, not 4 Long tableD = toml.getLong("table.d"); // returns null, not 5 Long arrayD = toml.getLong("array[0].d"); // returns 3 -```` +``` ### Limitations From df235e5a292d96e985b0f77b497402ee52a19f8d Mon Sep 17 00:00:00 2001 From: "moandji.ezana" Date: Thu, 15 Jan 2015 11:44:14 +0200 Subject: [PATCH 12/12] Added support for quoted keys with index arrays --- src/main/java/com/moandjiezana/toml/Keys.java | 87 +++++++++++++++++-- .../java/com/moandjiezana/toml/Results.java | 4 +- .../moandjiezana/toml/StringConverter.java | 33 +++---- src/main/java/com/moandjiezana/toml/Toml.java | 44 ++-------- .../com/moandjiezana/toml/TomlParser.java | 7 +- .../com/moandjiezana/toml/QuotedKeysTest.java | 25 +++++- 6 files changed, 132 insertions(+), 68 deletions(-) diff --git a/src/main/java/com/moandjiezana/toml/Keys.java b/src/main/java/com/moandjiezana/toml/Keys.java index 7478b8f..bffb319 100644 --- a/src/main/java/com/moandjiezana/toml/Keys.java +++ b/src/main/java/com/moandjiezana/toml/Keys.java @@ -1,33 +1,102 @@ package com.moandjiezana.toml; +import static com.moandjiezana.toml.ValueConverterUtils.isComment; + import java.util.ArrayList; import java.util.List; class Keys { - static String[] split(String key) { - List splitKey = new ArrayList(); + static class Key { + final String name; + final int index; + final String path; + + Key(String name, int index, Key next) { + this.name = name; + this.index = index; + if (next != null) { + this.path = name + "." + next.path; + } else { + this.path = name; + } + } + } + + static Keys.Key[] split(String key) { + List splitKey = new ArrayList(); StringBuilder current = new StringBuilder(); char[] chars = key.toCharArray(); boolean quoted = false; + boolean indexable = true; + boolean inIndex = false; + int index = -1; - for (char c : chars) { - if (c == '"') { + for (int i = chars.length - 1; i > -1; i--) { + char c = chars[i]; + if (c == ']' && indexable) { + inIndex = true; + continue; + } + indexable = false; + if (c == '[' && inIndex) { + inIndex = false; + index = Integer.parseInt(current.toString()); + current = new StringBuilder(); + continue; + } + if (c == '"' && (i == 0 || chars[i - 1] != '\\')) { quoted = !quoted; + indexable = false; } if (c != '.' || quoted) { - current.append(c); + current.insert(0, c); } else { - splitKey.add(current.toString()); + splitKey.add(0, new Key(current.toString(), index, !splitKey.isEmpty() ? splitKey.get(0) : null)); + indexable = true; + index = -1; current = new StringBuilder(); } } - splitKey.add(current.toString()); + splitKey.add(0, new Key(current.toString(), index, !splitKey.isEmpty() ? splitKey.get(0) : null)); - return splitKey.toArray(new String[0]); + return splitKey.toArray(new Key[0]); + } + + /** + * @param line raw TOML iine to parse + * @return null if line is not a valid table identifier + */ + static String getTableName(String line) { + StringBuilder sb = new StringBuilder(); + char[] chars = line.toCharArray(); + boolean quoted = false; + boolean terminated = false; + + for (int i = 1; i < chars.length; i++) { + char c = chars[i]; + if (c == '"' && chars[i - 1] != '\\') { + quoted = !quoted; + } else if (!quoted && c == ']') { + terminated = true; + break; + } else if (!quoted && c == '[') { + break; + } + + sb.append(c); + } + + String tableName = sb.toString(); + + if (!terminated || !isComment(line.substring(tableName.length() + 2))) { + return null; + } + + tableName = StringConverter.STRING_PARSER.replaceUnicodeCharacters(tableName); + return tableName; } private Keys() {} - } diff --git a/src/main/java/com/moandjiezana/toml/Results.java b/src/main/java/com/moandjiezana/toml/Results.java index 73ad666..1a3ae9b 100644 --- a/src/main/java/com/moandjiezana/toml/Results.java +++ b/src/main/java/com/moandjiezana/toml/Results.java @@ -75,9 +75,9 @@ class Results { stack.pop(); } - String[] tableParts = Keys.split(tableName); + Keys.Key[] tableParts = Keys.split(tableName); for (int i = 0; i < tableParts.length; i++) { - String tablePart = tableParts[i]; + String tablePart = tableParts[i].name; Container currentContainer = stack.peek(); if (tablePart.isEmpty()) { errors.append("Empty implicit table: " + tableName + "!\n"); diff --git a/src/main/java/com/moandjiezana/toml/StringConverter.java b/src/main/java/com/moandjiezana/toml/StringConverter.java index 05acfe7..2971e27 100644 --- a/src/main/java/com/moandjiezana/toml/StringConverter.java +++ b/src/main/java/com/moandjiezana/toml/StringConverter.java @@ -35,25 +35,16 @@ class StringConverter implements ValueConverter { value = value.substring(1, stringTerminator); value = replaceUnicodeCharacters(value); - - chars = value.toCharArray(); - for (int i = 0; i < chars.length - 1; i++) { - char ch = chars[i]; - char next = chars[i + 1]; - - if (ch == '\\' && next == '\\') { - i++; - } else if (ch == '\\' && !(next == 'b' || next == 'f' || next == 'n' || next == 't' || next == 'r' || next == '"' || next == '/' || next == '\\')) { - return INVALID; - } - } - value = replaceSpecialCharacters(value); + + if (value == null) { + return INVALID; + } return value; } - private String replaceUnicodeCharacters(String value) { + String replaceUnicodeCharacters(String value) { Matcher unicodeMatcher = UNICODE_REGEX.matcher(value); while (unicodeMatcher.find()) { @@ -62,7 +53,19 @@ class StringConverter implements ValueConverter { return value; } - private String replaceSpecialCharacters(String value) { + String replaceSpecialCharacters(String value) { + char[] chars = value.toCharArray(); + for (int i = 0; i < chars.length - 1; i++) { + char ch = chars[i]; + char next = chars[i + 1]; + + if (ch == '\\' && next == '\\') { + i++; + } else if (ch == '\\' && !(next == 'b' || next == 'f' || next == 'n' || next == 't' || next == 'r' || next == '"' || next == '/' || next == '\\')) { + return null; + } + } + return value.replace("\\n", "\n") .replace("\\\"", "\"") .replace("\\t", "\t") diff --git a/src/main/java/com/moandjiezana/toml/Toml.java b/src/main/java/com/moandjiezana/toml/Toml.java index a95b443..f79b5b9 100644 --- a/src/main/java/com/moandjiezana/toml/Toml.java +++ b/src/main/java/com/moandjiezana/toml/Toml.java @@ -12,14 +12,12 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Date; 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 com.google.gson.Gson; @@ -250,52 +248,28 @@ public class Toml { return values.get(key); } - String[] split = Keys.split(key); Object current = new HashMap(values); - for (int i = 0; i < split.length; i++) { - - String keyWithDot = join(Arrays.copyOfRange(split, i, split.length)); - if (current instanceof Map && ((Map) current).containsKey(keyWithDot)) { - return ((Map) current).get(keyWithDot); - } - - String splitKey = split[i]; - Matcher matcher = ARRAY_INDEX_PATTERN.matcher(splitKey); - int index = -1; - - if (matcher.find()) { - splitKey = matcher.group(1); - index = Integer.parseInt(matcher.group(2), 10); + Keys.Key[] keys = Keys.split(key); + + for (Keys.Key k : keys) { + if (k.index == -1 && current instanceof Map && ((Map) current).containsKey(k.path)) { + return ((Map) current).get(k.path); } - current = ((Map) current).get(splitKey); + current = ((Map) current).get(k.name); - if (index > -1 && current != null) { - current = ((List) current).get(index); + if (k.index > -1 && current != null) { + current = ((List) current).get(k.index); } if (current == null) { return defaults != null ? defaults.get(key) : null; } } - + return current; } - - private String join(String[] strings) { - StringBuilder sb = new StringBuilder(); - - for (String string : strings) { - sb.append(string).append('.'); - } - - if (sb.length() > 0) { - sb.deleteCharAt(sb.length() - 1); - } - - return sb.toString(); - } private Toml(Toml defaults, Map values) { this.values = values != null ? values : Collections.emptyMap(); diff --git a/src/main/java/com/moandjiezana/toml/TomlParser.java b/src/main/java/com/moandjiezana/toml/TomlParser.java index d514c86..8e3594b 100644 --- a/src/main/java/com/moandjiezana/toml/TomlParser.java +++ b/src/main/java/com/moandjiezana/toml/TomlParser.java @@ -186,12 +186,7 @@ class TomlParser { } private String getTableName(String line) { - List resultValue = parse(parser().Table(), line); - if (resultValue == null) { - return null; - } - - return (String) resultValue.get(0); + return Keys.getTableName(line); } private boolean isKeyValid(String key) { diff --git a/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java b/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java index 4be4d80..0e8402d 100644 --- a/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java +++ b/src/test/java/com/moandjiezana/toml/QuotedKeysTest.java @@ -51,13 +51,36 @@ public class QuotedKeysTest { } @Test - public void should_convert() throws Exception { + public void should_convert_quoted_keys_to_map_but_not_to_object_fields() throws Exception { Quoted quoted = new Toml().parse("\"ʎǝʞ\" = \"value\" \n[map] \n \"ʎǝʞ\" = \"value\"").to(Quoted.class); assertNull(quoted.ʎǝʞ); assertEquals("value", quoted.map.get("\"ʎǝʞ\"")); } + @Test + public void should_support_table_array_index_with_quoted_key() throws Exception { + Toml toml = new Toml().parse("[[dog.\" type\"]] \n name = \"type0\" \n [[dog.\" type\"]] \n name = \"type1\""); + + assertEquals("type0", toml.getString("dog.\" type\"[0].name")); + assertEquals("type1", toml.getString("dog.\" type\"[1].name")); + } + + @Test + public void should_support_quoted_key_containing_square_brackets() throws Exception { + Toml toml = new Toml().parse("[dog.\" type[abc]\"] \n name = \"type0\" \n [dog.\" type[1]\"] \n \"name[]\" = \"type1\""); + + assertEquals("type0", toml.getString("dog.\" type[abc]\".name")); + assertEquals("type1", toml.getString("dog.\" type[1]\".\"name[]\"")); + } + + @Test + public void should_support_quoted_key_containing_escaped_quote() throws Exception { + Toml toml = new Toml().parse("[dog.\"ty\\\"pe\"] \n \"na\\\"me\" = \"type0\""); + + assertEquals("type0", toml.getString("dog.\"ty\\\"pe\".\"na\\\"me\"")); + } + private static class Quoted { String ʎǝʞ;