Skip to content

Commit 6345301

Browse files
Update dependabot configuration to change update schedule from weekly to monthly; refactor ValueDecoder for improved parsing logic and error handling, including method simplifications and enhanced comments.
1 parent a5ab04d commit 6345301

File tree

3 files changed

+51
-124
lines changed

3 files changed

+51
-124
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ updates:
33
- package-ecosystem: "gradle"
44
directory: "/"
55
schedule:
6-
interval: "weekly"
6+
interval: "monthly"
77
open-pull-requests-limit: 10
88
groups:
99
test:
@@ -13,4 +13,4 @@ updates:
1313
- package-ecosystem: "github-actions"
1414
directory: "/"
1515
schedule:
16-
interval: "weekly"
16+
interval: "monthly"

src/main/java/com/felipestanzani/jtoon/decoder/ValueDecoder.java

Lines changed: 48 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ private static class Parser {
138138

139139
/**
140140
* Parses the current line at root level (depth 0).
141-
* Routes to appropriate handler based on line content.
141+
* Routes to appropriate handler based online content.
142142
*/
143143
Object parseValue() {
144144
if (currentLine >= lines.length) {
@@ -358,53 +358,8 @@ private List<Object> parseArrayWithDelimiter(String header, int depth, String ar
358358
*/
359359
private List<Object> parseArray(String header, int depth) {
360360
String arrayDelimiter = extractDelimiterFromHeader(header);
361-
Matcher tabularMatcher = TABULAR_HEADER_PATTERN.matcher(header);
362-
Matcher arrayMatcher = ARRAY_HEADER_PATTERN.matcher(header);
363-
364-
if (tabularMatcher.find()) {
365-
return parseTabularArray(header, depth, arrayDelimiter);
366-
}
367-
368-
if (arrayMatcher.find()) {
369-
int headerEndIdx = arrayMatcher.end();
370-
String afterHeader = header.substring(headerEndIdx).trim();
371-
372-
if (afterHeader.startsWith(":")) {
373-
String inlineContent = afterHeader.substring(1).trim();
374-
375-
if (!inlineContent.isEmpty()) {
376-
List<Object> result = parseArrayValues(inlineContent, arrayDelimiter);
377-
validateArrayLength(header, result.size());
378-
currentLine++;
379-
return result;
380-
}
381-
}
382361

383-
currentLine++;
384-
if (currentLine < lines.length) {
385-
String nextLine = lines[currentLine];
386-
int nextDepth = getDepth(nextLine);
387-
String nextContent = nextLine.substring(nextDepth * options.indent());
388-
389-
if (nextContent.startsWith("- ")) {
390-
currentLine--;
391-
return parseListArray(depth, header);
392-
} else {
393-
currentLine++;
394-
List<Object> result = parseArrayValues(nextContent, arrayDelimiter);
395-
validateArrayLength(header, result.size());
396-
return result;
397-
}
398-
}
399-
List<Object> empty = new ArrayList<>();
400-
validateArrayLength(header, 0);
401-
return empty;
402-
}
403-
404-
if (options.strict()) {
405-
throw new IllegalArgumentException("Invalid array header: " + header);
406-
}
407-
return Collections.emptyList();
362+
return parseArrayWithDelimiter(header, depth, arrayDelimiter);
408363
}
409364

410365
/**
@@ -491,7 +446,7 @@ private int findNextNonBlankLine(int startIndex) {
491446
}
492447

493448
/**
494-
* Determines if tabular array parsing should terminate based on line depth.
449+
* Determines if tabular array parsing should terminate based online depth.
495450
* Returns true if array should terminate, false otherwise.
496451
*/
497452
private boolean shouldTerminateTabularArray(String line, int lineDepth, int depth) {
@@ -594,7 +549,7 @@ private boolean handleBlankLineInListArray(int depth) {
594549
}
595550

596551
/**
597-
* Determines if list array parsing should terminate based on line depth.
552+
* Determines if list array parsing should terminate based online depth.
598553
* Returns true if array should terminate, false otherwise.
599554
*/
600555
private boolean shouldTerminateListArray(int lineDepth, int depth, String line) {
@@ -883,11 +838,8 @@ private void parseListItemFields(Map<String, Object> item, int depth) {
883838
}
884839

885840
// If neither pattern matched, skip this line to avoid infinite loop
886-
currentLine++;
887-
} else {
888-
// Depth is greater than depth + 2, skip this line
889-
currentLine++;
890841
}
842+
currentLine++;
891843
}
892844
}
893845

@@ -1021,11 +973,10 @@ private List<String> parseDelimitedValues(String input, String arrayDelimiter) {
1021973
String value = current.toString().trim();
1022974
result.add(value);
1023975
current = new StringBuilder();
1024-
i++; // Move past the delimiter
1025976
// Skip whitespace after delimiter
1026-
while (i < input.length() && Character.isWhitespace(input.charAt(i))) {
977+
do {
1027978
i++;
1028-
}
979+
} while (i < input.length() && Character.isWhitespace(input.charAt(i)));
1029980
} else {
1030981
current.append(c);
1031982
i++;
@@ -1212,7 +1163,7 @@ private boolean shouldExpandKey(String key) {
12121163
// Each segment must match this pattern
12131164
String[] segments = key.split("\\.");
12141165
for (String segment : segments) {
1215-
if (!segment.matches("^[a-zA-Z_][\\w]*$")) {
1166+
if (!segment.matches("^[a-zA-Z_]\\w*$")) {
12161167
return false;
12171168
}
12181169
}
@@ -1283,47 +1234,71 @@ private void checkFinalValueConflict(String finalSegment, Object existing, Objec
12831234
}
12841235

12851236
/**
1286-
* Parses a key-value pair at root level, creating a new Map.
1237+
* Parses a key-value string into an Object, handling nested objects, empty
1238+
* values, and primitives.
1239+
*
1240+
* @param value the value string to parse
1241+
* @param depth the depth at which the key-value pair is located
1242+
* @return the parsed value (Map, List, or primitive)
12871243
*/
1288-
private Object parseKeyValuePair(String key, String value, int depth, boolean parseRootFields) {
1289-
String originalKey = key;
1290-
key = StringEscaper.unescape(key);
1291-
1244+
private Object parseKeyValue(String value, int depth) {
12921245
// Check if next line is nested (deeper indentation)
1293-
Object parsedValue;
12941246
if (currentLine + 1 < lines.length) {
12951247
int nextDepth = getDepth(lines[currentLine + 1]);
12961248
if (nextDepth > depth) {
12971249
currentLine++;
1298-
parsedValue = parseNestedObject(depth);
1250+
// parseNestedObject manages currentLine, so we don't increment here
1251+
return parseNestedObject(depth);
12991252
} else {
13001253
// If value is empty, create empty object; otherwise parse as primitive
1254+
Object parsedValue;
13011255
if (value.trim().isEmpty()) {
13021256
parsedValue = new LinkedHashMap<>();
13031257
} else {
13041258
parsedValue = PrimitiveDecoder.parse(value);
13051259
}
13061260
currentLine++;
1261+
return parsedValue;
13071262
}
13081263
} else {
13091264
// If value is empty, create empty object; otherwise parse as primitive
1265+
Object parsedValue;
13101266
if (value.trim().isEmpty()) {
13111267
parsedValue = new LinkedHashMap<>();
13121268
} else {
13131269
parsedValue = PrimitiveDecoder.parse(value);
13141270
}
13151271
currentLine++;
1272+
return parsedValue;
13161273
}
1274+
}
13171275

1318-
Map<String, Object> obj = new LinkedHashMap<>();
1319-
1276+
/**
1277+
* Puts a key-value pair into a map, handling path expansion.
1278+
*
1279+
* @param map the map to put the key-value pair into
1280+
* @param originalKey the original key before being unescaped (used for path
1281+
* expansion check)
1282+
* @param unescapedKey the unescaped key
1283+
* @param value the value to put
1284+
*/
1285+
private void putKeyValueIntoMap(Map<String, Object> map, String originalKey, String unescapedKey,
1286+
Object value) {
13201287
// Handle path expansion
13211288
if (shouldExpandKey(originalKey)) {
1322-
expandPathIntoMap(obj, key, parsedValue);
1289+
expandPathIntoMap(map, unescapedKey, value);
13231290
} else {
1324-
checkPathExpansionConflict(obj, key, parsedValue);
1325-
obj.put(key, parsedValue);
1291+
checkPathExpansionConflict(map, unescapedKey, value);
1292+
map.put(unescapedKey, value);
13261293
}
1294+
}
1295+
1296+
/**
1297+
* Parses a key-value pair at root level, creating a new Map.
1298+
*/
1299+
private Object parseKeyValuePair(String key, String value, int depth, boolean parseRootFields) {
1300+
Map<String, Object> obj = new LinkedHashMap<>();
1301+
parseKeyValuePairIntoMap(obj, key, value, depth);
13271302

13281303
if (parseRootFields) {
13291304
parseRootObjectFields(obj, depth);
@@ -1335,45 +1310,10 @@ private Object parseKeyValuePair(String key, String value, int depth, boolean pa
13351310
* Parses a key-value pair and adds it to an existing map.
13361311
*/
13371312
private void parseKeyValuePairIntoMap(Map<String, Object> map, String key, String value, int depth) {
1338-
String originalKey = key;
1339-
key = StringEscaper.unescape(key);
1340-
1341-
// Check if next line is nested
1342-
Object parsedValue;
1343-
if (currentLine + 1 < lines.length) {
1344-
int nextDepth = getDepth(lines[currentLine + 1]);
1345-
if (nextDepth > depth) {
1346-
currentLine++;
1347-
parsedValue = parseNestedObject(depth);
1348-
// parseNestedObject manages currentLine, so we don't increment here
1349-
} else {
1350-
// If value is empty, create empty object; otherwise parse as primitive
1351-
if (value.trim().isEmpty()) {
1352-
parsedValue = new LinkedHashMap<>();
1353-
} else {
1354-
parsedValue = PrimitiveDecoder.parse(value);
1355-
}
1356-
// Increment currentLine for non-nested values
1357-
currentLine++;
1358-
}
1359-
} else {
1360-
// If value is empty, create empty object; otherwise parse as primitive
1361-
if (value.trim().isEmpty()) {
1362-
parsedValue = new LinkedHashMap<>();
1363-
} else {
1364-
parsedValue = PrimitiveDecoder.parse(value);
1365-
}
1366-
// Increment currentLine for non-nested values
1367-
currentLine++;
1368-
}
1313+
String unescapedKey = StringEscaper.unescape(key);
13691314

1370-
// Handle path expansion
1371-
if (shouldExpandKey(originalKey)) {
1372-
expandPathIntoMap(map, key, parsedValue);
1373-
} else {
1374-
checkPathExpansionConflict(map, key, parsedValue);
1375-
map.put(key, parsedValue);
1376-
}
1315+
Object parsedValue = parseKeyValue(value, depth);
1316+
putKeyValueIntoMap(map, key, unescapedKey, parsedValue);
13771317
}
13781318

13791319
/**
@@ -1386,19 +1326,7 @@ private void checkPathExpansionConflict(Map<String, Object> map, String key, Obj
13861326
}
13871327

13881328
Object existing = map.get(key);
1389-
if (existing != null) {
1390-
// Check for conflicts: existing is object/array but value is not
1391-
if (existing instanceof Map && !(value instanceof Map)) {
1392-
throw new IllegalArgumentException(
1393-
String.format("Path expansion conflict: %s is object, cannot set to %s",
1394-
key, value.getClass().getSimpleName()));
1395-
}
1396-
if (existing instanceof List && !(value instanceof List)) {
1397-
throw new IllegalArgumentException(
1398-
String.format("Path expansion conflict: %s is array, cannot set to %s",
1399-
key, value.getClass().getSimpleName()));
1400-
}
1401-
}
1329+
checkFinalValueConflict(key, existing, value);
14021330
}
14031331

14041332
/**

src/test/java/com/felipestanzani/jtoon/conformance/ConformanceTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,10 @@ private DecodeOptions parseOptions(JsonDecodeTestOptions options) {
197197

198198
boolean strict = options.strict() != null ? options.strict() : true;
199199

200-
PathExpansion expandPaths = PathExpansion.OFF;
200+
PathExpansion expandPaths = null;
201201
if (options.expandPaths() != null) {
202202
expandPaths = switch (options.expandPaths().toLowerCase()) {
203203
case "safe" -> PathExpansion.SAFE;
204-
case "off" -> PathExpansion.OFF;
205204
default -> PathExpansion.OFF;
206205
};
207206
}

0 commit comments

Comments
 (0)