Streamlined array and inline table converters to re-use other converters

[ci skip]
This commit is contained in:
moandji.ezana 2015-02-11 00:59:48 +02:00
parent ddb061b9f9
commit d1d7145a03
11 changed files with 263 additions and 206 deletions

View file

@ -19,7 +19,7 @@ class ArrayConverter implements ValueConverter {
@Override
public Object convert(String s) {
AtomicInteger sharedIndex = new AtomicInteger(1);
AtomicInteger sharedIndex = new AtomicInteger();
Object converted = convert(s, sharedIndex);
char[] chars = s.toCharArray();
@ -39,119 +39,56 @@ class ArrayConverter implements ValueConverter {
return converted;
}
Object convert(String s, AtomicInteger sharedIndex) {
@Override
public Object convert(String s, AtomicInteger index) {
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()];
for (int i = index.incrementAndGet(); i < chars.length; i = index.incrementAndGet()) {
if (stringType == StringType.NONE) {
if (c == ',') {
if (current.toString().trim().length() > 0) {
arrayItems.add(current.toString());
}
current = new StringBuilder();
continue;
}
char c = chars[i];
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 (Character.isWhitespace(c)) {
continue;
}
if (c == ',') {
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);
}
if (c == '[') {
arrayItems.add(convert(s, index));
continue;
}
current.append(c);
if (c == ']') {
terminated = true;
break;
}
arrayItems.add(VALUE_CONVERTERS.convert(s, index));
}
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;
}
for (Object arrayItem : arrayItems) {
if (arrayItem == INVALID) {
return INVALID;
}
if (!isHomogenousArray(arrayItem, arrayItems)) {
return INVALID;
}
}
return nestedList;
return arrayItems;
}
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() {}
}

View file

@ -1,6 +1,9 @@
package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.util.concurrent.atomic.AtomicInteger;
class BooleanConverter implements ValueConverter {
@ -14,13 +17,24 @@ class BooleanConverter implements ValueConverter {
@Override
public Object convert(String s) {
AtomicInteger index = new AtomicInteger();
Object converted = convert(s, index);
if (!isComment(s.substring(index.incrementAndGet()))) {
return INVALID;
}
return converted;
}
@Override
public Object convert(String s, AtomicInteger index) {
s = s.substring(index.get());
Boolean b = s.startsWith("true") ? Boolean.TRUE : Boolean.FALSE;
int endIndex = b == Boolean.TRUE ? 4 : 5;
if (!ValueConverterUtils.isComment(s.substring(endIndex))) {
return INVALID;
}
index.addAndGet(endIndex - 1);
return b;
}

View file

@ -1,8 +1,10 @@
package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.text.SimpleDateFormat;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -15,13 +17,18 @@ class DateConverter implements ValueConverter {
public boolean canConvert(String s) {
Matcher matcher = DATE_REGEX.matcher(s);
return matcher.matches() && ValueConverterUtils.isComment(matcher.group(4));
return matcher.matches();
}
@Override
public Object convert(String s) {
Matcher matcher = DATE_REGEX.matcher(s);
matcher.matches();
if (!isComment(matcher.group(4))) {
return INVALID;
}
s = matcher.group(1);
String zone = matcher.group(3);
String fractionalSeconds = matcher.group(2);
@ -36,6 +43,7 @@ class DateConverter implements ValueConverter {
} else if (zone.contains(":")) {
s += zone.replace(":", "");
}
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);
@ -44,6 +52,37 @@ class DateConverter implements ValueConverter {
return INVALID;
}
}
@Override
public Object convert(String original, AtomicInteger index) {
String s = original.substring(index.get());
Matcher matcher = DATE_REGEX.matcher(s);
matcher.matches();
String dateString = matcher.group(1);
String zone = matcher.group(3);
String fractionalSeconds = matcher.group(2);
String format = "yyyy-MM-dd'T'HH:mm:ss";
if (fractionalSeconds != null && !fractionalSeconds.isEmpty()) {
format += ".SSS";
dateString += fractionalSeconds;
}
format += "Z";
if ("Z".equals(zone)) {
dateString += "+0000";
} else if (zone.contains(":")) {
dateString += zone.replace(":", "");
}
index.addAndGet(matcher.end(3) - 1);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);
return dateFormat.parse(dateString);
} catch (Exception e) {
return INVALID;
}
}
private DateConverter() {}
}

View file

@ -17,55 +17,45 @@ class InlineTableConverter implements ValueConverter {
@Override
public Object convert(String s) {
AtomicInteger sharedIndex = new AtomicInteger(1);
Object converted = convert(s, sharedIndex);
char[] chars = s.toCharArray();
AtomicInteger index = new AtomicInteger();
Object converted = convert(s, index);
for (; sharedIndex.get() < s.length(); sharedIndex.incrementAndGet()) {
char c = chars[sharedIndex.get()];
if (Character.isWhitespace(c)) {
continue;
}
if (c == '#') {
break;
}
String substring = s.substring(index.incrementAndGet());
if (converted == INVALID || !ValueConverterUtils.isComment(substring)) {
return INVALID;
}
return converted;
}
Object convert(String s, AtomicInteger sharedIndex) {
@Override
public Object convert(String s, AtomicInteger sharedIndex) {
char[] chars = s.toCharArray();
boolean inKey = true;
boolean inValue = false;
boolean quoted = false;
boolean terminated = false;
StringBuilder currentKey = new StringBuilder();
StringBuilder current = new StringBuilder();
HashMap<String, Object> results = new HashMap<String, Object>();
for (; sharedIndex.get() < chars.length; sharedIndex.incrementAndGet()) {
int i = sharedIndex.get();
for (int i = sharedIndex.incrementAndGet(); sharedIndex.get() < chars.length; i = sharedIndex.incrementAndGet()) {
char c = chars[i];
if (c == '"') {
if (c == '"' && inKey) {
quoted = !quoted;
(inValue ? current : currentKey).append(c);
currentKey.append(c);
} else if (quoted) {
(inKey ? currentKey : current).append(c);
} else if (c == '[' && inValue) {
sharedIndex.incrementAndGet();
Object converted = ArrayConverter.ARRAY_PARSER.convert(s, sharedIndex);
currentKey.append(c);
} else if (inValue && !Character.isWhitespace(c)) {
Object converted = CONVERTERS.convert(s, sharedIndex);
if (converted == INVALID) {
return INVALID;
}
results.put(currentKey.toString().trim(), converted);
i = sharedIndex.get();
continue;
currentKey = new StringBuilder();
inValue = false;
} else if (c == '{') {
sharedIndex.incrementAndGet();
Object converted = convert(s, sharedIndex);
@ -79,43 +69,18 @@ class InlineTableConverter implements ValueConverter {
inKey = true;
inValue = false;
currentKey = new StringBuilder();
current = new StringBuilder();
} else if (c == ',') {
if (!current.toString().trim().isEmpty()) {
Object converted = CONVERTERS.convert(current.toString().trim());
if (converted == INVALID) {
return INVALID;
}
results.put(currentKey.toString().trim(), converted);
}
inKey = true;
inValue = false;
currentKey = new StringBuilder();
current = new StringBuilder();
} else if (c == '=') {
inKey = false;
inValue = true;
} else if (c == '}') {
terminated = true;
String trimmed = current.toString().trim();
if (!trimmed.isEmpty()) {
Object converted = CONVERTERS.convert(trimmed);
if (converted == INVALID) {
return INVALID;
}
results.put(currentKey.toString().trim(), converted);
}
sharedIndex.incrementAndGet();
break;
} else {
(inKey ? currentKey : current).append(c);
} else if (inKey) {
currentKey.append(c);
}
}

View file

@ -1,6 +1,9 @@
package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.util.concurrent.atomic.AtomicInteger;
class LiteralStringConverter implements ValueConverter {
@ -14,31 +17,38 @@ class LiteralStringConverter implements ValueConverter {
@Override
public Object convert(String s) {
char[] chars = s.toCharArray();
int endIndex = -1;
AtomicInteger index = new AtomicInteger();
Object converted = convert(s, index);
for (int i = 1; i < chars.length; i++) {
char c = chars[i];
if (c == '\'') {
endIndex = i;
continue;
}
if (endIndex > -1 && c == '#') {
break;
}
if (endIndex > -1 && !Character.isWhitespace(c)) {
return INVALID;
}
}
if (endIndex == -1) {
if (converted == INVALID || !isComment(s.substring(index.incrementAndGet()))) {
return INVALID;
}
return s.substring(1, endIndex);
return converted;
}
@Override
public Object convert(String s, AtomicInteger index) {
char[] chars = s.toCharArray();
boolean terminated = false;
int startIndex = index.incrementAndGet();
for (int i = index.get(); i < chars.length; i = index.incrementAndGet()) {
char c = chars[i];
if (c == '\'') {
terminated = true;
break;
}
}
if (!terminated) {
return INVALID;
}
String substring = s.substring(startIndex, index.get());
return substring;
}
private LiteralStringConverter() {}

View file

@ -1,6 +1,9 @@
package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.util.concurrent.atomic.AtomicInteger;
class MultilineLiteralStringConverter implements ValueConverter {
@ -13,28 +16,33 @@ class MultilineLiteralStringConverter implements ValueConverter {
@Override
public Object convert(String s) {
AtomicInteger index = new AtomicInteger();
Object converted = convert(s, index);
if (converted == INVALID || !isComment(s.substring(index.incrementAndGet()))) {
return INVALID;
}
return converted;
}
@Override
public Object convert(String s, AtomicInteger index) {
char[] chars = s.toCharArray();
int startIndex = index.addAndGet(3);
int endIndex = -1;
for (int i = 3; i < chars.length; i++) {
for (int i = startIndex; i < chars.length; i = index.incrementAndGet()) {
char c = chars[i];
if (c == '\'' && chars.length > i + 2 && chars[i + 1] == '\'' && chars[i + 2] == '\'') {
endIndex = i;
i += 2;
continue;
}
if (endIndex > -1 && c == '#') {
index.addAndGet(2);
break;
}
if (endIndex > -1 && !Character.isWhitespace(c)) {
return INVALID;
}
}
return s.substring(3, endIndex);
return s.substring(startIndex, endIndex);
}
private MultilineLiteralStringConverter() {}

View file

@ -1,9 +1,12 @@
package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.util.concurrent.atomic.AtomicInteger;
class MultilineStringConverter implements ValueConverter {
static final MultilineStringConverter MULTILINE_STRING_PARSER = new MultilineStringConverter();
@Override
@ -13,22 +16,45 @@ class MultilineStringConverter implements ValueConverter {
@Override
public Object convert(String s) {
int terminator = s.indexOf("\"\"\"", 3);
if (terminator == -1) {
AtomicInteger index = new AtomicInteger();
Object converted = convert(s, index);
if (converted == INVALID || !isComment(s.substring(index.incrementAndGet()))) {
return INVALID;
}
if (!ValueConverterUtils.isComment(s.substring(terminator + 3))) {
return INVALID;
}
s = s.substring(2, terminator + 1);
s = s.replaceAll("\\\\\\s+", "");
return StringConverter.STRING_PARSER.convert(s);
return converted;
}
@Override
public Object convert(String s, AtomicInteger index) {
char[] chars = s.toCharArray();
int startIndex = index.addAndGet(3);
int endIndex = -1;
for (int i = startIndex; i < chars.length; i = index.incrementAndGet()) {
char c = chars[i];
if (c == '"' && chars.length > i + 2 && chars[i + 1] == '"' && chars[i + 2] == '"') {
endIndex = i;
index.addAndGet(2);
break;
}
}
if (endIndex == -1) {
return INVALID;
}
s = s.substring(startIndex, endIndex);
s = s.replaceAll("\\\\\\s+", "");
s = StringConverter.STRING_PARSER.replaceUnicodeCharacters(s);
s = StringConverter.STRING_PARSER.replaceSpecialCharacters(s);
return s;
}
private MultilineStringConverter() {
}
private MultilineStringConverter() {}
}

View file

@ -1,6 +1,9 @@
package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.util.concurrent.atomic.AtomicInteger;
class NumberConverter implements ValueConverter {
static final NumberConverter NUMBER_PARSER = new NumberConverter();
@ -14,20 +17,33 @@ class NumberConverter implements ValueConverter {
@Override
public Object convert(String s) {
AtomicInteger index = new AtomicInteger();
Object converted = convert(s, index);
if (converted == INVALID || (s.length() > index.get() + 1 && !isComment(s.substring(index.incrementAndGet())))) {
return INVALID;
}
return converted;
}
@Override
public Object convert(String s, AtomicInteger index) {
char[] chars = s.toCharArray();
boolean whitespace = false;
boolean signable = true;
boolean dottable = false;
boolean exponentable = false;
boolean terminatable = false;
String type = "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
for (int i = index.get(); i < chars.length; i = index.incrementAndGet()) {
char c = chars[i];
if (Character.isDigit(c)) {
sb.append(c);
signable = false;
terminatable = true;
if (type.isEmpty()) {
type = "integer";
dottable = true;
@ -35,26 +51,28 @@ class NumberConverter implements ValueConverter {
exponentable = !type.equals("exponent");
} else if ((c == '+' || c == '-') && signable && chars.length > i + 1) {
signable = false;
terminatable = false;
if (c == '-') {
sb.append('-');
}
} else if (c == '.' && dottable && chars.length > i + 1) {
sb.append('.');
type = "float";
terminatable = false;
dottable = false;
exponentable = false;
} else if ((c == 'E' || c == 'e') && exponentable && chars.length > i + 1) {
sb.append('E');
type = "exponent";
terminatable = false;
signable = true;
dottable = false;
exponentable = false;
} else if (Character.isWhitespace(c)) {
whitespace = true;
} else if (whitespace && c == '#') {
break;
} else {
type = "";
if (!terminatable) {
type = "";
}
index.decrementAndGet();
break;
}
}

View file

@ -3,6 +3,7 @@ package com.moandjiezana.toml;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -18,22 +19,35 @@ class StringConverter implements ValueConverter {
@Override
public Object convert(String value) {
int stringTerminator = -1;
AtomicInteger index = new AtomicInteger();
Object converted = convert(value, index);
if (converted == INVALID || !isComment(value.substring(index.incrementAndGet()))) {
return INVALID;
}
return converted;
}
@Override
public Object convert(String value, AtomicInteger sharedIndex) {
int startIndex = sharedIndex.incrementAndGet();
int endIndex = -1;
char[] chars = value.toCharArray();
for (int i = 1; i < chars.length; i++) {
for (int i = sharedIndex.get(); i < chars.length; i = sharedIndex.incrementAndGet()) {
char ch = chars[i];
if (ch == '"' && chars[i - 1] != '\\') {
stringTerminator = i;
endIndex = i;
break;
}
}
if (stringTerminator == -1 || !isComment(value.substring(stringTerminator + 1))) {
if (endIndex == -1) {
return INVALID;
}
value = value.substring(1, stringTerminator);
value = value.substring(startIndex, endIndex);
value = replaceUnicodeCharacters(value);
value = replaceSpecialCharacters(value);

View file

@ -1,5 +1,7 @@
package com.moandjiezana.toml;
import java.util.concurrent.atomic.AtomicInteger;
interface ValueConverter {
/**
@ -9,6 +11,16 @@ interface ValueConverter {
/**
* @param s must already have been validated by {@link #canConvert(String)}
* @return a value or {@link ValueConverterUtils#INVALID}
*/
Object convert(String s);
/**
* Partial validation. Stops after type terminator, rather than at EOI.
*
* @param s must already have been validated by {@link #canConvert(String)}
* @param index where to start in s
* @return a value or {@link ValueConverterUtils#INVALID}
*/
Object convert(String s, AtomicInteger index);
}

View file

@ -11,13 +11,15 @@ import static com.moandjiezana.toml.NumberConverter.NUMBER_PARSER;
import static com.moandjiezana.toml.StringConverter.STRING_PARSER;
import static com.moandjiezana.toml.ValueConverterUtils.INVALID;
import java.util.concurrent.atomic.AtomicInteger;
class ValueConverters {
private static final ValueConverter[] PARSERS = {
MULTILINE_STRING_PARSER, MULTILINE_LITERAL_STRING_CONVERTER, LITERAL_STRING_PARSER, STRING_PARSER, DATE_PARSER, NUMBER_PARSER, BOOLEAN_PARSER, ARRAY_PARSER, INLINE_TABLE_PARSER
};
public Object convert(String value) {
Object convert(String value) {
for (ValueConverter valueParser : PARSERS) {
if (valueParser.canConvert(value)) {
return valueParser.convert(value);
@ -26,4 +28,16 @@ class ValueConverters {
return INVALID;
}
Object convert(String value, AtomicInteger index) {
String substring = value.substring(index.get());
for (ValueConverter valueParser : PARSERS) {
if (valueParser.canConvert(substring)) {
return valueParser.convert(value, index);
}
}
return INVALID;
}
}