mirror of
https://github.com/plexusorg/toml4j.git
synced 2024-12-28 19:24:15 +00:00
Improved error handling code and line numbers included in error messages
This commit is contained in:
parent
55d598c3b1
commit
d32ead52d4
6 changed files with 190 additions and 31 deletions
|
@ -8,6 +8,10 @@
|
|||
* Dropped dependency on Parboiled and its significant transitive dependencies
|
||||
* Updated Gson to 2.3.1
|
||||
|
||||
### Added
|
||||
|
||||
* Line numbers included in error messages
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed short-form Unicode escapes
|
||||
|
|
|
@ -7,8 +7,100 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
class Results {
|
||||
|
||||
static class Errors {
|
||||
|
||||
private final StringBuilder sb = new StringBuilder();
|
||||
|
||||
void duplicateTable(String table, int line) {
|
||||
sb.append("Duplicate table definition: [")
|
||||
.append(table)
|
||||
.append("]\n");
|
||||
}
|
||||
|
||||
void emptyImplicitTable(String table, int line) {
|
||||
sb.append("Invalid table definition due to empty implicit table name: ");
|
||||
if (!table.startsWith("[")) {
|
||||
sb.append('[');
|
||||
}
|
||||
sb.append(table);
|
||||
if (!table.endsWith("]")) {
|
||||
sb.append(']');
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
void invalidTable(String table, int line) {
|
||||
sb.append("Invalid table definition on line ")
|
||||
.append(line)
|
||||
.append(": ");
|
||||
if (!table.startsWith("[")) {
|
||||
sb.append('[');
|
||||
}
|
||||
sb.append(table);
|
||||
if (!table.endsWith("]")) {
|
||||
sb.append(']');
|
||||
}
|
||||
sb.append("]\n");
|
||||
}
|
||||
|
||||
void duplicateKey(String key, int line) {
|
||||
sb.append("Duplicate key: ")
|
||||
.append(key)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
void invalidKey(String key, int line) {
|
||||
sb.append("Invalid key");
|
||||
if (line > -1) {
|
||||
sb.append(" on line ")
|
||||
.append(line);
|
||||
}
|
||||
sb.append(": ")
|
||||
.append(key)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
void invalidTableArray(String tableArray, int line) {
|
||||
sb.append("Invalid table array definition on line ")
|
||||
.append(line)
|
||||
.append(": ")
|
||||
.append(tableArray)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
void invalidValue(String key, String value, int line) {
|
||||
sb.append("Invalid value on line ")
|
||||
.append(line)
|
||||
.append(": ")
|
||||
.append(key)
|
||||
.append(" = ")
|
||||
.append(value)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
void unterminated(String key, String multiline, int line) {
|
||||
sb.append("Unterminated multiline value on line ")
|
||||
.append(line)
|
||||
.append(": ")
|
||||
.append(key)
|
||||
.append(" = ")
|
||||
.append(multiline.trim())
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
boolean hasErrors() {
|
||||
return sb.length() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> tables = new HashSet<String>();
|
||||
StringBuilder errors = new StringBuilder();
|
||||
final Errors errors = new Errors();
|
||||
private Deque<Container> stack = new ArrayDeque<Container>();
|
||||
|
||||
Results() {
|
||||
|
@ -20,7 +112,7 @@ class Results {
|
|||
if (currentTable.accepts(key)) {
|
||||
currentTable.put(key, value);
|
||||
} else {
|
||||
errors.append("Key " + key + " is defined twice!\n");
|
||||
errors.duplicateKey(key, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +148,7 @@ class Results {
|
|||
stack.push(((Container.TableArray) newContainer).getCurrent());
|
||||
}
|
||||
} else {
|
||||
errors.append("Duplicate key and table definitions for " + tableName + "!\n");
|
||||
errors.duplicateTable(tableName, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -64,11 +156,11 @@ class Results {
|
|||
|
||||
void startTables(String tableName) {
|
||||
if (!tables.add(tableName)) {
|
||||
errors.append("Table " + tableName + " defined twice!\n");
|
||||
errors.duplicateTable(tableName, -1);
|
||||
}
|
||||
|
||||
if (tableName.endsWith(".")) {
|
||||
errors.append("Implicit table name cannot be empty: " + tableName);
|
||||
errors.emptyImplicitTable(tableName, -1);
|
||||
}
|
||||
|
||||
while (stack.size() > 1) {
|
||||
|
@ -80,7 +172,7 @@ class Results {
|
|||
String tablePart = tableParts[i].name;
|
||||
Container currentContainer = stack.peek();
|
||||
if (tablePart.isEmpty()) {
|
||||
errors.append("Empty implicit table: " + tableName + "!\n");
|
||||
errors.emptyImplicitTable(tableName, -1);
|
||||
} else if (currentContainer.get(tablePart) instanceof Container) {
|
||||
Container nextTable = (Container) currentContainer.get(tablePart);
|
||||
stack.push(nextTable);
|
||||
|
@ -90,7 +182,7 @@ class Results {
|
|||
} else if (currentContainer.accepts(tablePart)) {
|
||||
startTable(tablePart);
|
||||
} else {
|
||||
errors.append("Duplicate key and table definitions for " + tableName + "!\n");
|
||||
errors.duplicateTable(tableName, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ public class Toml {
|
|||
*/
|
||||
public Toml parse(String tomlString) throws IllegalStateException {
|
||||
Results results = new TomlParser().run(tomlString);
|
||||
if (results.errors.length() > 0) {
|
||||
if (results.errors.hasErrors()) {
|
||||
throw new IllegalStateException(results.errors.toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ class TomlParser {
|
|||
}
|
||||
|
||||
String[] lines = tomlString.split("[\\n\\r]");
|
||||
int lastKeyLine = 1;
|
||||
StringBuilder multilineBuilder = new StringBuilder();
|
||||
Multiline multiline = Multiline.NONE;
|
||||
|
||||
|
@ -41,7 +42,7 @@ class TomlParser {
|
|||
if (tableName != null) {
|
||||
results.startTableArray(tableName);
|
||||
} else {
|
||||
results.errors.append("Invalid table array definition: " + line + "\n\n");
|
||||
results.errors.invalidTableArray(line, i + 1);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -52,14 +53,14 @@ class TomlParser {
|
|||
if (tableName != null) {
|
||||
results.startTables(tableName);
|
||||
} else {
|
||||
results.errors.append("Invalid table definition: " + line + "\n\n");
|
||||
results.errors.invalidTable(line.trim(), i + 1);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multiline.isNotMultiline() && !line.contains("=")) {
|
||||
results.errors.append("Invalid key definition: " + line);
|
||||
results.errors.invalidKey(line, i + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -139,24 +140,24 @@ class TomlParser {
|
|||
} else {
|
||||
key = Keys.getKey(pair[0]);
|
||||
if (key == null) {
|
||||
results.errors.append("Invalid key name: " + pair[0] + "\n");
|
||||
results.errors.invalidKey(pair[0], i + 1);
|
||||
continue;
|
||||
}
|
||||
value = pair[1].trim();
|
||||
}
|
||||
|
||||
|
||||
lastKeyLine = i + 1;
|
||||
Object convertedValue = VALUE_ANALYSIS.convert(value);
|
||||
|
||||
if (convertedValue != INVALID) {
|
||||
results.addValue(key, convertedValue);
|
||||
} else {
|
||||
results.errors.append("Invalid key/value: " + key + " = " + value + "\n");
|
||||
results.errors.invalidValue(key, value, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (multiline != Multiline.NONE) {
|
||||
results.errors.append("Unterminated multiline " + multiline.toString().toLowerCase().replace('_', ' ') + "\n");
|
||||
results.errors.unterminated(key, multilineBuilder.toString().trim(), lastKeyLine);
|
||||
}
|
||||
|
||||
return results;
|
||||
|
|
|
@ -8,9 +8,6 @@ import org.junit.rules.ExpectedException;
|
|||
|
||||
public class BareKeysTest {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void should_ignore_spaces_around_key_segments() throws Exception {
|
||||
Toml toml = new Toml().parse("[ a . b . c ] \n key = \"a\"");
|
||||
|
@ -18,32 +15,23 @@ public class BareKeysTest {
|
|||
assertEquals("a", toml.getString("a.b.c.key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void should_fail_when_characters_outside_accept_range_are_used_in_table_name() throws Exception {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectMessage("Invalid table definition: [~]");
|
||||
|
||||
new Toml().parse("[~]");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IllegalStateException.class)
|
||||
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
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void should_fail_on_sharp_sign_in_table_name() throws Exception {
|
||||
exception.expect(IllegalStateException.class);
|
||||
|
||||
new Toml().parse("[group#]\nkey=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void should_fail_on_spaces_in_table_name() throws Exception {
|
||||
exception.expect(IllegalStateException.class);
|
||||
|
||||
new Toml().parse("[valid key]");
|
||||
}
|
||||
|
||||
|
|
74
src/test/java/com/moandjiezana/toml/ErrorMessagesTest.java
Normal file
74
src/test/java/com/moandjiezana/toml/ErrorMessagesTest.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
package com.moandjiezana.toml;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
public class ErrorMessagesTest {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException e = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void should_message_invalid_table() throws Exception {
|
||||
e.expectMessage("Invalid table definition on line 1: [in valid]");
|
||||
|
||||
new Toml().parse("[in valid]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_duplicate_table() throws Exception {
|
||||
e.expectMessage("Duplicate table definition: [again]");
|
||||
|
||||
new Toml().parse("[again]\n[again]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_empty_implicit_table_name() throws Exception {
|
||||
e.expectMessage("Invalid table definition due to empty implicit table name: [a..b]");
|
||||
|
||||
new Toml().parse("[a..b]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_duplicate_key() throws Exception {
|
||||
e.expectMessage("Duplicate key: k");
|
||||
|
||||
new Toml().parse("k = 1\n k = 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_invalid_key() throws Exception {
|
||||
e.expectMessage("Invalid key on line 1: k\"");
|
||||
|
||||
new Toml().parse("k\" = 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_invalid_table_array() throws Exception {
|
||||
e.expectMessage("Invalid table array definition on line 1: [[in valid]]");
|
||||
|
||||
new Toml().parse("[[in valid]]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_invalid_value() throws Exception {
|
||||
e.expectMessage("Invalid value on line 1: k = 1 t");
|
||||
|
||||
new Toml().parse("k = 1 t");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_unterminated_value() throws Exception {
|
||||
e.expectMessage("Unterminated multiline value on line 1: k = '''abc");
|
||||
|
||||
new Toml().parse("k = '''abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_message_key_without_equals() throws Exception {
|
||||
e.expectMessage("Invalid key on line 2: k");
|
||||
|
||||
new Toml().parse("\nk\n=3");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue