mirror of
https://github.com/plexusorg/toml4j.git
synced 2024-12-29 11:42:15 +00:00
Arrays handle mixed string types. Removed Parboiled dependency.
This commit is contained in:
parent
84de939b7c
commit
0f198e5a5e
6 changed files with 119 additions and 91 deletions
|
@ -3,6 +3,7 @@
|
||||||
## NEXT
|
## NEXT
|
||||||
* Fixed short-form Unicode escapes
|
* Fixed short-form Unicode escapes
|
||||||
* Fixed exponent handling
|
* Fixed exponent handling
|
||||||
|
* Dropped dependency on Parboiled
|
||||||
* Updated Gson to 2.3.1
|
* Updated Gson to 2.3.1
|
||||||
|
|
||||||
## 0.3.1 / 2014-12-16
|
## 0.3.1 / 2014-12-16
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -30,11 +30,6 @@
|
||||||
<version>4.12</version>
|
<version>4.12</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.parboiled</groupId>
|
|
||||||
<artifactId>parboiled-java</artifactId>
|
|
||||||
<version>1.1.6</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
package com.moandjiezana.toml;
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
|
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
|
||||||
import static com.moandjiezana.toml.ValueConverterUtils.parse;
|
|
||||||
import static com.moandjiezana.toml.ValueConverterUtils.parser;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
class ArrayConverter implements ValueConverter {
|
class ArrayConverter implements ValueConverter {
|
||||||
|
|
||||||
static final ArrayConverter ARRAY_PARSER = new ArrayConverter();
|
static final ArrayConverter ARRAY_PARSER = new ArrayConverter();
|
||||||
|
|
||||||
private static final List<Object> INVALID_ARRAY = new ArrayList<Object>();
|
private static final ValueConverters VALUE_CONVERTERS = new ValueConverters();
|
||||||
private static final ValueConverters VALUE_ANALYSIS = new ValueConverters();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canConvert(String s) {
|
public boolean canConvert(String s) {
|
||||||
|
@ -21,37 +19,105 @@ class ArrayConverter implements ValueConverter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object convert(String s) {
|
public Object convert(String s) {
|
||||||
List<Object> tokens = parse(parser().Array(), s);
|
return convert(s, new AtomicInteger(1), true);
|
||||||
List<Object> values = convertList(tokens);
|
}
|
||||||
|
|
||||||
|
public Object convert(String s, AtomicInteger sharedIndex, boolean topLevel) {
|
||||||
|
char[] chars = s.toCharArray();
|
||||||
|
List<Object> arrayItems = new ArrayList<Object>();
|
||||||
|
boolean terminated = false;
|
||||||
|
StringType stringType = StringType.NONE;
|
||||||
|
StringBuilder current = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 1; i < chars.length; i++, sharedIndex.incrementAndGet()) {
|
||||||
|
char c = chars[i];
|
||||||
|
|
||||||
if (values == INVALID_ARRAY) {
|
if (terminated && !topLevel) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (terminated) {
|
||||||
|
if (c == '#') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return INVALID;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stringType == StringType.NONE) {
|
||||||
|
if (c == ',') {
|
||||||
|
if (current.toString().trim().length() > 0) {
|
||||||
|
arrayItems.add(current.toString());
|
||||||
|
}
|
||||||
|
current = new StringBuilder();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '[') {
|
||||||
|
arrayItems.add(convert(s.substring(i), sharedIndex, false));
|
||||||
|
i = sharedIndex.get();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == ']') {
|
||||||
|
terminated = true;
|
||||||
|
if (current.toString().trim().length() > 0) {
|
||||||
|
arrayItems.add(current.toString());
|
||||||
|
}
|
||||||
|
current = new StringBuilder();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '"' && chars[i - 1] != '\\' && !stringType.accepts(c)) {
|
||||||
|
if (chars.length > i + 2 && chars[i + 1] == c && chars[i + 2] == c) {
|
||||||
|
stringType = stringType.flip(StringType.MULTILINE);
|
||||||
|
} else {
|
||||||
|
stringType = stringType.flip(StringType.BASIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\'' && !stringType.accepts(c)) {
|
||||||
|
if (chars.length > i + 2 && chars[i + 1] == c && chars[i + 2] == c) {
|
||||||
|
stringType = stringType.flip(StringType.MULTILINE_LITERAL);
|
||||||
|
} else {
|
||||||
|
stringType = stringType.flip(StringType.LITERAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!terminated) {
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return convertList(arrayItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> convertList(List<Object> tokens) {
|
private Object convertList(List<Object> tokens) {
|
||||||
ArrayList<Object> nestedList = new ArrayList<Object>();
|
ArrayList<Object> nestedList = new ArrayList<Object>();
|
||||||
|
|
||||||
for (Object token : tokens) {
|
for (Object token : tokens) {
|
||||||
if (token instanceof String) {
|
if (token instanceof String) {
|
||||||
Object converted = VALUE_ANALYSIS.convert(((String) token).trim());
|
Object converted = VALUE_CONVERTERS.convert(((String) token).trim());
|
||||||
if (converted == INVALID) {
|
if (converted == INVALID) {
|
||||||
return INVALID_ARRAY;
|
return INVALID;
|
||||||
}
|
}
|
||||||
if (isHomogenousArray(converted, nestedList)) {
|
if (isHomogenousArray(converted, nestedList)) {
|
||||||
nestedList.add(converted);
|
nestedList.add(converted);
|
||||||
} else {
|
} else {
|
||||||
return INVALID_ARRAY;
|
return INVALID;
|
||||||
}
|
}
|
||||||
} else if (token instanceof List) {
|
} else if (token instanceof List) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<Object> convertedList = convertList((List<Object>) token);
|
List<Object> convertedList = (List<Object>) token;
|
||||||
if (convertedList != INVALID_ARRAY && isHomogenousArray(convertedList, nestedList)) {
|
if (isHomogenousArray(convertedList, nestedList)) {
|
||||||
nestedList.add(convertedList);
|
nestedList.add(convertedList);
|
||||||
} else {
|
} else {
|
||||||
return INVALID_ARRAY;
|
return INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +128,26 @@ class ArrayConverter implements ValueConverter {
|
||||||
private boolean isHomogenousArray(Object o, List<?> values) {
|
private boolean isHomogenousArray(Object o, List<?> values) {
|
||||||
return values.isEmpty() || values.get(0).getClass().isAssignableFrom(o.getClass()) || o.getClass().isAssignableFrom(values.get(0).getClass());
|
return values.isEmpty() || values.get(0).getClass().isAssignableFrom(o.getClass()) || o.getClass().isAssignableFrom(values.get(0).getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static enum StringType {
|
||||||
|
NONE, BASIC, LITERAL, MULTILINE, MULTILINE_LITERAL;
|
||||||
|
|
||||||
|
StringType flip(StringType to) {
|
||||||
|
return this == NONE ? to : NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean accepts(char c) {
|
||||||
|
if (this == BASIC || this == MULTILINE) {
|
||||||
|
return c != '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this == LITERAL || this == MULTILINE_LITERAL) {
|
||||||
|
return c != '\'';
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ArrayConverter() {}
|
private ArrayConverter() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
package com.moandjiezana.toml;
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.parboiled.Parboiled;
|
|
||||||
import org.parboiled.Rule;
|
|
||||||
import org.parboiled.parserunners.BasicParseRunner;
|
|
||||||
|
|
||||||
class ValueConverterUtils {
|
class ValueConverterUtils {
|
||||||
static final Object INVALID = new Object();
|
static final Object INVALID = new Object();
|
||||||
|
|
||||||
static ValueParser parser() {
|
|
||||||
return Parboiled.createParser(ValueParser.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> List<T> parse(Rule rule, String s) {
|
|
||||||
return new BasicParseRunner<List<T>>(rule).run(s).resultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean isComment(String line) {
|
static boolean isComment(String line) {
|
||||||
if (line == null || line.isEmpty()) {
|
if (line == null || line.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package com.moandjiezana.toml;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.parboiled.BaseParser;
|
|
||||||
import org.parboiled.Rule;
|
|
||||||
import org.parboiled.annotations.BuildParseTree;
|
|
||||||
|
|
||||||
@BuildParseTree
|
|
||||||
class ValueParser extends BaseParser<List<Object>> {
|
|
||||||
|
|
||||||
public Rule Array() {
|
|
||||||
return FirstOf(EmptyArray(), Sequence('[', startList(), OneOrMore(FirstOf(NonEmptyArray(), ' ', ',')), ']', endList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Rule NonEmptyArray() {
|
|
||||||
return FirstOf(Array(), OneOrMore(TestNot(']'), FirstOf(StringToken(), Array(), ',', ' ', OtherValue())));
|
|
||||||
}
|
|
||||||
|
|
||||||
Rule StringToken() {
|
|
||||||
return Sequence(Sequence('"', ZeroOrMore(Sequence(TestNot('"'), ANY)), '"'), pushToken(match()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Rule EmptyArray() {
|
|
||||||
return Sequence('[', ']', startList(), endList());
|
|
||||||
}
|
|
||||||
|
|
||||||
Rule OtherValue() {
|
|
||||||
return Sequence(ZeroOrMore(NoneOf("],")), pushToken(match()));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean startList() {
|
|
||||||
ArrayList<Object> newTokens = new ArrayList<Object>();
|
|
||||||
|
|
||||||
if (!getContext().getValueStack().isEmpty()) {
|
|
||||||
peek().add(newTokens);
|
|
||||||
}
|
|
||||||
push(newTokens);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean endList() {
|
|
||||||
if (getContext().getValueStack().size() > 1) {
|
|
||||||
pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean pushToken(String s) {
|
|
||||||
peek().add(s);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.moandjiezana.toml;
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -57,6 +59,20 @@ public class ArrayTest {
|
||||||
assertEquals(asList(1L, 2L, 3L), toml.getList("key", Long.class));
|
assertEquals(asList(1L, 2L, 3L), toml.getList("key", Long.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_support_mixed_string_types() throws Exception {
|
||||||
|
Toml toml = new Toml().parse("key = [\"a\", 'b', \"\"\"c\"\"\", '''d''']");
|
||||||
|
|
||||||
|
assertThat(toml.getList("key", String.class), contains("a", "b", "c", "d"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_support_array_terminator_in_strings() throws Exception {
|
||||||
|
Toml toml = new Toml().parse("key = [\"a]\", 'b]', \"\"\"c]\"\"\", '''d]''']");
|
||||||
|
|
||||||
|
assertThat(toml.getList("key", String.class), contains("a]", "b]", "c]", "d]"));
|
||||||
|
}
|
||||||
|
|
||||||
private File file(String file) {
|
private File file(String file) {
|
||||||
return Utils.file(getClass(), file);
|
return Utils.file(getClass(), file);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue