1
1
package io .avaje .config ;
2
2
3
+ import static java .util .stream .Collectors .joining ;
4
+
3
5
import java .io .IOException ;
4
6
import java .io .InputStream ;
5
7
import java .io .InputStreamReader ;
6
8
import java .io .LineNumberReader ;
7
9
import java .io .Reader ;
8
10
import java .io .UncheckedIOException ;
11
+ import java .util .ArrayDeque ;
9
12
import java .util .ArrayList ;
13
+ import java .util .Deque ;
14
+ import java .util .Iterator ;
10
15
import java .util .LinkedHashMap ;
11
16
import java .util .List ;
12
17
import java .util .Map ;
13
- import java .util .Stack ;
14
18
import java .util .StringJoiner ;
15
19
16
20
import org .jspecify .annotations .NullMarked ;
@@ -42,12 +46,13 @@ enum MultiLineTrim {
42
46
enum State {
43
47
RequireKey ,
44
48
MultiLine ,
49
+ List ,
45
50
KeyOrValue ,
46
51
RequireTopKey
47
52
}
48
53
49
54
private final Map <String , String > keyValues = new LinkedHashMap <>();
50
- private final Stack <Key > keyStack = new Stack <>();
55
+ private final Deque <Key > keyStack = new ArrayDeque <>();
51
56
private final List <String > multiLines = new ArrayList <>();
52
57
53
58
private State state = State .RequireKey ;
@@ -80,7 +85,7 @@ private void processLine(String line) {
80
85
} else {
81
86
currentLine ++;
82
87
readIndent (line );
83
- if (state == State .MultiLine ) {
88
+ if (state == State .MultiLine || state == State . List ) {
84
89
processMultiLine (line );
85
90
} else {
86
91
processNext (line );
@@ -92,6 +97,9 @@ private void checkFinalMultiLine() {
92
97
if (state == State .MultiLine ) {
93
98
addKeyVal (multiLineValue ());
94
99
}
100
+ if (state == State .List ) {
101
+ addKeyVal (listValue ());
102
+ }
95
103
}
96
104
97
105
private void processMultiLine (String line ) {
@@ -112,18 +120,32 @@ private void processMultiLine(String line) {
112
120
}
113
121
114
122
private void multiLineEnd (String line ) {
115
- addKeyVal (multiLineValue ());
123
+ if (state == State .MultiLine ) addKeyVal (multiLineValue ());
124
+ else {
125
+ addKeyVal (listValue ());
126
+ }
116
127
processNext (line );
117
128
}
118
129
130
+ private String listValue () {
131
+ if (multiLines .isEmpty ()) {
132
+ return "" ;
133
+ }
134
+ multiLineTrimTrailing ();
135
+ var result =
136
+ multiLines .stream ().map (s -> s .trim ().substring (1 ).stripLeading ()).collect (joining ("," ));
137
+ multiLineEnd ();
138
+ return result ;
139
+ }
140
+
119
141
private String multiLineValue () {
120
142
if (multiLines .isEmpty ()) {
121
143
return "" ;
122
144
}
123
145
if (multiLineTrim != MultiLineTrim .Keep ) {
124
146
multiLineTrimTrailing ();
125
147
}
126
- String join = ( multiLineTrim == MultiLineTrim .Implicit ) ? " " : "\n " ;
148
+ String join = multiLineTrim == MultiLineTrim .Implicit ? " " : "\n " ;
127
149
StringBuilder sb = new StringBuilder ();
128
150
int lastIndex = multiLines .size () - 1 ;
129
151
for (int i = 0 ; i <= lastIndex ; i ++) {
@@ -184,6 +206,8 @@ private void processNext(String line) {
184
206
if (trimmedValue .startsWith ("|" )) {
185
207
multilineStart (multiLineTrimMode (trimmedValue ));
186
208
209
+ } else if (trimmedValue .startsWith ("-" )) {
210
+ listStart (multiLineTrimMode (trimmedValue ));
187
211
} else if (trimmedValue .isEmpty () || trimmedValue .startsWith ("#" )) {
188
212
// empty or comment
189
213
state = State .KeyOrValue ;
@@ -230,7 +254,11 @@ private void processNonKey(String line) {
230
254
if (currentIndent <= keyIndent ) {
231
255
throw new IllegalStateException ("Value not indented enough for key " + fullKey () + " at line: " + currentLine + " line[" + line + "]" );
232
256
}
233
- multilineStart (MultiLineTrim .Implicit );
257
+ if (line .stripLeading ().charAt (0 ) == '-' ) {
258
+ listStart (MultiLineTrim .Implicit );
259
+ } else {
260
+ multilineStart (MultiLineTrim .Implicit );
261
+ }
234
262
multiLineIndent = currentIndent ;
235
263
multiLines .add (line );
236
264
}
@@ -241,6 +269,12 @@ private void multilineStart(MultiLineTrim trim) {
241
269
multiLineTrim = trim ;
242
270
}
243
271
272
+ private void listStart (MultiLineTrim trim ) {
273
+ state = State .List ;
274
+ multiLineIndent = 0 ;
275
+ multiLineTrim = trim ;
276
+ }
277
+
244
278
private void multiLineEnd () {
245
279
state = State .RequireKey ;
246
280
multiLineIndent = 0 ;
@@ -281,8 +315,9 @@ private String unquoteValue(char quoteChar, String value) {
281
315
282
316
private String fullKey () {
283
317
StringJoiner fullKey = new StringJoiner ("." );
284
- for (Key next : keyStack ) {
285
- fullKey .add (next .key ());
318
+ Iterator <Key > it = keyStack .descendingIterator ();
319
+ while (it .hasNext ()) {
320
+ fullKey .add (it .next ().key ());
286
321
}
287
322
return fullKey .toString ();
288
323
}
@@ -302,10 +337,7 @@ private String trimKey(String indentKey) {
302
337
}
303
338
304
339
private String unquoteKey (String value ) {
305
- if (value .startsWith ("'" ) && value .endsWith ("'" )) {
306
- return value .substring (1 , value .length () - 1 );
307
- }
308
- if (value .startsWith ("\" " ) && value .endsWith ("\" " )) {
340
+ if (value .startsWith ("'" ) && value .endsWith ("'" ) || value .startsWith ("\" " ) && value .endsWith ("\" " )) {
309
341
return value .substring (1 , value .length () - 1 );
310
342
}
311
343
return value ;
0 commit comments