mirror of
https://github.com/plexusorg/toml4j.git
synced 2024-12-28 19:24: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
|
||||
* Fixed short-form Unicode escapes
|
||||
* Fixed exponent handling
|
||||
* Dropped dependency on Parboiled
|
||||
* Updated Gson to 2.3.1
|
||||
|
||||
## 0.3.1 / 2014-12-16
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -30,11 +30,6 @@
|
|||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.parboiled</groupId>
|
||||
<artifactId>parboiled-java</artifactId>
|
||||
<version>1.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
package com.moandjiezana.toml;
|
||||
|
||||
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.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
class ArrayConverter implements ValueConverter {
|
||||
|
||||
static final ArrayConverter ARRAY_PARSER = new ArrayConverter();
|
||||
|
||||
private static final List<Object> INVALID_ARRAY = new ArrayList<Object>();
|
||||
private static final ValueConverters VALUE_ANALYSIS = new ValueConverters();
|
||||
private static final ValueConverters VALUE_CONVERTERS = new ValueConverters();
|
||||
|
||||
@Override
|
||||
public boolean canConvert(String s) {
|
||||
|
@ -21,37 +19,105 @@ class ArrayConverter implements ValueConverter {
|
|||
|
||||
@Override
|
||||
public Object convert(String s) {
|
||||
List<Object> tokens = parse(parser().Array(), s);
|
||||
List<Object> values = convertList(tokens);
|
||||
return convert(s, new AtomicInteger(1), true);
|
||||
}
|
||||
|
||||
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 values;
|
||||
|
||||
return convertList(arrayItems);
|
||||
}
|
||||
|
||||
private List<Object> convertList(List<Object> tokens) {
|
||||
private Object convertList(List<Object> tokens) {
|
||||
ArrayList<Object> nestedList = new ArrayList<Object>();
|
||||
|
||||
for (Object token : tokens) {
|
||||
if (token instanceof String) {
|
||||
Object converted = VALUE_ANALYSIS.convert(((String) token).trim());
|
||||
Object converted = VALUE_CONVERTERS.convert(((String) token).trim());
|
||||
if (converted == INVALID) {
|
||||
return INVALID_ARRAY;
|
||||
return INVALID;
|
||||
}
|
||||
if (isHomogenousArray(converted, nestedList)) {
|
||||
nestedList.add(converted);
|
||||
} else {
|
||||
return INVALID_ARRAY;
|
||||
return INVALID;
|
||||
}
|
||||
} else if (token instanceof List) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> convertedList = convertList((List<Object>) token);
|
||||
if (convertedList != INVALID_ARRAY && isHomogenousArray(convertedList, nestedList)) {
|
||||
List<Object> convertedList = (List<Object>) token;
|
||||
if (isHomogenousArray(convertedList, nestedList)) {
|
||||
nestedList.add(convertedList);
|
||||
} else {
|
||||
return INVALID_ARRAY;
|
||||
return INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +128,26 @@ class ArrayConverter implements ValueConverter {
|
|||
private boolean isHomogenousArray(Object o, List<?> values) {
|
||||
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() {}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
package com.moandjiezana.toml;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.parboiled.Parboiled;
|
||||
import org.parboiled.Rule;
|
||||
import org.parboiled.parserunners.BasicParseRunner;
|
||||
|
||||
class ValueConverterUtils {
|
||||
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) {
|
||||
if (line == null || line.isEmpty()) {
|
||||
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;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -57,6 +59,20 @@ public class ArrayTest {
|
|||
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) {
|
||||
return Utils.file(getClass(), file);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue