Merge remote-tracking branch 'upstream/wip' into merge

Conflicts:
	README.md
	src/main/java/com/moandjiezana/toml/Toml.java
This commit is contained in:
Jonathan Wood 2015-06-24 21:49:24 -07:00
commit f8f55a6f16
13 changed files with 365 additions and 40 deletions

View file

@ -5,4 +5,4 @@ jdk:
- openjdk7
- openjdk6
after_success:
- mvn clean test jacoco:report coveralls:jacoco
- mvn clean test jacoco:report coveralls:report

View file

@ -5,10 +5,12 @@
### Changed
* __BREAKING:__ Toml#getList(String), Toml#getTable(String) and Toml#getTables(String) return null when key is not found
* Removed trailing newline from error messages
### Added
* Support for underscores in numbers (the feature branch had accidentally not been merged! :( )
* Set<Map.Entry> Toml#entrySet() cf. Reflection section in README
* Overloaded getters that take a default value. Thanks to __[udiabon](https://github.com/udiabon)__.
## 0.4.0 / 2015-02-16

View file

@ -204,6 +204,35 @@ Long tableD = toml.getLong("table.d"); // returns null, not 5, because of shallo
Long arrayD = toml.getLong("array[0].d"); // returns 3
```
### Reflection
`Toml#entrySet()` returns a Set of [Map.Entry](http://docs.oracle.com/javase/6/docs/api/java/util/Map.Entry.html) instances. Modifications to the returned Set are not reflected in the Toml instance. Note that Map.Entry#setValue() will throw an UnsupportedOperationException.
```java
for (Map.Entry<String, Object> entry : myToml.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
```
You can also convert a Toml instance to a `Map<String, Object>`:
```java
Toml toml = new Toml().parse("a = 1");
Map<String, Object> map = toml.to(Map.class);
```
`Toml#contains(String)` verifies that the instance contains a key of any type (primitive, table or array of tables) of the given name. `Toml#containsKey(String)`, `Toml#containsTable(String)` and `Toml#containsTableArray(String)` return true only if a key exists and is a primitive, table or array of tables, respectively. Compound keys can be used to check existence at any depth.
```java
Toml toml = new Toml().parse("a = 1");
toml.contains("a"); // true
toml.conatinsKey("a"); // true
toml.containsTable("a"); // false
toml.containsTableArray("a"); // false
```
### Serialization
You can serialize a `Toml` or any arbitrary object to a TOML string.

14
pom.xml
View file

@ -56,12 +56,6 @@
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
@ -74,7 +68,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<version>3.3</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
@ -83,7 +77,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.2.201409121644</version>
<version>0.7.5.201505241946</version>
<executions>
<execution>
<id>prepare-agent</id>
@ -96,7 +90,7 @@
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>3.0.1</version>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
@ -114,7 +108,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>

View file

@ -19,7 +19,7 @@ class Results {
.append(line)
.append(": [")
.append(table)
.append("]\n");
.append("]");
}
public void tableDuplicatesKey(String table, AtomicInteger line) {
@ -27,21 +27,19 @@ class Results {
.append(line.get())
.append(": [")
.append(table)
.append("]\n");
.append("]");
}
public void keyDuplicatesTable(String key, AtomicInteger line) {
sb.append("Table already exists for key defined on line ")
.append(line.get())
.append(": ")
.append(key)
.append('\n');
.append(key);
}
void emptyImplicitTable(String table, int line) {
sb.append("Invalid table definition due to empty implicit table name: ")
.append(table)
.append("\n");
.append(table);
}
void invalidTable(String table, int line) {
@ -49,7 +47,7 @@ class Results {
.append(line)
.append(": ")
.append(table)
.append("]\n");
.append("]");
}
void duplicateKey(String key, int line) {
@ -59,8 +57,7 @@ class Results {
.append(line);
}
sb.append(": ")
.append(key)
.append('\n');
.append(key);
}
void invalidTextAfterIdentifier(Identifier identifier, char text, int line) {
@ -68,24 +65,21 @@ class Results {
.append(identifier.getName())
.append(" on line ")
.append(line)
.append(". Make sure to terminate the value or add a comment (#).")
.append('\n');
.append(". Make sure to terminate the value or add a comment (#).");
}
void invalidKey(String key, int line) {
sb.append("Invalid key on line ")
.append(line)
.append(": ")
.append(key)
.append('\n');
.append(key);
}
void invalidTableArray(String tableArray, int line) {
sb.append("Invalid table array definition on line ")
.append(line)
.append(": ")
.append(tableArray)
.append('\n');
.append(tableArray);
}
void invalidValue(String key, String value, int line) {
@ -94,16 +88,14 @@ class Results {
.append(": ")
.append(key)
.append(" = ")
.append(value)
.append('\n');
.append(value);
}
void unterminatedKey(String key, int line) {
sb.append("Key is not followed by an equals sign on line ")
.append(line)
.append(": ")
.append(key)
.append('\n');
.append(key);
}
void unterminated(String key, String value, int line) {
@ -112,15 +104,13 @@ class Results {
.append(": ")
.append(key)
.append(" = ")
.append(value.trim())
.append('\n');
.append(value.trim());
}
public void heterogenous(String key, int line) {
sb.append(key)
.append(" becomes a heterogeneous array on line ")
.append(line)
.append('\n');
.append(line);
}
boolean hasErrors() {

View file

@ -1,11 +1,24 @@
package com.moandjiezana.toml;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import java.io.*;
import java.util.*;
/**
* <p>Provides access to the keys and tables in a TOML data source.</p>
*
@ -26,7 +39,7 @@ import java.util.*;
*
*/
public class Toml {
private static final Gson DEFAULT_GSON = new Gson();
private Map<String, Object> values = new HashMap<String, Object>();
@ -220,6 +233,44 @@ public class Toml {
return tables;
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present
*/
public boolean contains(String key) {
return get(key) != null;
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present and is a primitive
*/
public boolean containsKey(String key) {
Object object = get(key);
return object != null && !(object instanceof Map) && !(object instanceof List);
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present and is a table
*/
public boolean containsTable(String key) {
Object object = get(key);
return object != null && (object instanceof Map);
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present and is a table array
*/
public boolean containsTableArray(String key) {
Object object = get(key);
return object != null && (object instanceof List);
}
public boolean isEmpty() {
return values.isEmpty();
}
@ -268,6 +319,58 @@ public class Toml {
return DEFAULT_GSON.fromJson(json, targetClass);
}
/**
* @return a {@link Set} of Map.Entry instances. Modifications to the {@link Set} are not reflected in this Toml instance. Entries are immutable, so {@link Map.Entry#setValue(Object)} throws an UnsupportedOperationException.
*/
public Set<Map.Entry<String,Object>> entrySet() {
Set<Map.Entry<String, Object>> entries = new LinkedHashSet<Map.Entry<String, Object>>();
for (Map.Entry<String, Object> entry : values.entrySet()) {
Class<? extends Object> entryClass = entry.getValue().getClass();
if (Map.class.isAssignableFrom(entryClass)) {
entries.add(new Toml.Entry(entry.getKey(), getTable(entry.getKey())));
} else if (List.class.isAssignableFrom(entryClass)) {
List<?> value = (List<?>) entry.getValue();
if (!value.isEmpty() && value.get(0) instanceof Map) {
entries.add(new Toml.Entry(entry.getKey(), getTables(entry.getKey())));
} else {
entries.add(new Toml.Entry(entry.getKey(), value));
}
} else {
entries.add(new Toml.Entry(entry.getKey(), entry.getValue()));
}
}
return entries;
}
private class Entry implements Map.Entry<String, Object> {
private final String key;
private final Object value;
@Override
public String getKey() {
return key;
}
@Override
public Object getValue() {
return value;
}
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException("TOML entry values cannot be changed.");
}
private Entry(String key, Object value) {
this.key = key;
this.value = value;
}
}
/**
* Serializes the values of this Toml instance into TOML.
@ -324,9 +427,9 @@ public class Toml {
return current;
}
private Toml(Toml defaults, Map<String, Object> values) {
this.values = values != null ? values : Collections.<String, Object>emptyMap();
this.values = values;
this.defaults = defaults;
}
}

View file

@ -0,0 +1,79 @@
package com.moandjiezana.toml;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class ContainsTest {
@Test
public void should_contain_top_level_of_any_type() throws Exception {
Toml toml = new Toml().parse("a = 1 \n [b] \n b1 = 1 \n [[c]] \n c1 = 1");
assertTrue(toml.contains("a"));
assertTrue(toml.contains("b"));
assertTrue(toml.contains("c"));
assertFalse(toml.contains("d"));
}
@Test
public void should_contain_nested_of_any_type() throws Exception {
Toml toml = new Toml().parse("[a] \n a1 = 1 \n [[b]] \n b1 = 1 \n [[b]] \n b1 = 2 \n b2 = 3");
assertTrue(toml.contains("a.a1"));
assertTrue(toml.contains("b[0].b1"));
assertTrue(toml.contains("b[1].b1"));
assertFalse(toml.contains("b[2].b1"));
assertFalse(toml.contains("c.d"));
}
@Test
public void should_contain_key() throws Exception {
Toml toml = new Toml().parse("a = 1 \n [b] \n b1 = 1 \n [[c]] \n c1 = 1");
assertTrue(toml.containsKey("a"));
assertTrue(toml.containsKey("b.b1"));
assertTrue(toml.containsKey("c[0].c1"));
assertFalse(toml.containsKey("b"));
assertFalse(toml.containsKey("c"));
assertFalse(toml.containsKey("d"));
}
@Test
public void should_contain_table() throws Exception {
Toml toml = new Toml().parse("a = 1 \n [b] \n b1 = 1 \n [b.b2] \n [[c]] \n c1 = 1 \n [c.c2]");
assertTrue(toml.containsTable("b"));
assertTrue(toml.containsTable("b.b2"));
assertTrue(toml.containsTable("c[0].c2"));
assertFalse(toml.containsTable("a"));
assertFalse(toml.containsTable("b.b1"));
assertFalse(toml.containsTable("c"));
assertFalse(toml.containsTable("c[0].c1"));
assertFalse(toml.containsTable("d"));
}
@Test
public void should_contain_table_array() throws Exception {
Toml toml = new Toml().parse("a = 1 \n [b] \n b1 = 1 \n [[c]] \n c1 = 1 \n [c.c2] \n [[c]] \n [[c.c3]] \n c4 = 4");
assertTrue(toml.containsTableArray("c"));
assertTrue(toml.containsTableArray("c[1].c3"));
assertFalse(toml.containsTableArray("a"));
assertFalse(toml.containsTableArray("b"));
assertFalse(toml.containsTableArray("b.b1"));
assertFalse(toml.containsTableArray("c[1].c3[0].c4"));
assertFalse(toml.containsTableArray("d"));
}
@Test
public void should_not_contain_when_parent_table_is_missing() throws Exception {
Toml toml = new Toml().parse("a = \"1\"");
assertFalse(toml.contains("b.b1"));
assertFalse(toml.containsKey("b.b1"));
assertFalse(toml.containsTable("b.b1"));
assertFalse(toml.containsTableArray("b.b1"));
}
}

View file

@ -0,0 +1,115 @@
package com.moandjiezana.toml;
import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import com.moandjiezana.toml.testutils.Utils;
public class IterationTest {
@Rule
public final ExpectedException expectedException = ExpectedException.none();
@Test
public void should_iterate_over_primitive() throws Exception {
Toml toml = new Toml().parse(file("long"));
Map.Entry<String, Object> entry = toml.entrySet().iterator().next();
assertEquals("long", entry.getKey());
assertEquals(2L, entry.getValue());
}
@Test
@SuppressWarnings("unchecked")
public void should_iterate_over_list() throws Exception {
Toml toml = new Toml().parse(file("list"));
Map.Entry<String, Object> entry = toml.entrySet().iterator().next();
assertEquals("list", entry.getKey());
assertThat((List<String>) entry.getValue(), contains("a", "b", "c"));
}
@Test
@SuppressWarnings("unchecked")
public void should_iterate_over_empty_list() throws Exception {
Toml toml = new Toml().parse("list = []");
Map.Entry<String, Object> entry = toml.entrySet().iterator().next();
assertEquals("list", entry.getKey());
assertThat((List<String>) entry.getValue(), empty());
}
@Test
public void should_iterate_over_table() throws Exception {
Toml toml = new Toml().parse(file("table"));
Map.Entry<String, Object> entry = toml.entrySet().iterator().next();
assertEquals("table", entry.getKey());
assertEquals("a", ((Toml) entry.getValue()).getString("a"));
}
@Test
@SuppressWarnings("unchecked")
public void should_iterate_over_table_array() throws Exception {
Toml toml = new Toml().parse(file("table_array"));
Map.Entry<String, Object> entry = toml.entrySet().iterator().next();
List<Toml> tableArray = (List<Toml>) entry.getValue();
assertEquals("table_array", entry.getKey());
assertThat(tableArray, contains(instanceOf(Toml.class), instanceOf(Toml.class)));
}
@Test
@SuppressWarnings("unchecked")
public void should_iterate_over_multiple_entries() throws Exception {
Toml toml = new Toml().parse(file("multiple"));
Map<String, Object> entries = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : toml.entrySet()) {
entries.put(entry.getKey(), entry.getValue());
}
assertThat(entries.keySet(), containsInAnyOrder("a", "b", "c", "e"));
assertThat(entries, hasEntry("a", (Object) "a"));
assertThat(entries, hasEntry("b", (Object) asList(1L, 2L, 3L)));
assertTrue(((Toml) entries.get("c")).getBoolean("d"));
assertThat(((List<Toml>) entries.get("e")), hasSize(1));
}
@Test
public void should_not_support_setValue_method() throws Exception {
Map.Entry<String, Object> entry = new Toml().parse("a = 1").entrySet().iterator().next();
expectedException.expect(UnsupportedOperationException.class);
entry.setValue(2L);
}
@Test
public void should_not_iterate_over_empty_toml() throws Exception {
Toml toml = new Toml().parse("");
assertThat(toml.entrySet(), empty());
}
private File file(String name) {
return Utils.file(getClass(), "/IteratorTest/" + name);
}
}

View file

@ -0,0 +1 @@
list = ["a", "b", "c"]

View file

@ -0,0 +1 @@
long = 2

View file

@ -0,0 +1,7 @@
a = "a"
b = [1, 2, 3]
[c]
d = true
[[e]]

View file

@ -0,0 +1 @@
table = { a = "a" }

View file

@ -0,0 +1,3 @@
[[table_array]]
[[table_array]]