mirror of
https://github.com/plexusorg/toml4j.git
synced 2024-12-28 19:24:15 +00:00
Added support for quoted keys with index arrays
This commit is contained in:
parent
62b2718a17
commit
df235e5a29
6 changed files with 132 additions and 68 deletions
|
@ -1,33 +1,102 @@
|
|||
package com.moandjiezana.toml;
|
||||
|
||||
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class Keys {
|
||||
|
||||
static String[] split(String key) {
|
||||
List<String> splitKey = new ArrayList<String>();
|
||||
static class Key {
|
||||
final String name;
|
||||
final int index;
|
||||
final String path;
|
||||
|
||||
Key(String name, int index, Key next) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
if (next != null) {
|
||||
this.path = name + "." + next.path;
|
||||
} else {
|
||||
this.path = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Keys.Key[] split(String key) {
|
||||
List<Key> splitKey = new ArrayList<Key>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
char[] chars = key.toCharArray();
|
||||
boolean quoted = false;
|
||||
boolean indexable = true;
|
||||
boolean inIndex = false;
|
||||
int index = -1;
|
||||
|
||||
for (char c : chars) {
|
||||
if (c == '"') {
|
||||
for (int i = chars.length - 1; i > -1; i--) {
|
||||
char c = chars[i];
|
||||
if (c == ']' && indexable) {
|
||||
inIndex = true;
|
||||
continue;
|
||||
}
|
||||
indexable = false;
|
||||
if (c == '[' && inIndex) {
|
||||
inIndex = false;
|
||||
index = Integer.parseInt(current.toString());
|
||||
current = new StringBuilder();
|
||||
continue;
|
||||
}
|
||||
if (c == '"' && (i == 0 || chars[i - 1] != '\\')) {
|
||||
quoted = !quoted;
|
||||
indexable = false;
|
||||
}
|
||||
if (c != '.' || quoted) {
|
||||
current.append(c);
|
||||
current.insert(0, c);
|
||||
} else {
|
||||
splitKey.add(current.toString());
|
||||
splitKey.add(0, new Key(current.toString(), index, !splitKey.isEmpty() ? splitKey.get(0) : null));
|
||||
indexable = true;
|
||||
index = -1;
|
||||
current = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
splitKey.add(current.toString());
|
||||
splitKey.add(0, new Key(current.toString(), index, !splitKey.isEmpty() ? splitKey.get(0) : null));
|
||||
|
||||
return splitKey.toArray(new String[0]);
|
||||
return splitKey.toArray(new Key[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line raw TOML iine to parse
|
||||
* @return null if line is not a valid table identifier
|
||||
*/
|
||||
static String getTableName(String line) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char[] chars = line.toCharArray();
|
||||
boolean quoted = false;
|
||||
boolean terminated = false;
|
||||
|
||||
for (int i = 1; i < chars.length; i++) {
|
||||
char c = chars[i];
|
||||
if (c == '"' && chars[i - 1] != '\\') {
|
||||
quoted = !quoted;
|
||||
} else if (!quoted && c == ']') {
|
||||
terminated = true;
|
||||
break;
|
||||
} else if (!quoted && c == '[') {
|
||||
break;
|
||||
}
|
||||
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
String tableName = sb.toString();
|
||||
|
||||
if (!terminated || !isComment(line.substring(tableName.length() + 2))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
tableName = StringConverter.STRING_PARSER.replaceUnicodeCharacters(tableName);
|
||||
return tableName;
|
||||
}
|
||||
|
||||
private Keys() {}
|
||||
|
||||
}
|
||||
|
|
|
@ -75,9 +75,9 @@ class Results {
|
|||
stack.pop();
|
||||
}
|
||||
|
||||
String[] tableParts = Keys.split(tableName);
|
||||
Keys.Key[] tableParts = Keys.split(tableName);
|
||||
for (int i = 0; i < tableParts.length; i++) {
|
||||
String tablePart = tableParts[i];
|
||||
String tablePart = tableParts[i].name;
|
||||
Container currentContainer = stack.peek();
|
||||
if (tablePart.isEmpty()) {
|
||||
errors.append("Empty implicit table: " + tableName + "!\n");
|
||||
|
|
|
@ -35,25 +35,16 @@ class StringConverter implements ValueConverter {
|
|||
|
||||
value = value.substring(1, stringTerminator);
|
||||
value = replaceUnicodeCharacters(value);
|
||||
|
||||
chars = value.toCharArray();
|
||||
for (int i = 0; i < chars.length - 1; i++) {
|
||||
char ch = chars[i];
|
||||
char next = chars[i + 1];
|
||||
|
||||
if (ch == '\\' && next == '\\') {
|
||||
i++;
|
||||
} else if (ch == '\\' && !(next == 'b' || next == 'f' || next == 'n' || next == 't' || next == 'r' || next == '"' || next == '/' || next == '\\')) {
|
||||
return INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
value = replaceSpecialCharacters(value);
|
||||
|
||||
if (value == null) {
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private String replaceUnicodeCharacters(String value) {
|
||||
String replaceUnicodeCharacters(String value) {
|
||||
Matcher unicodeMatcher = UNICODE_REGEX.matcher(value);
|
||||
|
||||
while (unicodeMatcher.find()) {
|
||||
|
@ -62,7 +53,19 @@ class StringConverter implements ValueConverter {
|
|||
return value;
|
||||
}
|
||||
|
||||
private String replaceSpecialCharacters(String value) {
|
||||
String replaceSpecialCharacters(String value) {
|
||||
char[] chars = value.toCharArray();
|
||||
for (int i = 0; i < chars.length - 1; i++) {
|
||||
char ch = chars[i];
|
||||
char next = chars[i + 1];
|
||||
|
||||
if (ch == '\\' && next == '\\') {
|
||||
i++;
|
||||
} else if (ch == '\\' && !(next == 'b' || next == 'f' || next == 'n' || next == 't' || next == 'r' || next == '"' || next == '/' || next == '\\')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return value.replace("\\n", "\n")
|
||||
.replace("\\\"", "\"")
|
||||
.replace("\\t", "\t")
|
||||
|
|
|
@ -12,14 +12,12 @@ import java.math.BigDecimal;
|
|||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
@ -250,52 +248,28 @@ public class Toml {
|
|||
return values.get(key);
|
||||
}
|
||||
|
||||
String[] split = Keys.split(key);
|
||||
Object current = new HashMap<String, Object>(values);
|
||||
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
|
||||
String keyWithDot = join(Arrays.copyOfRange(split, i, split.length));
|
||||
if (current instanceof Map && ((Map<String, Object>) current).containsKey(keyWithDot)) {
|
||||
return ((Map<String, Object>) current).get(keyWithDot);
|
||||
}
|
||||
|
||||
String splitKey = split[i];
|
||||
Matcher matcher = ARRAY_INDEX_PATTERN.matcher(splitKey);
|
||||
int index = -1;
|
||||
|
||||
if (matcher.find()) {
|
||||
splitKey = matcher.group(1);
|
||||
index = Integer.parseInt(matcher.group(2), 10);
|
||||
Keys.Key[] keys = Keys.split(key);
|
||||
|
||||
for (Keys.Key k : keys) {
|
||||
if (k.index == -1 && current instanceof Map && ((Map<String, Object>) current).containsKey(k.path)) {
|
||||
return ((Map<String, Object>) current).get(k.path);
|
||||
}
|
||||
|
||||
current = ((Map<String, Object>) current).get(splitKey);
|
||||
current = ((Map<String, Object>) current).get(k.name);
|
||||
|
||||
if (index > -1 && current != null) {
|
||||
current = ((List<?>) current).get(index);
|
||||
if (k.index > -1 && current != null) {
|
||||
current = ((List<?>) current).get(k.index);
|
||||
}
|
||||
|
||||
if (current == null) {
|
||||
return defaults != null ? defaults.get(key) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
private String join(String[] strings) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (String string : strings) {
|
||||
sb.append(string).append('.');
|
||||
}
|
||||
|
||||
if (sb.length() > 0) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Toml(Toml defaults, Map<String, Object> values) {
|
||||
this.values = values != null ? values : Collections.<String, Object>emptyMap();
|
||||
|
|
|
@ -186,12 +186,7 @@ class TomlParser {
|
|||
}
|
||||
|
||||
private String getTableName(String line) {
|
||||
List<Object> resultValue = parse(parser().Table(), line);
|
||||
if (resultValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (String) resultValue.get(0);
|
||||
return Keys.getTableName(line);
|
||||
}
|
||||
|
||||
private boolean isKeyValid(String key) {
|
||||
|
|
|
@ -51,13 +51,36 @@ public class QuotedKeysTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void should_convert() throws Exception {
|
||||
public void should_convert_quoted_keys_to_map_but_not_to_object_fields() throws Exception {
|
||||
Quoted quoted = new Toml().parse("\"ʎǝʞ\" = \"value\" \n[map] \n \"ʎǝʞ\" = \"value\"").to(Quoted.class);
|
||||
|
||||
assertNull(quoted.ʎǝʞ);
|
||||
assertEquals("value", quoted.map.get("\"ʎǝʞ\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_support_table_array_index_with_quoted_key() throws Exception {
|
||||
Toml toml = new Toml().parse("[[dog.\" type\"]] \n name = \"type0\" \n [[dog.\" type\"]] \n name = \"type1\"");
|
||||
|
||||
assertEquals("type0", toml.getString("dog.\" type\"[0].name"));
|
||||
assertEquals("type1", toml.getString("dog.\" type\"[1].name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_support_quoted_key_containing_square_brackets() throws Exception {
|
||||
Toml toml = new Toml().parse("[dog.\" type[abc]\"] \n name = \"type0\" \n [dog.\" type[1]\"] \n \"name[]\" = \"type1\"");
|
||||
|
||||
assertEquals("type0", toml.getString("dog.\" type[abc]\".name"));
|
||||
assertEquals("type1", toml.getString("dog.\" type[1]\".\"name[]\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_support_quoted_key_containing_escaped_quote() throws Exception {
|
||||
Toml toml = new Toml().parse("[dog.\"ty\\\"pe\"] \n \"na\\\"me\" = \"type0\"");
|
||||
|
||||
assertEquals("type0", toml.getString("dog.\"ty\\\"pe\".\"na\\\"me\""));
|
||||
}
|
||||
|
||||
private static class Quoted {
|
||||
|
||||
String ʎǝʞ;
|
||||
|
|
Loading…
Reference in a new issue