mirror of
https://github.com/plexusorg/toml4j.git
synced 2024-06-02 18:01:30 +00:00
Added support for quoted keys with index arrays
This commit is contained in:
parent
62b2718a17
commit
e5966d6bed
|
@ -1,33 +1,102 @@
|
||||||
package com.moandjiezana.toml;
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import static com.moandjiezana.toml.ValueConverterUtils.isComment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class Keys {
|
class Keys {
|
||||||
|
|
||||||
static String[] split(String key) {
|
static class Key {
|
||||||
List<String> splitKey = new ArrayList<String>();
|
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();
|
StringBuilder current = new StringBuilder();
|
||||||
char[] chars = key.toCharArray();
|
char[] chars = key.toCharArray();
|
||||||
boolean quoted = false;
|
boolean quoted = false;
|
||||||
|
boolean indexable = true;
|
||||||
|
boolean inIndex = false;
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
for (char c : chars) {
|
for (int i = chars.length - 1; i > -1; i--) {
|
||||||
if (c == '"') {
|
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;
|
quoted = !quoted;
|
||||||
|
indexable = false;
|
||||||
}
|
}
|
||||||
if (c != '.' || quoted) {
|
if (c != '.' || quoted) {
|
||||||
current.append(c);
|
current.insert(0, c);
|
||||||
} else {
|
} 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();
|
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() {}
|
private Keys() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,9 +75,9 @@ class Results {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] tableParts = Keys.split(tableName);
|
Keys.Key[] tableParts = Keys.split(tableName);
|
||||||
for (int i = 0; i < tableParts.length; i++) {
|
for (int i = 0; i < tableParts.length; i++) {
|
||||||
String tablePart = tableParts[i];
|
String tablePart = tableParts[i].name;
|
||||||
Container currentContainer = stack.peek();
|
Container currentContainer = stack.peek();
|
||||||
if (tablePart.isEmpty()) {
|
if (tablePart.isEmpty()) {
|
||||||
errors.append("Empty implicit table: " + tableName + "!\n");
|
errors.append("Empty implicit table: " + tableName + "!\n");
|
||||||
|
|
|
@ -35,25 +35,16 @@ class StringConverter implements ValueConverter {
|
||||||
|
|
||||||
value = value.substring(1, stringTerminator);
|
value = value.substring(1, stringTerminator);
|
||||||
value = replaceUnicodeCharacters(value);
|
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);
|
value = replaceSpecialCharacters(value);
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
return INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String replaceUnicodeCharacters(String value) {
|
String replaceUnicodeCharacters(String value) {
|
||||||
Matcher unicodeMatcher = UNICODE_REGEX.matcher(value);
|
Matcher unicodeMatcher = UNICODE_REGEX.matcher(value);
|
||||||
|
|
||||||
while (unicodeMatcher.find()) {
|
while (unicodeMatcher.find()) {
|
||||||
|
@ -62,7 +53,19 @@ class StringConverter implements ValueConverter {
|
||||||
return value;
|
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")
|
return value.replace("\\n", "\n")
|
||||||
.replace("\\\"", "\"")
|
.replace("\\\"", "\"")
|
||||||
.replace("\\t", "\t")
|
.replace("\\t", "\t")
|
||||||
|
|
|
@ -12,14 +12,12 @@ import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
@ -250,52 +248,28 @@ public class Toml {
|
||||||
return values.get(key);
|
return values.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] split = Keys.split(key);
|
|
||||||
Object current = new HashMap<String, Object>(values);
|
Object current = new HashMap<String, Object>(values);
|
||||||
|
|
||||||
for (int i = 0; i < split.length; i++) {
|
Keys.Key[] keys = Keys.split(key);
|
||||||
|
|
||||||
String keyWithDot = join(Arrays.copyOfRange(split, i, split.length));
|
for (Keys.Key k : keys) {
|
||||||
if (current instanceof Map && ((Map<String, Object>) current).containsKey(keyWithDot)) {
|
if (k.index == -1 && current instanceof Map && ((Map<String, Object>) current).containsKey(k.path)) {
|
||||||
return ((Map<String, Object>) current).get(keyWithDot);
|
return ((Map<String, Object>) current).get(k.path);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
current = ((Map<String, Object>) current).get(splitKey);
|
current = ((Map<String, Object>) current).get(k.name);
|
||||||
|
|
||||||
if (index > -1 && current != null) {
|
if (k.index > -1 && current != null) {
|
||||||
current = ((List<?>) current).get(index);
|
current = ((List<?>) current).get(k.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
return defaults != null ? defaults.get(key) : null;
|
return defaults != null ? defaults.get(key) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current;
|
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) {
|
private Toml(Toml defaults, Map<String, Object> values) {
|
||||||
this.values = values != null ? values : Collections.<String, Object>emptyMap();
|
this.values = values != null ? values : Collections.<String, Object>emptyMap();
|
||||||
|
|
|
@ -186,12 +186,13 @@ class TomlParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTableName(String line) {
|
private String getTableName(String line) {
|
||||||
List<Object> resultValue = parse(parser().Table(), line);
|
return Keys.getTableName(line);
|
||||||
if (resultValue == null) {
|
// List<Object> resultValue = parse(parser().Table(), line);
|
||||||
return null;
|
// if (resultValue == null) {
|
||||||
}
|
// return null;
|
||||||
|
// }
|
||||||
return (String) resultValue.get(0);
|
//
|
||||||
|
// return (String) resultValue.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isKeyValid(String key) {
|
private boolean isKeyValid(String key) {
|
||||||
|
|
|
@ -51,13 +51,36 @@ public class QuotedKeysTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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);
|
Quoted quoted = new Toml().parse("\"ʎǝʞ\" = \"value\" \n[map] \n \"ʎǝʞ\" = \"value\"").to(Quoted.class);
|
||||||
|
|
||||||
assertNull(quoted.ʎǝʞ);
|
assertNull(quoted.ʎǝʞ);
|
||||||
assertEquals("value", quoted.map.get("\"ʎǝʞ\""));
|
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 {
|
private static class Quoted {
|
||||||
|
|
||||||
String ʎǝʞ;
|
String ʎǝʞ;
|
||||||
|
|
Loading…
Reference in a new issue