Improved handling of bare and quoted keys

This commit is contained in:
moandji.ezana 2015-01-23 15:17:50 +02:00
parent f804c99534
commit b7b546dc83
12 changed files with 182 additions and 56 deletions

View file

@ -25,6 +25,35 @@ class Keys {
} }
} }
static String getKey(String key) {
key = key.trim();
if (key.isEmpty()) {
return null;
}
boolean quoted = false;
char[] chars = key.toCharArray();
StringBuilder sb = new StringBuilder(key.length());
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
if (c == '"' && (i == 0 || chars[i - 1] != '\\')) {
if (!quoted && i > 0 && chars [i - 1] != '.') {
return null;
}
quoted = !quoted;
} else if (!quoted && (ALLOWED_CHARS.indexOf(c) == -1)) {
return null;
}
sb.append(c);
}
return sb.toString();
}
static Keys.Key[] split(String key) { static Keys.Key[] split(String key) {
List<Key> splitKey = new ArrayList<Key>(); List<Key> splitKey = new ArrayList<Key>();
StringBuilder current = new StringBuilder(); StringBuilder current = new StringBuilder();
@ -111,6 +140,9 @@ class Keys {
for (int i = 1; i < chars.length; i++) { for (int i = 1; i < chars.length; i++) {
char c = chars[i]; char c = chars[i];
if (c == '"' && chars[i - 1] != '\\') { if (c == '"' && chars[i - 1] != '\\') {
if (!quoted && i > 1 && chars [i - 1] != '.') {
break;
}
quoted = !quoted; quoted = !quoted;
} else if (!quoted && c == ']') { } else if (!quoted && c == ']') {
terminated = true; terminated = true;

View file

@ -36,7 +36,7 @@ class TomlParser {
} }
if (isTableArray(line)) { if (isTableArray(line)) {
String tableName = getTableArrayName(line); String tableName = Keys.getTableArrayName(line);
if (tableName != null) { if (tableName != null) {
results.startTableArray(tableName); results.startTableArray(tableName);
String afterTableName = line.substring(tableName.length() + 4); String afterTableName = line.substring(tableName.length() + 4);
@ -51,7 +51,7 @@ class TomlParser {
} }
if (multiline.isNotMultiline() && isTable(line)) { if (multiline.isNotMultiline() && isTable(line)) {
String tableName = getTableName(line); String tableName = Keys.getTableName(line);
if (tableName != null) { if (tableName != null) {
results.startTables(tableName); results.startTables(tableName);
} else { } else {
@ -140,14 +140,14 @@ class TomlParser {
continue; continue;
} }
} else { } else {
key = pair[0].trim(); key = Keys.getKey(pair[0]);
if (key == null) {
results.errors.append("Invalid key name: " + pair[0] + "\n");
continue;
}
value = pair[1].trim(); value = pair[1].trim();
} }
if (!isKeyValid(key)) {
results.errors.append("Invalid key name: " + key + "\n");
continue;
}
Object convertedValue = VALUE_ANALYSIS.convert(value); Object convertedValue = VALUE_ANALYSIS.convert(value);
@ -169,26 +169,10 @@ class TomlParser {
return line.startsWith("[["); return line.startsWith("[[");
} }
private String getTableArrayName(String line) {
return Keys.getTableArrayName(line);
}
private boolean isTable(String line) { private boolean isTable(String line) {
return line.startsWith("["); return line.startsWith("[");
} }
private String getTableName(String line) {
return Keys.getTableName(line);
}
private boolean isKeyValid(String key) {
if (key.contains("#") || key.trim().isEmpty()) {
return false;
}
return true;
}
private boolean isComment(String line) { private boolean isComment(String line) {
if (line == null || line.isEmpty()) { if (line == null || line.isEmpty()) {
return true; return true;

View file

@ -10,7 +10,7 @@ public class BareKeysTest {
public final ExpectedException exception = ExpectedException.none(); public final ExpectedException exception = ExpectedException.none();
@Test @Test
public void should_fail_when_characters_outside_accept_range_are_used() throws Exception { public void should_fail_when_characters_outside_accept_range_are_used_in_table_name() throws Exception {
exception.expect(IllegalStateException.class); exception.expect(IllegalStateException.class);
exception.expectMessage("Invalid table definition: [~]"); exception.expectMessage("Invalid table definition: [~]");
@ -18,16 +18,48 @@ public class BareKeysTest {
} }
@Test @Test
public void should_fail_on_sharp_sign_in_table_names() throws Exception { public void should_fail_when_characters_outside_accept_range_are_used_in_key_name() throws Exception {
exception.expect(IllegalStateException.class);
new Toml().parse("~ = 1");
}
@Test
public void should_fail_on_sharp_sign_in_table_name() throws Exception {
exception.expect(IllegalStateException.class); exception.expect(IllegalStateException.class);
new Toml().parse("[group#]\nkey=1"); new Toml().parse("[group#]\nkey=1");
} }
@Test @Test
public void should_fail_on_spaces_in_table_names() throws Exception { public void should_fail_on_spaces_in_table_name() throws Exception {
exception.expect(IllegalStateException.class); exception.expect(IllegalStateException.class);
new Toml().parse("[valid key]"); new Toml().parse("[valid key]");
} }
@Test(expected = IllegalStateException.class)
public void should_fail_on_question_marks_in_key_name() throws Exception {
new Toml().parse("key?=true");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_empty_table_name() {
new Toml().parse("[]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_nested_table_name_ending_with_empty_table_name() {
new Toml().parse("[a.]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_nested_table_name_containing_empty_table_name() {
new Toml().parse("[a..b]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_nested_table_name_starting_with_empty_table_name() {
new Toml().parse("[.b]");
}
} }

View file

@ -109,16 +109,26 @@ public class BurntSushiValidTest {
run("key-equals-nospace"); run("key-equals-nospace");
} }
@Test @Test @Ignore
public void key_space() throws Exception { public void key_space() throws Exception {
run("key-space"); run("key-space");
} }
@Test @Test
public void key_space_modified() throws Exception {
run("key-space-modified");
}
@Test @Ignore
public void key_special_chars() throws Exception { public void key_special_chars() throws Exception {
run("key-special-chars"); run("key-special-chars");
} }
@Test
public void key_special_chars_modified() throws Exception {
run("key-special-chars-modified");
}
@Test @Test
public void keys_with_dots() throws Exception { public void keys_with_dots() throws Exception {
run("keys-with-dots"); run("keys-with-dots");
@ -134,11 +144,16 @@ public class BurntSushiValidTest {
run("long-integer"); run("long-integer");
} }
@Test @Test @Ignore
public void multiline_string() throws Exception { public void multiline_string() throws Exception {
run("multiline-string"); run("multiline-string");
} }
@Test
public void multiline_string_modified() throws Exception {
run("multiline-string-modified");
}
@Test @Test
public void raw_multiline_string() throws Exception { public void raw_multiline_string() throws Exception {
run("raw-multiline-string"); run("raw-multiline-string");
@ -154,11 +169,16 @@ public class BurntSushiValidTest {
run("string-empty"); run("string-empty");
} }
@Test @Test @Ignore
public void string_escapes() throws Exception { public void string_escapes() throws Exception {
run("string-escapes-modified"); run("string-escapes-modified");
} }
@Test
public void string_escapes_modified() throws Exception {
run("string-escapes-modified");
}
@Test @Test
public void string_simple() throws Exception { public void string_simple() throws Exception {
run("string-simple"); run("string-simple");

View file

@ -81,6 +81,28 @@ public class QuotedKeysTest {
assertEquals("type0", toml.getString("dog.\"ty\\\"pe\".\"na\\\"me\"")); assertEquals("type0", toml.getString("dog.\"ty\\\"pe\".\"na\\\"me\""));
} }
@Test
public void should_support_fully_quoted_table_name() throws Exception {
Toml toml = new Toml().parse("[\"abc.def\"] \n key = 1");
assertEquals(1, toml.getLong("\"abc.def\".key").intValue());
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_malformed_quoted_key() throws Exception {
new Toml().parse("k\"ey\" = 1");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_malformed_quoted_table() throws Exception {
new Toml().parse("[a\"bc\"]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_malformed_quoted_nested_table() throws Exception {
new Toml().parse("[a.a\"bc\"]");
}
private static class Quoted { private static class Quoted {
String ʎǝʞ; String ʎǝʞ;

View file

@ -116,13 +116,6 @@ public class TomlTest {
assertEquals(1, toml.getLong("a_a").intValue()); assertEquals(1, toml.getLong("a_a").intValue());
} }
@Test
public void should_support_question_marks_in_key_names() throws Exception {
Toml toml = new Toml().parse("key?=true");
assertTrue(toml.getBoolean("key?"));
}
@Test @Test
public void should_support_dots_in_key_names() throws Exception { public void should_support_dots_in_key_names() throws Exception {
@ -207,26 +200,6 @@ public class TomlTest {
public void should_fail_when_illegal_characters_after_table() throws Exception { public void should_fail_when_illegal_characters_after_table() throws Exception {
new Toml().parse("[error] if you didn't catch this, your parser is broken"); new Toml().parse("[error] if you didn't catch this, your parser is broken");
} }
@Test(expected = IllegalStateException.class)
public void should_fail_on_empty_table_name() {
new Toml().parse("[]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_compound_table_name_ending_with_empty_table_name() {
new Toml().parse("[a.]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_compound_table_name_containing_empty_table_name() {
new Toml().parse("[a..b]");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_compound_table_name_starting_with_empty_table_name() {
new Toml().parse("[.b]");
}
private File file(String file) { private File file(String file) {
return new File(getClass().getResource(file + ".toml").getFile()); return new File(getClass().getResource(file + ".toml").getFile());

View file

@ -0,0 +1,3 @@
{
"ab": {"type": "integer", "value": "1"}
}

View file

@ -0,0 +1,5 @@
{
"_-1234567890": {
"type": "integer", "value": "1"
}
}

View file

@ -0,0 +1,30 @@
{
"multiline_empty_one": {
"type": "string",
"value": ""
},
"multiline_empty_two": {
"type": "string",
"value": ""
},
"multiline_empty_three": {
"type": "string",
"value": ""
},
"multiline_empty_four": {
"type": "string",
"value": ""
},
"equivalent_one": {
"type": "string",
"value": "The quick brown fox jumps over the lazy dog."
},
"equivalent_two": {
"type": "string",
"value": "The quick brown fox jumps over the lazy dog."
},
"equivalent_three": {
"type": "string",
"value": "The quick brown fox jumps over the lazy dog."
}
}

View file

@ -0,0 +1,23 @@
multiline_empty_one = """"""
multiline_empty_two = """
"""
multiline_empty_three = """\
"""
multiline_empty_four = """\
\
\
"""
equivalent_one = "The quick brown fox jumps over the lazy dog."
equivalent_two = """
The quick brown \
fox jumps over \
the lazy dog."""
equivalent_three = """\
The quick brown \
fox jumps over \
the lazy dog.\
"""