toml4j/src/main/java/com/moandjiezana/toml/ArrayConverter.java
2015-02-10 14:32:44 +02:00

158 lines
4.2 KiB
Java

package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
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 ValueConverters VALUE_CONVERTERS = new ValueConverters();
@Override
public boolean canConvert(String s) {
return s.startsWith("[");
}
@Override
public Object convert(String s) {
AtomicInteger sharedIndex = new AtomicInteger(1);
Object converted = convert(s, sharedIndex);
char[] chars = s.toCharArray();
for (int i = sharedIndex.incrementAndGet(); i < chars.length; i++) {
char c = chars[i];
if (c == '#') {
break;
}
if (!Character.isWhitespace(c)) {
return INVALID;
}
}
return converted;
}
Object convert(String s, AtomicInteger sharedIndex) {
char[] chars = s.toCharArray();
List<Object> arrayItems = new ArrayList<Object>();
boolean terminated = false;
StringType stringType = StringType.NONE;
StringBuilder current = new StringBuilder();
for (; sharedIndex.get() < chars.length; sharedIndex.incrementAndGet()) {
int i = sharedIndex.get();
char c = chars[sharedIndex.get()];
if (stringType == StringType.NONE) {
if (c == ',') {
if (current.toString().trim().length() > 0) {
arrayItems.add(current.toString());
}
current = new StringBuilder();
continue;
}
if (c == '[') {
sharedIndex.incrementAndGet();
arrayItems.add(convert(s, sharedIndex));
continue;
}
if (c == ']') {
terminated = true;
if (current.toString().trim().length() > 0) {
arrayItems.add(current.toString());
}
current = new StringBuilder();
break;
}
}
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 convertList(arrayItems);
}
private Object convertList(List<Object> tokens) {
ArrayList<Object> nestedList = new ArrayList<Object>();
for (Object token : tokens) {
if (token instanceof String) {
Object converted = VALUE_CONVERTERS.convert(((String) token).trim());
if (converted == INVALID) {
return INVALID;
}
if (isHomogenousArray(converted, nestedList)) {
nestedList.add(converted);
} else {
return INVALID;
}
} else if (token instanceof List) {
@SuppressWarnings("unchecked")
List<Object> convertedList = (List<Object>) token;
if (isHomogenousArray(convertedList, nestedList)) {
nestedList.add(convertedList);
} else {
return INVALID;
}
}
}
return nestedList;
}
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() {}
}