@@ -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 /**
0 commit comments