View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.config;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.commons.utils.CollectionUtils.*;
21  import static org.apache.juneau.junit.bct.BctAssertions.*;
22  import static org.junit.jupiter.api.Assertions.*;
23  
24  import java.io.*;
25  import java.net.*;
26  import java.util.*;
27  import java.util.concurrent.*;
28  
29  import org.apache.juneau.*;
30  import org.apache.juneau.collections.*;
31  import org.apache.juneau.config.event.*;
32  import org.apache.juneau.config.mod.*;
33  import org.apache.juneau.config.store.*;
34  import org.apache.juneau.parser.*;
35  import org.apache.juneau.svl.*;
36  import org.apache.juneau.uon.*;
37  import org.junit.jupiter.api.*;
38  
39  class Config_Test extends TestBase {
40  
41  	private Config.Builder cb = Config.create().store(MemoryStore.DEFAULT).name("Test.cfg");
42  
43  	private Config init(String...lines) {
44  		MemoryStore.DEFAULT.update("Test.cfg", lines);
45  		return cb.build().rollback();
46  	}
47  
48  	//====================================================================================================
49  	//	public String get(String key)
50  	//====================================================================================================
51  	@Test void get() {
52  		var c = init("a=1", "[S]", "b=2");
53  
54  		assertEquals("1", c.get("a").get());
55  		assertEquals("1", c.get("a").get());
56  		assertEquals("2", c.get("S/b").get());
57  		assertNull(c.get("b").orElse(null));
58  		assertNull(c.get("S/c").orElse(null));
59  		assertNull(c.get("T/d").orElse(null));
60  		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'key' cannot be null.", ()->c.get(null));
61  		c.close();
62  	}
63  
64  	//====================================================================================================
65  	//	public Config set(String key, String value)
66  	//====================================================================================================
67  	@Test void set1() throws Exception {
68  		var c = init("a1=1", "[S]", "b1=1");
69  
70  		c.set("a1", "2");
71  		c.set("a2", "3");
72  		c.set("a3", "4");
73  		c.set("S/b1", "5");
74  		c.set("S/b2", "6");
75  		c.set("T/c1", "7");
76  
77  		assertEquals("2", c.get("a1").get());
78  		assertEquals("3", c.get("a2").get());
79  		assertEquals("4", c.get("a3").get());
80  		assertEquals("5", c.get("S/b1").get());
81  		assertEquals("6", c.get("S/b2").get());
82  		assertEquals("7", c.get("T/c1").get());
83  
84  		c.commit();
85  
86  		assertEquals("2", c.get("a1").get());
87  		assertEquals("3", c.get("a2").get());
88  		assertEquals("4", c.get("a3").get());
89  		assertEquals("5", c.get("S/b1").get());
90  		assertEquals("6", c.get("S/b2").get());
91  		assertEquals("7", c.get("T/c1").get());
92  
93  		c = cb.build();
94  
95  		assertEquals("2", c.get("a1").get());
96  		assertEquals("3", c.get("a2").get());
97  		assertEquals("4", c.get("a3").get());
98  		assertEquals("5", c.get("S/b1").get());
99  		assertEquals("6", c.get("S/b2").get());
100 		assertEquals("7", c.get("T/c1").get());
101 
102 		assertEquals("a1 = 2|a2 = 3|a3 = 4|[S]|b1 = 5|b2 = 6|[T]|c1 = 7|", pipedLines(c));
103 	}
104 
105 	//====================================================================================================
106 	//	public Config set(String key, Object value)
107 	//====================================================================================================
108 	@Test void set2() throws Exception {
109 		var c = init("a1=1", "[S]", "b1=1");
110 
111 		c.set("a1", 2);
112 		c.set("a2", 3);
113 		c.set("a3", 4);
114 		c.set("S/b1", 5);
115 		c.set("S/b2", 6);
116 		c.set("T/c1", 7);
117 
118 		assertEquals("2", c.get("a1").get());
119 		assertEquals("3", c.get("a2").get());
120 		assertEquals("4", c.get("a3").get());
121 		assertEquals("5", c.get("S/b1").get());
122 		assertEquals("6", c.get("S/b2").get());
123 		assertEquals("7", c.get("T/c1").get());
124 
125 		c.commit();
126 
127 		assertEquals("2", c.get("a1").get());
128 		assertEquals("3", c.get("a2").get());
129 		assertEquals("4", c.get("a3").get());
130 		assertEquals("5", c.get("S/b1").get());
131 		assertEquals("6", c.get("S/b2").get());
132 		assertEquals("7", c.get("T/c1").get());
133 
134 		c = cb.build();
135 
136 		assertEquals("2", c.get("a1").get());
137 		assertEquals("3", c.get("a2").get());
138 		assertEquals("4", c.get("a3").get());
139 		assertEquals("5", c.get("S/b1").get());
140 		assertEquals("6", c.get("S/b2").get());
141 		assertEquals("7", c.get("T/c1").get());
142 
143 		assertEquals("a1 = 2|a2 = 3|a3 = 4|[S]|b1 = 5|b2 = 6|[T]|c1 = 7|", pipedLines(c));
144 	}
145 
146 	//====================================================================================================
147 	//	public Config set(String key, Object value, Serializer serializer)
148 	//====================================================================================================
149 	@Test void set3() {
150 		var c = init("a1=1", "[S]", "b1=1");
151 
152 		var b = new ABean().init();
153 		c.set("a1", b, UonSerializer.DEFAULT);
154 		c.set("a2", b, UonSerializer.DEFAULT);
155 		c.set("a3", b, UonSerializer.DEFAULT);
156 		c.set("S/b1", b, UonSerializer.DEFAULT);
157 		c.set("S/b2", b, UonSerializer.DEFAULT);
158 		c.set("T/c1", b, UonSerializer.DEFAULT);
159 
160 		assertEquals("(foo=bar)", c.get("a1").get());
161 		assertEquals("(foo=bar)", c.get("a2").get());
162 		assertEquals("(foo=bar)", c.get("a3").get());
163 		assertEquals("(foo=bar)", c.get("S/b1").get());
164 		assertEquals("(foo=bar)", c.get("S/b2").get());
165 		assertEquals("(foo=bar)", c.get("T/c1").get());
166 	}
167 
168 	//====================================================================================================
169 	//	public Config set(String key, Object value, Serializer serializer, ConfigMod[] modifiers, String comment, List<String> preLines)
170 	//====================================================================================================
171 	@Test void set4() throws Exception {
172 		var c = init("a1=1", "[S]", "b1=1");
173 
174 		var b = new ABean().init();
175 		c.set("a1", b, UonSerializer.DEFAULT, "*", "comment", l("#c1","#c2"));
176 		c.set("a2", b, UonSerializer.DEFAULT, "*", "comment", l("#c1","#c2"));
177 		c.set("a3", b, UonSerializer.DEFAULT, "*", "comment", l("#c1","#c2"));
178 		c.set("S/b1", b, UonSerializer.DEFAULT, "*", "comment", l("#c1","#c2"));
179 		c.set("S/b2", b, UonSerializer.DEFAULT, "*", "comment", l("#c1","#c2"));
180 		c.set("T/c1", b, UonSerializer.DEFAULT, "*", "comment", l("#c1","#c2"));
181 
182 		assertEquals("#c1|#c2|a1<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a2<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a3<*> = {RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1<*> = {RhMWWFIFVksf} # comment|#c1|#c2|b2<*> = {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1<*> = {RhMWWFIFVksf} # comment|", pipedLines(c));
183 		c.commit();
184 		assertEquals("#c1|#c2|a1<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a2<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a3<*> = {RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1<*> = {RhMWWFIFVksf} # comment|#c1|#c2|b2<*> = {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1<*> = {RhMWWFIFVksf} # comment|", pipedLines(c));
185 		c = cb.build();
186 		assertEquals("#c1|#c2|a1<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a2<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a3<*> = {RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1<*> = {RhMWWFIFVksf} # comment|#c1|#c2|b2<*> = {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1<*> = {RhMWWFIFVksf} # comment|", pipedLines(c));
187 
188 		assertEquals("(foo=bar)", c.get("a1").get());
189 		assertEquals("(foo=bar)", c.get("a2").get());
190 		assertEquals("(foo=bar)", c.get("a3").get());
191 		assertEquals("(foo=bar)", c.get("S/b1").get());
192 		assertEquals("(foo=bar)", c.get("S/b2").get());
193 		assertEquals("(foo=bar)", c.get("T/c1").get());
194 	}
195 
196 	//====================================================================================================
197 	//	public Config remove(String key)
198 	//====================================================================================================
199 	@Test void remove() throws Exception {
200 		var c = init("a1=1", "a2=2", "[S]", "b1=1");
201 
202 		c.remove("a1");
203 		c.remove("a2");
204 		c.remove("a3");
205 		c.remove("S/b1");
206 		c.remove("T/c1");
207 
208 		assertEquals("[S]|", pipedLines(c));
209 		c.commit();
210 		assertEquals("[S]|", pipedLines(c));
211 		c = cb.build();
212 		assertEquals("[S]|", pipedLines(c));
213 	}
214 
215 	//====================================================================================================
216 	//	public String getString1(String key)
217 	//====================================================================================================
218 	@Test void xgetString1() {
219 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
220 
221 		assertEquals("1", c.get("a1").as(String.class).orElse(null));
222 		assertEquals("2", c.get("a2").as(String.class).orElse(null));
223 		assertEquals(null, c.get("a3").as(String.class).orElse(null));
224 		assertEquals("1", c.get("S/b1").as(String.class).orElse(null));
225 		assertEquals("", c.get("S/b2").as(String.class).orElse(null));
226 		assertEquals(null, c.get("S/b3").as(String.class).orElse(null));
227 		assertEquals(null, c.get("T/c1").as(String.class).orElse(null));
228 	}
229 
230 	//====================================================================================================
231 	//	public String getString(String key, String def)
232 	//====================================================================================================
233 	@Test void getString2() {
234 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
235 		assertEquals("1", c.get("a1").orElse("foo"));
236 		assertEquals("2", c.get("a2").orElse("foo"));
237 		assertEquals("foo", c.get("a3").orElse("foo"));
238 		assertEquals("1", c.get("S/b1").orElse("foo"));
239 		assertEquals("", c.get("S/b2").orElse("foo"));
240 		assertEquals("foo", c.get("S/b3").orElse("foo"));
241 		assertEquals("foo", c.get("T/c1").orElse("foo"));
242 	}
243 
244 	//====================================================================================================
245 	//	public String[] getStringArray(String key)
246 	//====================================================================================================
247 	@Test void getStringArray1() {
248 		var c = init("a1=1,2", "a2= 2 , 3 ", "[S]", "b1=1", "b2=");
249 		assertList(c.get("a1").as(String[].class).orElse(null), "1", "2");
250 		assertList(c.get("a2").as(String[].class).orElse(null), "2", "3");
251 		assertNull(c.get("a3").as(String[].class).orElse(null));
252 		assertList(c.get("S/b1").as(String[].class).orElse(null), "1");
253 		assertEmpty(c.get("S/b2").as(String[].class).orElse(null));
254 		assertNull(c.get("S/b3").as(String[].class).orElse(null));
255 		assertNull(c.get("T/c1").as(String[].class).orElse(null));
256 	}
257 
258 	//====================================================================================================
259 	//	public String[] getStringArray(String key, String[] def)
260 	//====================================================================================================
261 	@Test void getStringArray2() {
262 		var c = init("a1=1,2", "a2= 2 , 3 ", "[S]", "b1=1", "b2=");
263 		assertList(c.get("a1").asStringArray().orElse(a("foo")), "1", "2");
264 		assertList(c.get("a2").asStringArray().orElse(a("foo")), "2", "3");
265 		assertList(c.get("a3").asStringArray().orElse(a("foo")), "foo");
266 		assertList(c.get("S/b1").asStringArray().orElse(a("foo")), "1");
267 		assertEmpty(c.get("S/b2").asStringArray().orElse(a("foo")));
268 		assertList(c.get("S/b3").asStringArray().orElse(a("foo")), "foo");
269 		assertList(c.get("T/c1").asStringArray().orElse(a("foo")), "foo");
270 	}
271 
272 	//====================================================================================================
273 	//	public int getInt(String key)
274 	//====================================================================================================
275 	@Test void getInt1() {
276 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
277 		assertEquals(1, c.get("a1").asInteger().orElse(0));
278 		assertEquals(2, c.get("a2").asInteger().orElse(0));
279 		assertEquals(0, c.get("a3").asInteger().orElse(0));
280 		assertEquals(1, c.get("S/b1").asInteger().orElse(0));
281 		assertEquals(0, c.get("S/b2").asInteger().orElse(0));
282 		assertEquals(0, c.get("S/b3").asInteger().orElse(0));
283 		assertEquals(0, c.get("T/c1").asInteger().orElse(0));
284 	}
285 
286 	@Test void getInt1BadValues() {
287 		var c = init("a1=foo", "a2=2.3", "a3=[1]", "a4=false");
288 		assertThrows(Exception.class, ()->c.get("a1").asInteger().orElse(0));
289 		assertThrows(Exception.class, ()->c.get("a2").asInteger().orElse(0));
290 		assertThrows(Exception.class, ()->c.get("a3").asInteger().orElse(0));
291 		assertThrows(Exception.class, ()->c.get("a4").asInteger().orElse(0));
292 	}
293 
294 	//====================================================================================================
295 	//	public int getInt2(String key, int def)
296 	//====================================================================================================
297 	@Test void getInt2() {
298 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
299 		assertEquals(1, c.get("a1").asInteger().orElse(-1));
300 		assertEquals(2, c.get("a2").asInteger().orElse(-1));
301 		assertEquals(-1, c.get("a3").asInteger().orElse(-1));
302 		assertEquals(1, c.get("S/b1").asInteger().orElse(-1));
303 		assertEquals(-1, c.get("S/b2").asInteger().orElse(-1));
304 		assertEquals(-1, c.get("S/b3").asInteger().orElse(-1));
305 		assertEquals(-1, c.get("T/c1").asInteger().orElse(-1));
306 	}
307 
308 	@Test void getInt2BadValues() {
309 		var c = init("a1=foo", "a2=2.3", "a3=[1]", "a4=false");
310 		assertThrows(Exception.class, ()->c.get("a1").asInteger().orElse(-1));
311 		assertThrows(Exception.class, ()->c.get("a2").asInteger().orElse(-1));
312 		assertThrows(Exception.class, ()->c.get("a3").asInteger().orElse(-1));
313 		assertThrows(Exception.class, ()->c.get("a4").asInteger().orElse(-1));
314 	}
315 
316 	//====================================================================================================
317 	//	public boolean getBoolean(String key)
318 	//====================================================================================================
319 	@Test void getBoolean1() {
320 		var c = init("a1=true", "a2=false", "[S]", "b1=TRUE", "b2=");
321 		assertEquals(true, c.get("a1").asBoolean().orElse(false));
322 		assertEquals(false, c.get("a2").asBoolean().orElse(false));
323 		assertEquals(false, c.get("a3").asBoolean().orElse(false));
324 		assertEquals(true, c.get("S/b1").asBoolean().orElse(false));
325 		assertEquals(false, c.get("S/b2").asBoolean().orElse(false));
326 		assertEquals(false, c.get("S/b3").asBoolean().orElse(false));
327 		assertEquals(false, c.get("T/c1").asBoolean().orElse(false));
328 	}
329 
330 	@Test void getBoolean1BadValues() {
331 		var c = init("a1=foo", "a2=2.3", "a3=[1]", "a4=T");
332 		assertEquals(false, c.get("a1").asBoolean().orElse(false));
333 		assertEquals(false, c.get("a2").asBoolean().orElse(false));
334 		assertEquals(false, c.get("a3").asBoolean().orElse(false));
335 		assertEquals(false, c.get("a4").asBoolean().orElse(false));
336 	}
337 
338 	//====================================================================================================
339 	//	public boolean getBoolean(String key, boolean def)
340 	//====================================================================================================
341 	@Test void getBoolean2() {
342 		var c = init("a1=true", "a2=false", "[S]", "b1=TRUE", "b2=");
343 		assertEquals(true, c.get("a1").asBoolean().orElse(true));
344 		assertEquals(false, c.get("a2").asBoolean().orElse(true));
345 		assertEquals(true, c.get("a3").asBoolean().orElse(true));
346 		assertEquals(true, c.get("S/b1").asBoolean().orElse(true));
347 		assertEquals(true, c.get("S/b2").asBoolean().orElse(true));
348 		assertEquals(true, c.get("S/b3").asBoolean().orElse(true));
349 		assertEquals(true, c.get("T/c1").asBoolean().orElse(true));
350 	}
351 
352 	@Test void getBoolean2BadValues() {
353 		var c = init("a1=foo", "a2=2.3", "a3=[1]", "a4=T");
354 		assertEquals(false, c.get("a1").asBoolean().orElse(true));
355 		assertEquals(false, c.get("a2").asBoolean().orElse(true));
356 		assertEquals(false, c.get("a3").asBoolean().orElse(true));
357 		assertEquals(false, c.get("a4").asBoolean().orElse(true));
358 	}
359 
360 	//====================================================================================================
361 	//	public long getLong(String key)
362 	//====================================================================================================
363 	@Test void getLong1() {
364 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
365 		assertEquals(1L, c.get("a1").asLong().orElse(0L));
366 		assertEquals(2L, c.get("a2").asLong().orElse(0L));
367 		assertEquals(0L, c.get("a3").asLong().orElse(0L));
368 		assertEquals(1L, c.get("S/b1").asLong().orElse(0L));
369 		assertEquals(0L, c.get("S/b2").asLong().orElse(0L));
370 		assertEquals(0L, c.get("S/b3").asLong().orElse(0L));
371 		assertEquals(0L, c.get("T/c1").asLong().orElse(0L));
372 	}
373 
374 	@Test void getLong1BadValues() {
375 		var c = init("a1=foo", "a2=2.3", "a3=[1]", "a4=false");
376 		assertThrows(Exception.class, ()->c.get("a1").as(long.class));
377 		assertThrows(Exception.class, ()->c.get("a2").as(long.class));
378 		assertThrows(Exception.class, ()->c.get("a3").as(long.class));
379 		assertThrows(Exception.class, ()->c.get("a4").as(long.class));
380 	}
381 
382 	//====================================================================================================
383 	//	public long getLong(String key, long def)
384 	//====================================================================================================
385 	@Test void getLong2() {
386 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
387 		assertEquals(1L, c.get("a1").asLong().orElse(Long.MAX_VALUE));
388 		assertEquals(2L, c.get("a2").asLong().orElse(Long.MAX_VALUE));
389 		assertEquals(Long.MAX_VALUE, c.get("a3").asLong().orElse(Long.MAX_VALUE));
390 		assertEquals(1L, c.get("S/b1").asLong().orElse(Long.MAX_VALUE));
391 		assertEquals(Long.MAX_VALUE, c.get("S/b2").asLong().orElse(Long.MAX_VALUE));
392 		assertEquals(Long.MAX_VALUE, c.get("S/b3").asLong().orElse(Long.MAX_VALUE));
393 		assertEquals(Long.MAX_VALUE, c.get("T/c1").asLong().orElse(Long.MAX_VALUE));
394 	}
395 
396 	@Test void getLong2BadValues() {
397 		var c = init("a1=foo", "a2=2.3", "a3=[1]", "a4=false");
398 
399 		assertThrows(NumberFormatException.class, ()->c.get("a1").asLong().orElse(-1L));
400 		assertThrows(NumberFormatException.class, ()->c.get("a2").asLong().orElse(-1L));
401 		assertThrows(NumberFormatException.class, ()->c.get("a3").asLong().orElse(-1L));
402 		assertThrows(NumberFormatException.class, ()->c.get("a4").asLong().orElse(-1L));
403 	}
404 
405 	//====================================================================================================
406 	//	public boolean getBytes(String key)
407 	//====================================================================================================
408 	@Test void getBytes1() {
409 		var c = init("a1=Zm9v", "a2=Zm", "\t9v", "a3=");
410 
411 		assertList(c.get("a1").as(byte[].class).get(), (byte)102, (byte)111, (byte)111);
412 		assertList(c.get("a2").as(byte[].class).get(), (byte)102, (byte)111, (byte)111);
413 		assertEmpty(c.get("a3").as(byte[].class).get());
414 		assertFalse(c.get("a4").as(byte[].class).isPresent());
415 	}
416 
417 	//====================================================================================================
418 	//	public boolean getBytes(String key, byte[] def)
419 	//====================================================================================================
420 	@Test void getBytes2() {
421 		var c = init("a1=Zm9v", "a2=Zm", "\t9v", "a3=");
422 
423 		assertList(c.get("a1").asBytes().orElse(bytes(1)), (byte)102, (byte)111, (byte)111);
424 		assertList(c.get("a2").asBytes().orElse(bytes(1)), (byte)102, (byte)111, (byte)111);
425 		assertEmpty(c.get("a3").asBytes().orElse(bytes(1)));
426 		assertList(c.get("a4").asBytes().orElse(bytes(1)), (byte)1);
427 	}
428 
429 	//====================================================================================================
430 	//	public <T> T getObject(String key, Type type, Type...args) throws ParseException
431 	//====================================================================================================
432 	@Test void getObject1() {
433 		var c = init(
434 			"a1={foo:123}",
435 			"a2=[{foo:123}]",
436 			"a3=",
437 			"a4=\t{",
438 			"\t foo : 123 /* comment */",
439 			"\t}"
440 			);
441 
442 		var a1 = (Map<String,Integer>) c.get("a1").as(Map.class, String.class, Integer.class).get();
443 		assertJson("{foo:123}", a1);
444 		assertInstanceOf(String.class, a1.keySet().iterator().next());
445 		assertInstanceOf(Integer.class, a1.values().iterator().next());
446 
447 		var a2a = (List<Map<String,Integer>>) c.get("a2").as(List.class, Map.class, String.class, Integer.class).get();
448 		assertJson("[{foo:123}]", a2a);
449 		assertInstanceOf(String.class, a2a.get(0).keySet().iterator().next());
450 		assertInstanceOf(Integer.class, a2a.get(0).values().iterator().next());
451 
452 		var a2b = (List<ABean>) c.get("a2").as(List.class, ABean.class).get();
453 		assertJson("[{foo:'123'}]", a2b);
454 		assertInstanceOf(ABean.class, a2b.get(0));
455 
456 		var a3 = (Map<String,Integer>) c.get("a3").as(Map.class, String.class, Integer.class).orElse(null);
457 		assertNull(a3);
458 
459 		var a4a = (Map<String,Integer>) c.get("a4").as(Map.class, String.class, Integer.class).get();
460 		assertJson("{foo:123}", a4a);
461 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
462 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
463 
464 		var a4b = c.get("a4").as(ABean.class).get();
465 		assertJson("{foo:'123'}", a4b);
466 		assertInstanceOf(ABean.class, a4b);
467 	}
468 
469 	//====================================================================================================
470 	//	public <T> T getObject(String key, Parser parser, Type type, Type...args) throws ParseException
471 	//====================================================================================================
472 	@Test void getObject2() {
473 		var c = init(
474 			"a1=(foo=123)",
475 			"a2=@((foo=123))",
476 			"a3=",
477 			"a4=\t(",
478 			"\t foo = 123",
479 			"\t)"
480 			);
481 
482 		var a1 = (Map<String,Integer>) c.get("a1").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).get();
483 		assertJson("{foo:123}", a1);
484 		assertInstanceOf(String.class, a1.keySet().iterator().next());
485 		assertInstanceOf(Integer.class, a1.values().iterator().next());
486 
487 		var a2a = (List<Map<String,Integer>>) c.get("a2").as(UonParser.DEFAULT, List.class, Map.class, String.class, Integer.class).get();
488 		assertJson("[{foo:123}]", a2a);
489 		assertInstanceOf(String.class, a2a.get(0).keySet().iterator().next());
490 		assertInstanceOf(Integer.class, a2a.get(0).values().iterator().next());
491 
492 		var a2b = (List<ABean>) c.get("a2").as(UonParser.DEFAULT, List.class, ABean.class).get();
493 		assertJson("[{foo:'123'}]", a2b);
494 		assertInstanceOf(ABean.class, a2b.get(0));
495 
496 		var a3 = (Map<String,Integer>) c.get("a3").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).orElse(null);
497 		assertNull(a3);
498 
499 		var a4a = (Map<String,Integer>) c.get("a4").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).get();
500 		assertJson("{foo:123}", a4a);
501 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
502 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
503 
504 		var a4b = c.get("a4").as(UonParser.DEFAULT, ABean.class).get();
505 		assertJson("{foo:'123'}", a4b);
506 		assertInstanceOf(ABean.class, a4b);
507 	}
508 
509 	//====================================================================================================
510 	//	public <T> T getObject(String key, Class<T> type) throws ParseException
511 	//====================================================================================================
512 	@SuppressWarnings("rawtypes")
513 	@Test void getObject3() {
514 		var c = init(
515 			"a1={foo:123}",
516 			"a2=[{foo:123}]",
517 			"a3=",
518 			"a4=\t{",
519 			"\t foo : 123 /* comment */",
520 			"\t}"
521 			);
522 
523 		var a1 = c.get("a1").as(Map.class).get();
524 		assertJson("{foo:123}", a1);
525 		assertInstanceOf(String.class, a1.keySet().iterator().next());
526 		assertInstanceOf(Integer.class, a1.values().iterator().next());
527 
528 		var a2a = c.get("a2").as(List.class).get();
529 		assertJson("[{foo:123}]", a2a);
530 		assertInstanceOf(String.class, ((Map)a2a.get(0)).keySet().iterator().next());
531 		assertInstanceOf(Integer.class, ((Map)a2a.get(0)).values().iterator().next());
532 
533 		var a3 = c.get("a3").as(Map.class).orElse(null);
534 		assertNull(a3);
535 
536 		var a4a = c.get("a4").as(Map.class).get();
537 		assertJson("{foo:123}", a4a);
538 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
539 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
540 
541 		var a4b = c.get("a4").as(ABean.class).orElse(null);
542 		assertJson("{foo:'123'}", a4b);
543 		assertInstanceOf(ABean.class, a4b);
544 	}
545 
546 	//====================================================================================================
547 	//	public <T> T getObject(String key, Parser parser, Class<T> type) throws ParseException
548 	//====================================================================================================
549 	@SuppressWarnings("rawtypes")
550 	@Test void getObject4() {
551 		var c = init(
552 			"a1=(foo=123)",
553 			"a2=@((foo=123))",
554 			"a3=",
555 			"a4=\t(",
556 			"\t foo = 123",
557 			"\t)"
558 		);
559 
560 		var a1 = c.get("a1").as(UonParser.DEFAULT, Map.class).get();
561 		assertJson("{foo:123}", a1);
562 		assertInstanceOf(String.class, a1.keySet().iterator().next());
563 		assertInstanceOf(Integer.class, a1.values().iterator().next());
564 
565 		var a2a = c.get("a2").as(UonParser.DEFAULT, List.class).get();
566 		assertJson("[{foo:123}]", a2a);
567 		assertInstanceOf(String.class, ((Map)a2a.get(0)).keySet().iterator().next());
568 		assertInstanceOf(Integer.class, ((Map)a2a.get(0)).values().iterator().next());
569 
570 		var a3 = c.get("a3").as(UonParser.DEFAULT, Map.class).orElse(null);
571 		assertNull(a3);
572 
573 		var a4a = c.get("a4").as(UonParser.DEFAULT, Map.class).get();
574 		assertJson("{foo:123}", a4a);
575 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
576 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
577 
578 		var a4b = c.get("a4").as(UonParser.DEFAULT, ABean.class).get();
579 		assertJson("{foo:'123'}", a4b);
580 		assertInstanceOf(ABean.class, a4b);
581 	}
582 
583 	//====================================================================================================
584 	//	public <T> T getObjectWithDefault(String key, T def, Class<T> type) throws ParseException
585 	//====================================================================================================
586 	@SuppressWarnings("rawtypes")
587 	@Test void getObjectWithDefault1() {
588 		var c = init(
589 			"a1={foo:123}",
590 			"a2=[{foo:123}]",
591 			"a3=",
592 			"a4=\t{",
593 			"\t foo : 123 /* comment */",
594 			"\t}"
595 		);
596 
597 		var a1 = c.get("a1").as(Map.class).orElseGet(JsonMap::new);
598 		assertJson("{foo:123}", a1);
599 		assertInstanceOf(String.class, a1.keySet().iterator().next());
600 		assertInstanceOf(Integer.class, a1.values().iterator().next());
601 
602 		var a1b = c.get("a1b").as(Map.class).orElseGet(JsonMap::new);
603 		assertJson("{}", a1b);
604 
605 		var a2a = c.get("a2").as(List.class).orElseGet(JsonList::new);
606 		assertJson("[{foo:123}]", a2a);
607 		assertInstanceOf(String.class, ((Map)a2a.get(0)).keySet().iterator().next());
608 		assertInstanceOf(Integer.class, ((Map)a2a.get(0)).values().iterator().next());
609 
610 		var a2b = c.get("a2b").as(List.class).orElseGet(JsonList::new);
611 		assertJson("[]", a2b);
612 
613 		var a3 = c.get("a3").as(Map.class).orElseGet(JsonMap::new);
614 		assertJson("{}", a3);
615 
616 		var a4a = c.get("a4").as(Map.class).orElseGet(JsonMap::new);
617 		assertJson("{foo:123}", a4a);
618 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
619 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
620 
621 		var a4b = c.get("a4b").as(Map.class).orElseGet(JsonMap::new);
622 		assertJson("{}", a4b);
623 
624 		var a4c = c.get("a4c").as(ABean.class).orElse(new ABean().init());
625 		assertJson("{foo:'bar'}", a4c);
626 		assertInstanceOf(ABean.class, a4c);
627 	}
628 
629 	//====================================================================================================
630 	//	public <T> T getObjectWithDefault(String key, Parser parser, T def, Class<T> type) throws ParseException
631 	//====================================================================================================
632 	@SuppressWarnings("rawtypes")
633 	@Test void getObjectWithDefault2() {
634 		var c = init(
635 			"a1=(foo=123)",
636 			"a2=@((foo=123))",
637 			"a3=",
638 			"a4=\t(",
639 			"\t foo = 123",
640 			"\t)"
641 		);
642 
643 		var a1 = c.get("a1").as(UonParser.DEFAULT, Map.class).orElseGet(JsonMap::new);
644 		assertJson("{foo:123}", a1);
645 		assertInstanceOf(String.class, a1.keySet().iterator().next());
646 		assertInstanceOf(Integer.class, a1.values().iterator().next());
647 
648 		var a1b = c.get("a1b").as(UonParser.DEFAULT, Map.class).orElseGet(JsonMap::new);
649 		assertJson("{}", a1b);
650 
651 		var a2a = c.get("a2").as(UonParser.DEFAULT, List.class).orElseGet(JsonList::new);
652 		assertJson("[{foo:123}]", a2a);
653 		assertInstanceOf(String.class, ((Map)a2a.get(0)).keySet().iterator().next());
654 		assertInstanceOf(Integer.class, ((Map)a2a.get(0)).values().iterator().next());
655 
656 		var a2b = c.get("a2b").as(UonParser.DEFAULT, List.class).orElseGet(JsonList::new);
657 		assertJson("[]", a2b);
658 
659 		var a3 = c.get("a3").as(UonParser.DEFAULT, Map.class).orElseGet(JsonMap::new);
660 		assertJson("{}", a3);
661 
662 		var a4a = c.get("a4").as(UonParser.DEFAULT, Map.class).orElseGet(JsonMap::new);
663 		assertJson("{foo:123}", a4a);
664 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
665 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
666 
667 		var a4b = c.get("a4b").as(UonParser.DEFAULT, Map.class).orElseGet(JsonMap::new);
668 		assertJson("{}", a4b);
669 
670 		var a4c = c.get("a4c").as(UonParser.DEFAULT, ABean.class).orElse(new ABean().init());
671 		assertJson("{foo:'bar'}", a4c);
672 		assertInstanceOf(ABean.class, a4c);
673 	}
674 
675 	//====================================================================================================
676 	//	public <T> T getObjectWithDefault(String key, T def, Type type, Type...args) throws ParseException
677 	//====================================================================================================
678 	@Test void getObjectWithDefault3() {
679 		var c = init(
680 			"a1={foo:123}",
681 			"a2=[{foo:123}]",
682 			"a3=",
683 			"a4=\t{",
684 			"\t foo : 123 /* comment */",
685 			"\t}"
686 		);
687 
688 		var a1 = (Map<String,Integer>) c.get("a1").as(Map.class, String.class, Integer.class).orElse(new HashMap<>());
689 		assertJson("{foo:123}", a1);
690 		assertInstanceOf(String.class, a1.keySet().iterator().next());
691 		assertInstanceOf(Integer.class, a1.values().iterator().next());
692 
693 		var a1b = (Map<String,Integer>) c.get("a1b").as(Map.class, String.class, Integer.class).orElse(new HashMap<>());
694 		assertJson("{}", a1b);
695 
696 		var a2a = (List<Map<String,Integer>>) c.get("a2").as(List.class, Map.class, String.class, Integer.class).orElse(list());
697 		assertJson("[{foo:123}]", a2a);
698 		assertInstanceOf(String.class, a2a.get(0).keySet().iterator().next());
699 		assertInstanceOf(Integer.class, a2a.get(0).values().iterator().next());
700 
701 		var a2b = (List<ABean>) c.get("a2b").as(List.class, ABean.class).orElse(list());
702 		assertJson("[]", a2b);
703 
704 		var a3 = (Map<String,Object>) c.get("a3").as(Map.class, String.class, Object.class).orElse(new JsonMap());
705 		assertJson("{}", a3);
706 
707 		var a4a = (Map<String,Integer>) c.get("a4").as(Map.class, String.class, Integer.class).orElse(new HashMap<>());
708 		assertJson("{foo:123}", a4a);
709 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
710 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
711 
712 		var a4b = (Map<String,Integer>) c.get("a4b").as(Map.class, String.class, Integer.class).orElse(new HashMap<>());
713 		assertJson("{}", a4b);
714 
715 		var a4c = c.get("a4c").as(ABean.class).orElse(new ABean().init());
716 		assertJson("{foo:'bar'}", a4c);
717 		assertInstanceOf(ABean.class, a4c);
718 	}
719 
720 	//====================================================================================================
721 	//	public <T> T getObjectWithDefault(String key, Parser parser, T def, Type type, Type...args) throws ParseException
722 	//====================================================================================================
723 	@Test void getObjectWithDefault4() {
724 		var c = init(
725 			"a1=(foo=123)",
726 			"a2=@((foo=123))",
727 			"a3=",
728 			"a4=\t(",
729 			"\t foo = 123",
730 			"\t)"
731 		);
732 
733 		var a1 = (Map<String,Integer>) c.get("a1").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).orElse(new HashMap<>());
734 		assertJson("{foo:123}", a1);
735 		assertInstanceOf(String.class, a1.keySet().iterator().next());
736 		assertInstanceOf(Integer.class, a1.values().iterator().next());
737 
738 		var a1b = (Map<String,Integer>) c.get("a1b").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).orElse(new HashMap<>());
739 		assertJson("{}", a1b);
740 
741 		var a2a = (List<Map<String,Integer>>) c.get("a2").as(UonParser.DEFAULT, List.class, Map.class, String.class, Integer.class).orElse(list());
742 		assertJson("[{foo:123}]", a2a);
743 		assertInstanceOf(String.class, a2a.get(0).keySet().iterator().next());
744 		assertInstanceOf(Integer.class, a2a.get(0).values().iterator().next());
745 
746 		var a2b = (List<ABean>) c.get("a2b").as(UonParser.DEFAULT, List.class, ABean.class).orElse(list());
747 		assertJson("[]", a2b);
748 
749 		var a3 = (Map<String,Object>) c.get("a3").as(UonParser.DEFAULT,Map.class, String.class, Object.class).orElse( new JsonMap());
750 		assertJson("{}", a3);
751 
752 		var a4a = (Map<String,Integer>) c.get("a4").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).orElse(new HashMap<>());
753 		assertJson("{foo:123}", a4a);
754 		assertInstanceOf(String.class, a4a.keySet().iterator().next());
755 		assertInstanceOf(Integer.class, a4a.values().iterator().next());
756 
757 		var a4b = (Map<String,Integer>) c.get("a4b").as(UonParser.DEFAULT, Map.class, String.class, Integer.class).orElse(new HashMap<>());
758 		assertJson("{}", a4b);
759 
760 		var a4c = c.get("a4c").as(UonParser.DEFAULT, ABean.class).orElse(new ABean().init());
761 		assertJson("{foo:'bar'}", a4c);
762 		assertInstanceOf(ABean.class, a4c);
763 	}
764 
765 	//====================================================================================================
766 	//	public Set<String> getKeys(String section)
767 	//====================================================================================================
768 	@Test void getKeys() {
769 		var c = init("a1=1", "a2=2", "[S]", "b1=1", "b2=");
770 
771 		assertJson("['a1','a2']", c.getKeys(""));
772 		assertJson("['a1','a2']", c.getKeys(""));
773 		assertJson("['b1','b2']", c.getKeys("S"));
774 		assertEmpty(c.getKeys("T"));
775 
776 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'section' cannot be null.", ()->c.getKeys(null));
777 	}
778 
779 	//====================================================================================================
780 	//	public Config writeProperties(String section, Object bean, boolean ignoreUnknownProperties)
781 	//====================================================================================================
782 	@Test void writeProperties() {
783 		var a = new ABean().init();
784 		var b = new BBean().init();
785 
786 		var c = init("foo=qux", "[S]", "foo=baz", "bar=baz");
787 		c.getSection("S").writeToBean(a, true);
788 		assertJson("{foo:'baz'}", a);
789 		c.getSection("S").writeToBean(b, true);
790 		assertJson("{foo:'baz'}", b);
791 		assertThrowsWithMessage(ParseException.class, "Unknown property 'bar' encountered in configuration section 'S'.", ()->c.getSection("S").writeToBean(a, false));
792 		assertThrowsWithMessage(ParseException.class, "Unknown property 'bar' encountered in configuration section 'S'.", ()->c.getSection("S").writeToBean(b, false));
793 		c.getSection("").writeToBean(b, true);
794 		assertJson("{foo:'qux'}", b);
795 		c.getSection("").writeToBean(a, true);
796 		assertJson("{foo:'qux'}", a);
797 		c.getSection(null).writeToBean(a, true);
798 		assertJson("{foo:'qux'}", a);
799 	}
800 
801 	//====================================================================================================
802 	//	public <T> T getSectionAsBean(String sectionName, Class<T>c)
803 	//====================================================================================================
804 	@Test void getSectionAsBean1() {
805 		var c = init("foo=qux", "[S]", "foo=baz", "[T]", "foo=qux", "bar=qux");
806 
807 		var a = c.getSection("").asBean(ABean.class).get();
808 		assertJson("{foo:'qux'}", a);
809 		a = c.getSection("").asBean(ABean.class).get();
810 		assertJson("{foo:'qux'}", a);
811 		a = c.getSection(null).asBean(ABean.class).get();
812 		assertJson("{foo:'qux'}", a);
813 		a = c.getSection("S").asBean(ABean.class).get();
814 		assertJson("{foo:'baz'}", a);
815 
816 		var b = c.getSection("").asBean(BBean.class).get();
817 		assertJson("{foo:'qux'}", b);
818 		b = c.getSection("").asBean(BBean.class).get();
819 		assertJson("{foo:'qux'}", b);
820 		b = c.getSection("S").asBean(BBean.class).get();
821 		assertJson("{foo:'baz'}", b);
822 
823 		assertThrowsWithMessage(ParseException.class, "Unknown property 'bar' encountered in configuration section 'T'.", ()->c.getSection("T").asBean(ABean.class).get());
824 		assertThrowsWithMessage(ParseException.class, "Unknown property 'bar' encountered in configuration section 'T'.", ()->c.getSection("T").asBean(BBean.class).get());
825 	}
826 
827 	//====================================================================================================
828 	//	public <T> T getSectionAsBean(String section, Class<T> c, boolean ignoreUnknownProperties)
829 	//====================================================================================================
830 	@Test void getSectionAsBean2() {
831 		var c = init("foo=qux", "[S]", "foo=baz", "[T]", "foo=qux", "bar=qux");
832 
833 		var a = c.getSection("T").asBean(ABean.class, true).get();
834 		assertJson("{foo:'qux'}", a);
835 		var b = c.getSection("T").asBean(BBean.class, true).get();
836 		assertJson("{foo:'qux'}", b);
837 
838 		assertThrowsWithMessage(ParseException.class, "Unknown property 'bar' encountered in configuration section 'T'.", ()->c.getSection("T").asBean(ABean.class, false).get());
839 		assertThrowsWithMessage(ParseException.class, "Unknown property 'bar' encountered in configuration section 'T'.", ()->c.getSection("T").asBean(BBean.class, false).get());
840 	}
841 
842 	//====================================================================================================
843 	//	public JsonMap getSectionAsMap(String section)
844 	//====================================================================================================
845 	@Test void getSectionAsMap() {
846 		var c = init("a=1", "[S]", "b=2", "[T]");
847 
848 		assertJson("{a:'1'}", c.getSection("").asMap().get());
849 		assertJson("{a:'1'}", c.getSection("").asMap().get());
850 		assertJson("{a:'1'}", c.getSection(null).asMap().get());
851 		assertJson("{b:'2'}", c.getSection("S").asMap().get());
852 		assertJson("{}", c.getSection("T").asMap().get());
853 		assertFalse(c.getSection("U").asMap().isPresent());
854 	}
855 
856 	//====================================================================================================
857 	//	public <T> T getSectionAsInterface(String sectionName, Class<T> c)
858 	//====================================================================================================
859 	@Test void getSectionAsInterface() {
860 		var c = init("foo=qux", "[S]", "foo=baz", "[T]", "foo=qux", "bar=qux");
861 
862 		var a = c.getSection("").asInterface(AInterface.class).get();
863 		assertEquals("qux", a.getFoo());
864 
865 		a = c.getSection("").asInterface(AInterface.class).get();
866 		assertEquals("qux", a.getFoo());
867 
868 		a = c.getSection(null).asInterface(AInterface.class).get();
869 		assertEquals("qux", a.getFoo());
870 
871 		a = c.getSection("S").asInterface(AInterface.class).get();
872 		assertEquals("baz", a.getFoo());
873 
874 		a = c.getSection("T").asInterface(AInterface.class).get();
875 		assertEquals("qux", a.getFoo());
876 
877 		assertThrowsWithMessage(IllegalArgumentException.class, "Class 'org.apache.juneau.config.Config_Test$ABean' passed to toInterface() is not an interface.", ()->c.getSection("T").asInterface(ABean.class).get());
878 	}
879 
880 	public interface AInterface {
881 		String getFoo();
882 	}
883 
884 	//====================================================================================================
885 	//	public boolean exists(String key)
886 	//====================================================================================================
887 	@Test void exists() {
888 		var c = init("a=1", "[S]", "b=2", "c=", "[T]");
889 
890 		assertTrue(c.exists("a"));
891 		assertFalse(c.exists("b"));
892 		assertTrue(c.exists("S/b"));
893 		assertFalse(c.exists("S/c"));
894 		assertFalse(c.exists("T/d"));
895 		assertFalse(c.exists("U/e"));
896 	}
897 
898 	//====================================================================================================
899 	//	public Config setSection(String name, List<String> preLines)
900 	//====================================================================================================
901 	@Test void setSection1() {
902 		var c = init();
903 
904 		c.setSection("", l("#C1", "#C2"));
905 		assertEquals("#C1|#C2||", pipedLines(c));
906 
907 		c.setSection("", l("#C3", "#C4"));
908 		assertEquals("#C3|#C4||", pipedLines(c));
909 
910 		c.setSection("S1", l("", "#C5", "#C6"));
911 		assertEquals("#C3|#C4|||#C5|#C6|[S1]|", pipedLines(c));
912 
913 		c.setSection("S1", null);
914 		assertEquals("#C3|#C4|||#C5|#C6|[S1]|", pipedLines(c));
915 
916 		c.setSection("S1", Collections.<String>emptyList());
917 		assertEquals("#C3|#C4||[S1]|", pipedLines(c));
918 
919 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'section' cannot be null.", ()->c.setSection(null, l("", "#C5", "#C6")));
920 	}
921 
922 	//====================================================================================================
923 	//	public Config setSection(String name, List<String> preLines, Map<String,Object> contents)
924 	//====================================================================================================
925 	@Test void setSection2() {
926 		var c = init();
927 		var m = JsonMap.of("a", "b");
928 
929 		c.setSection("", l("#C1", "#C2"), m);
930 		assertEquals("#C1|#C2||a = b|", pipedLines(c));
931 
932 		c.setSection("", l("#C3", "#C4"), m);
933 		assertEquals("#C3|#C4||a = b|", pipedLines(c));
934 
935 		c.setSection("S1", l("", "#C5", "#C6"), m);
936 		assertEquals("#C3|#C4||a = b||#C5|#C6|[S1]|a = b|", pipedLines(c));
937 
938 		c.setSection("S1", null, m);
939 		assertEquals("#C3|#C4||a = b||#C5|#C6|[S1]|a = b|", pipedLines(c));
940 
941 		c.setSection("S1", Collections.<String>emptyList(), m);
942 		assertEquals("#C3|#C4||a = b|[S1]|a = b|", pipedLines(c));
943 
944 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'section' cannot be null.", ()->c.setSection(null, l("", "#C5", "#C6"), m));
945 	}
946 
947 	//====================================================================================================
948 	//	public Config removeSection(String name)
949 	//====================================================================================================
950 	@Test void removeSection() {
951 		var c = init("a=1", "[S]", "b=2", "c=", "[T]");
952 
953 		c.removeSection("S");
954 		c.removeSection("T");
955 
956 		assertEquals("a=1|", pipedLines(c));
957 
958 		c.removeSection("");
959 		assertEquals("", pipedLines(c));
960 	}
961 
962 	//====================================================================================================
963 	//	public Writer writeTo(Writer w)
964 	//====================================================================================================
965 	@Test void writeTo() throws Exception {
966 		var c = init("a=1", "[S]", "b=2", "c=", "[T]");
967 
968 		assertEquals("a=1|[S]|b=2|c=|[T]|", pipedLines(c.writeTo(new StringWriter())));
969 	}
970 
971 	//====================================================================================================
972 	// testExampleInConfig - Example in Config
973 	//====================================================================================================
974 	@Test void a01_exampleInConfig() throws Exception {
975 
976 		var cf = init(
977 			"# Default section", "key1 = 1", "key2 = true", "key3 = [1,2,3]", "key4 = http://foo", "",
978 			"# section1", "# Section 1",
979 			"[section1]", "key1 = 2", "key2 = false", "key3 = [4,5,6]", "key4 = http://bar"
980 		);
981 
982 		assertEquals(1, cf.get("key1").asInteger().get());
983 		assertTrue(cf.get("key2").asBoolean().get());
984 		assertEquals(3, cf.get("key3").as(int[].class).get()[2]);
985 		assertEquals(6, cf.get("xkey3").as(int[].class).orElse(ints(4,5,6))[2]);
986 		assertEquals(6, cf.get("X/key3").as(int[].class).orElse(ints(4,5,6))[2]);
987 		assertEquals(url("http://foo").toString(), cf.get("key4").as(URL.class).get().toString());
988 
989 		assertEquals(2, cf.get("section1/key1").asInteger().get());
990 		assertFalse(cf.get("section1/key2").asBoolean().get());
991 		assertEquals(6, cf.get("section1/key3").as(int[].class).get()[2]);
992 		assertEquals(url("http://bar").toString(), cf.get("section1/key4").as(URL.class).get().toString());
993 
994 		cf = init(
995 			"# Default section",
996 			"[section1]", "# Section 1"
997 		);
998 
999 		cf.set("key1", 1);
1000 		cf.set("key2", true);
1001 		cf.set("key3", ints(1,2,3));
1002 		cf.set("key4", url("http://foo"));
1003 		cf.set("section1/key1", 2);
1004 		cf.set("section1/key2", false);
1005 		cf.set("section1/key3", ints(4,5,6));
1006 		cf.set("section1/key4", url("http://bar"));
1007 
1008 		cf.commit();
1009 
1010 		assertEquals(1, cf.get("key1").asInteger().get());
1011 		assertTrue(cf.get("key2").asBoolean().get());
1012 		assertEquals(3, cf.get("key3").as(int[].class).get()[2]);
1013 		assertEquals(url("http://foo").toString(), cf.get("key4").as(URL.class).get().toString());
1014 
1015 		assertEquals(2, cf.get("section1/key1").asInteger().get());
1016 		assertFalse(cf.get("section1/key2").asBoolean().get());
1017 		assertEquals(6, cf.get("section1/key3").as(int[].class).get()[2]);
1018 		assertEquals(url("http://bar").toString(), cf.get("section1/key4").as(URL.class).get().toString());
1019 	}
1020 
1021 	//====================================================================================================
1022 	// testEnum
1023 	//====================================================================================================
1024 	@Test void a02_enum() throws Exception {
1025 		var cf = init(
1026 			"key1 = MINUTES"
1027 		);
1028 		assertEquals(TimeUnit.MINUTES, cf.get("key1").as(TimeUnit.class).get());
1029 
1030 		cf.commit();
1031 
1032 		assertEquals(TimeUnit.MINUTES, cf.get("key1").as(TimeUnit.class).get());
1033 	}
1034 
1035 	//====================================================================================================
1036 	// testEncodedValues
1037 	//====================================================================================================
1038 	@Test void a03_encodedValues() throws Exception {
1039 		var cf = init(
1040 			"[s1]", "", "foo<*> = "
1041 		);
1042 
1043 		cf.set("s1/foo", "mypassword");
1044 
1045 		assertEquals("mypassword", cf.get("s1/foo").get());
1046 
1047 		cf.commit();
1048 
1049 		assertEquals("[s1]||foo<*> = {AwwJVhwUQFZEMg==}|", pipedLines(cf));
1050 
1051 		assertEquals("mypassword", cf.get("s1/foo").get());
1052 
1053 		cf.load(reader("[s1]\nfoo<*> = mypassword2\n"), true);
1054 
1055 		assertEquals("mypassword2", cf.get("s1/foo").get());
1056 
1057 		cf.set("s1/foo", "mypassword");
1058 
1059 		// INI output should be encoded
1060 		var sw = new StringWriter();
1061 		cf.writeTo(new PrintWriter(sw));
1062 		assertEquals("[s1]|foo<*> = {AwwJVhwUQFZEMg==}|", pipedLines(sw));
1063 	}
1064 
1065 	//====================================================================================================
1066 	// public Config encodeEntries()
1067 	//====================================================================================================
1068 	@Test void a04_encodeEntries() throws Exception {
1069 		var cf = init(
1070 			"[s1]", "", "foo<*> = mypassword"
1071 		);
1072 
1073 		cf.applyMods();
1074 		cf.commit();
1075 		assertEquals("[s1]||foo<*> = {AwwJVhwUQFZEMg==}|", pipedLines(MemoryStore.DEFAULT.read("Test.cfg")));
1076 	}
1077 
1078 	//====================================================================================================
1079 	// testVariables
1080 	//====================================================================================================
1081 	@Test void a05_variables() {
1082 
1083 		var cf = init(
1084 			"[s1]",
1085 			"f1 = $S{foo}",
1086 			"f2 = $S{foo,bar}",
1087 			"f3 = $S{$S{baz,bing},bar}"
1088 		);
1089 
1090 		System.getProperties().remove("foo");
1091 		System.getProperties().remove("bar");
1092 		System.getProperties().remove("baz");
1093 		System.getProperties().remove("bing");
1094 
1095 		assertEquals("", cf.get("s1/f1").get());
1096 		assertEquals("bar", cf.get("s1/f2").get());
1097 		assertEquals("bar", cf.get("s1/f3").get());
1098 
1099 		System.setProperty("foo", "123");
1100 		assertEquals("123", cf.get("s1/f1").get());
1101 		assertEquals("123", cf.get("s1/f2").get());
1102 		assertEquals("bar", cf.get("s1/f3").get());
1103 
1104 		System.setProperty("foo", "$S{bar}");
1105 		System.setProperty("bar", "baz");
1106 		assertEquals("baz", cf.get("s1/f1").get());
1107 		assertEquals("baz", cf.get("s1/f2").get());
1108 		assertEquals("bar", cf.get("s1/f3").get());
1109 
1110 		System.setProperty("bing", "$S{foo}");
1111 		assertEquals("baz", cf.get("s1/f3").get());
1112 
1113 		System.setProperty("baz", "foo");
1114 		System.setProperty("foo", "123");
1115 		assertEquals("123", cf.get("s1/f3").get());
1116 	}
1117 
1118 	//====================================================================================================
1119 	// testXorEncoder
1120 	//====================================================================================================
1121 	@Test void a06_xorEncoder() {
1122 		testXor("foo");
1123 		testXor("");
1124 		testXor("123");
1125 		testXor("€");  // 3-byte UTF-8 character
1126 		testXor("𤭢"); // 4-byte UTF-8 character
1127 	}
1128 
1129 	private static void testXor(String in) {
1130 		var e = new XorEncodeMod();
1131 		var s = e.apply(in);
1132 		var s2 = e.remove(s);
1133 		assertEquals(in, s2);
1134 	}
1135 
1136 	//====================================================================================================
1137 	// testHex
1138 	//====================================================================================================
1139 	@Test void a07_hex() throws Exception {
1140 		var cf = init().copy().binaryFormat(BinaryFormat.HEX).build();
1141 
1142 		cf.set("foo", "bar".getBytes("UTF-8"));
1143 		assertEquals("626172", cf.get("foo").getValue());
1144 		assertJson("[98,97,114]", cf.get("foo").asBytes().get());
1145 	}
1146 
1147 	//====================================================================================================
1148 	// testSpacedHex
1149 	//====================================================================================================
1150 	@Test void a08_spacedHex() throws Exception {
1151 		var cf = init().copy().binaryFormat(BinaryFormat.SPACED_HEX).build();
1152 
1153 		cf.set("foo", "bar".getBytes("UTF-8"));
1154 		assertEquals("62 61 72", cf.get("foo").getValue());
1155 		assertJson("[98,97,114]", cf.get("foo").asBytes().get());
1156 	}
1157 
1158 	//====================================================================================================
1159 	// testMultiLines
1160 	//====================================================================================================
1161 	@Test void a09_multiLines() throws Exception {
1162 		var cf = init(
1163 			"[s1]",
1164 			"f1 = x \n\ty \n\tz"
1165 		);
1166 
1167 		assertEquals("x \ny \nz", cf.get("s1/f1").get());
1168 
1169 		var sw = new StringWriter();
1170 		cf.writeTo(sw);
1171 		assertEquals("[s1]|f1 = x |\ty |\tz|", pipedLines(sw));
1172 	}
1173 
1174 	//====================================================================================================
1175 	// testNumberShortcuts
1176 	//====================================================================================================
1177 	@Test void a10_numberShortcuts() {
1178 		var cf = init(
1179 			"[s1]",
1180 			"f1 = 1M",
1181 			"f2 = 1K",
1182 			"f3 = 1 M",
1183 			"f4 = 1 K"
1184 		);
1185 		assertEquals(1048576, cf.get("s1/f1").asInteger().get());
1186 		assertEquals(1024, cf.get("s1/f2").asInteger().get());
1187 		assertEquals(1048576, cf.get("s1/f3").asInteger().get());
1188 		assertEquals(1024, cf.get("s1/f4").asInteger().get());
1189 	}
1190 
1191 	//====================================================================================================
1192 	// testListeners
1193 	//====================================================================================================
1194 	@Test void a11_listeners() throws Exception {
1195 		var cf = init();
1196 
1197 		final var changes = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
1198 
1199 		cf.addListener(events -> {
1200 			for (var ce : events) {
1201 				var key = (ce.getSection().isEmpty() ? "" : (ce.getSection() + '/')) + ce.getKey();
1202 				if (ce.getType() == ConfigEventType.REMOVE_ENTRY) {
1203 					changes.add("REMOVE_ENTRY(" + key + ")");
1204 				} else if (ce.getType() == ConfigEventType.REMOVE_SECTION) {
1205 					changes.add("REMOVE_SECTION(" + ce.getSection() + ")");
1206 				} else if (ce.getType() == ConfigEventType.SET_SECTION) {
1207 					changes.add("SET_SECTION(" + ce.getSection() + ")");
1208 				} else {
1209 					changes.add(key + '=' + ce.getValue());
1210 				}
1211 			}
1212 		});
1213 
1214 		// No changes until save.
1215 		changes.clear();
1216 		cf.set("a1", 3);
1217 		cf.set("a3", 3);
1218 		cf.set("B/b1", 3);
1219 		cf.set("B/b3", 3);
1220 		assertJson("[]", changes);
1221 		cf.commit();
1222 		assertJson("['a1=3','a3=3','B/b1=3','B/b3=3']", changes);
1223 
1224 		// Rollback.
1225 		changes.clear();
1226 		cf.set("a1", 3);
1227 		cf.set("a3", 3);
1228 		cf.set("B/b1", 3);
1229 		cf.set("B/b3", 3);
1230 		assertJson("[]", changes);
1231 		cf.rollback();
1232 		cf.commit();
1233 		assertJson("[]", changes);
1234 
1235 		// Overwrite
1236 		changes.clear();
1237 		cf.set("a1", "2");
1238 		cf.set("B/b1", "2");
1239 		cf.set("a2", "2");
1240 		cf.set("B/b2", "2");
1241 		cf.set("C/c1", "2");
1242 		cf.set("C/c2", "2");
1243 		cf.commit();
1244 		assertJson("['a1=2','a2=2','B/b1=2','B/b2=2','C/c1=2','C/c2=2']", changes);
1245 
1246 		// Encoded
1247 		changes.clear();
1248 		cf.set("a4", "4", null, "*", null, null);
1249 		cf.set("B/b4", "4", null, "*", null, null);
1250 		cf.commit();
1251 		assertJson("['a4={Wg==}','B/b4={Wg==}']", changes);
1252 
1253 		// Encoded overwrite
1254 		changes.clear();
1255 		cf.set("a4", "5");
1256 		cf.set("B/b4", "5");
1257 		cf.commit();
1258 		assertJson("['a4={Ww==}','B/b4={Ww==}']", changes);
1259 
1260 		// Remove entries
1261 		changes.clear();
1262 		cf.remove("a4");
1263 		cf.remove("ax");
1264 		cf.remove("B/b4");
1265 		cf.remove("B/bx");
1266 		cf.remove("X/bx");
1267 		cf.commit();
1268 		assertJson("['REMOVE_ENTRY(a4)','REMOVE_ENTRY(B/b4)']", changes);
1269 
1270 		// Add section
1271 		// Shouldn't trigger listener.
1272 		changes.clear();
1273 		cf.setSection("D", l("#comment"));
1274 		cf.commit();
1275 		assertJson("[]", changes);
1276 
1277 		// Add section with contents
1278 		changes.clear();
1279 		cf.setSection("E", null, m("e1","1","e2","2"));
1280 		cf.commit();
1281 		assertJson("['E/e1=1','E/e2=2']", changes);
1282 
1283 		// Remove section
1284 		changes.clear();
1285 		cf.removeSection("E");
1286 		cf.commit();
1287 		assertJson("['REMOVE_ENTRY(E/e1)','REMOVE_ENTRY(E/e2)']", changes);
1288 
1289 		// Remove non-existent section
1290 		changes.clear();
1291 		cf.removeSection("E");
1292 		cf.commit();
1293 		assertJson("[]", changes);
1294 	}
1295 
1296 	//====================================================================================================
1297 	// getObjectArray(Class c, String key)
1298 	// getObjectArray(Class c, String key, T[] def)
1299 	//====================================================================================================
1300 	@Test void a12_getObjectArray() {
1301 		var cf = init("[A]", "a1=[1,2,3]");
1302 		assertJson("[1,2,3]", cf.get("A/a1").as(Integer[].class).get());
1303 		assertJson("[4,5,6]", cf.get("A/a2").as(Integer[].class).orElse(a(4,5,6)));
1304 		assertJson("[7,8,9]", cf.get("B/a1").as(Integer[].class).orElse(a(7,8,9)));
1305 		assertFalse(cf.get("B/a1").as(Integer[].class).isPresent());
1306 
1307 		cf = init("[A]", "a1 = [1 ,\n\t2 ,\n\t3] ");
1308 		assertJson("[1,2,3]", cf.get("A/a1").as(Integer[].class).get());
1309 
1310 		// We cannot cast primitive arrays to Object[], so the following throws exceptions.
1311 		assertJson("[1,2,3]", cf.get("A/a1").as(int[].class).get());
1312 		assertEquals("int", cf.get("A/a1").as(int[].class).get().getClass().getComponentType().getSimpleName());
1313 		assertFalse(cf.get("B/a1").as(int[].class).isPresent());
1314 		assertEquals("int", cf.get("B/a1").as(int[].class).orElse(new int[0]).getClass().getComponentType().getSimpleName());
1315 		assertFalse(cf.get("A/a2").as(int[].class).isPresent());
1316 		assertEquals("int", cf.get("A/a2").as(int[].class).orElse(new int[0]).getClass().getComponentType().getSimpleName());
1317 
1318 		assertJson("[1,2,3]", cf.get("A/a1").as(int[].class).orElse(ints(4)));
1319 		assertEquals("int", cf.get("A/a1").as(int[].class).orElse(ints(4)).getClass().getComponentType().getSimpleName());
1320 		assertJson("[4]", cf.get("B/a1").as(int[].class).orElse(ints(4)));
1321 		assertEquals("int", cf.get("B/a1").as(int[].class).orElse(ints(4)).getClass().getComponentType().getSimpleName());
1322 		assertJson("[4]", cf.get("A/a2").as(int[].class).orElse(ints(4)));
1323 		assertEquals("int", cf.get("A/a2").as(int[].class).orElse(ints(4)).getClass().getComponentType().getSimpleName());
1324 
1325 		System.setProperty("X", "[4,5,6]");
1326 		cf = init("x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "[A]", "a1=[1,2,3]");
1327 		assertJson("[1,2,3]", cf.get("x1").as(int[].class).orElse(ints(9)));
1328 		assertJson("[4,5,6]", cf.get("x2").as(int[].class).orElse(ints(9)));
1329 		assertJson("[]", cf.get("x3").as(int[].class).orElse(ints(9)));
1330 		System.clearProperty("X");
1331 	}
1332 
1333 	//====================================================================================================
1334 	// getStringArray(String key)
1335 	// getStringArray(String key, String[] def)
1336 	//====================================================================================================
1337 	@Test void a13_getStringArray() {
1338 		var cf = init("[A]", "a1=1,2,3");
1339 		assertJson("['1','2','3']", cf.get("A/a1").asStringArray().get());
1340 		assertJson("['4','5','6']", cf.get("A/a2").asStringArray().orElse(a("4","5","6")));
1341 		assertJson("['7','8','9']", cf.get("B/a1").asStringArray().orElse(a("7","8","9")));
1342 		assertNull(cf.get("B/a1").asStringArray().orElse(null));
1343 
1344 		cf = init("[A]", "a1 = 1 ,\n\t2 ,\n\t3 ");
1345 		assertJson("['1','2','3']", cf.get("A/a1").asStringArray().get());
1346 
1347 		System.setProperty("X", "4,5,6");
1348 		cf = init(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}", "x4=$S{Y,$S{X}}", "[A]", "a1=1,2,3");
1349 		assertJson("['1','2','3']", cf.get("x1").asStringArray().orElse(a("9")));
1350 		assertJson("['4','5','6']", cf.get("x2").asStringArray().orElse(a("9")));
1351 		assertJson("['9']", cf.get("x9").asStringArray().orElse(a("9")));
1352 
1353 		System.clearProperty("X");
1354 	}
1355 
1356 	//====================================================================================================
1357 	// getSectionMap(String name)
1358 	//====================================================================================================
1359 	@Test void a14_getSectionMap() {
1360 		var cf = init("[A]", "a1=1", "", "[D]", "d1=$C{A/a1}","d2=$S{X}");
1361 
1362 		assertJson("{a1:'1'}", cf.getSection("A").asMap().get());
1363 		assertFalse(cf.getSection("B").asMap().isPresent());
1364 		assertJson("null", cf.getSection("C").asMap().orElse(null));
1365 
1366 		var m = cf.getSection("A").asMap().get();
1367 		assertJson("{a1:'1'}", m);
1368 
1369 		System.setProperty("X", "x");
1370 		m = cf.getSection("D").asMap().get();
1371 		assertJson("{d1:'1',d2:'x'}", m);
1372 		System.clearProperty("X");
1373 	}
1374 
1375 	//====================================================================================================
1376 	// toWritable()
1377 	//====================================================================================================
1378 	@Test void a15_toWritable() throws Exception {
1379 		var cf = init("a = b");
1380 
1381 		var sw = new StringWriter();
1382 		cf.writeTo(sw);
1383 		assertEquals("a = b|", pipedLines(sw));
1384 	}
1385 
1386 	//====================================================================================================
1387 	// Test resolving with override
1388 	//====================================================================================================
1389 	@Test void a16_resolvingWithOverride() {
1390 		var cf = init();
1391 		cf.set("a", "$A{X}");
1392 		cf.set("b", "$B{X}");
1393 		cf.set("c", "$A{$B{X}}");
1394 		cf.set("d", "$B{$A{X}}");
1395 		cf.set("e", "$D{X}");
1396 
1397 		var vr = VarResolver.create().defaultVars().vars(ALVar.class, BLVar.class).build();
1398 
1399 		cf = cf.resolving(vr.createSession());
1400 
1401 		assertEquals("aXa", cf.get("a").get());
1402 		assertEquals("bXb", cf.get("b").get());
1403 		assertEquals("abXba", cf.get("c").get());
1404 		assertEquals("baXab", cf.get("d").get());
1405 		assertEquals("$D{X}", cf.get("e").get());
1406 
1407 		// Create new resolver that addx $C and overrides $A
1408 		var vr2 = vr.copy().vars(AUVar.class, DUVar.class).build();
1409 
1410 		// true == augment by adding existing as parent to the new resolver
1411 		cf = cf.resolving(vr2.createSession());
1412 		assertEquals("AXA", cf.get("a").get());
1413 		assertEquals("bXb", cf.get("b").get());
1414 		assertEquals("AbXbA", cf.get("c").get());
1415 		assertEquals("bAXAb", cf.get("d").get());
1416 		assertEquals("DXD", cf.get("e").get());
1417 	}
1418 
1419 	public static class ALVar extends SimpleVar {
1420 		public ALVar() {
1421 			super("A");
1422 		}
1423 		@Override
1424 		public String resolve(VarResolverSession session, String key) {
1425 			return 'a' + key + 'a';
1426 		}
1427 	}
1428 
1429 	public static class AUVar extends SimpleVar {
1430 		public AUVar() {
1431 			super("A");
1432 		}
1433 		@Override
1434 		public String resolve(VarResolverSession session, String key) {
1435 			return 'A' + key + 'A';
1436 		}
1437 	}
1438 
1439 	public static class BLVar extends SimpleVar {
1440 		public BLVar() {
1441 			super("B");
1442 		}
1443 		@Override
1444 		public String resolve(VarResolverSession session, String key) {
1445 			return 'b' + key + 'b';
1446 		}
1447 	}
1448 
1449 	public static class DUVar extends SimpleVar {
1450 		public DUVar() {
1451 			super("D");
1452 		}
1453 		@Override
1454 		public String resolve(VarResolverSession session, String key) {
1455 			return 'D' + key + 'D';
1456 		}
1457 	}
1458 
1459 	//====================================================================================================
1460 	// Ensure pound signs in values are encoded.
1461 	//====================================================================================================
1462 	@Test void a17_poundSignEscape() throws Exception {
1463 		var cf = init();
1464 		cf.set("a", "a,#b,=c");
1465 		cf.set("A/a", "a,#b,=c");
1466 
1467 		assertEquals("a = a,\\u0023b,=c|[A]|a = a,\\u0023b,=c|", pipedLines(cf));
1468 		cf.commit();
1469 		assertEquals("a = a,\\u0023b,=c|[A]|a = a,\\u0023b,=c|", pipedLines(cf));
1470 
1471 		assertEquals("a,#b,=c", cf.get("a").get());
1472 		assertEquals("a,#b,=c", cf.get("A/a").get());
1473 
1474 		cf.set("a", "a,#b,=c", null, null, "comment#comment", null);
1475 		assertEquals("a = a,\\u0023b,=c # comment#comment|[A]|a = a,\\u0023b,=c|", pipedLines(cf));
1476 		assertEquals("a,#b,=c", cf.get("a").get());
1477 	}
1478 
1479 	public static class ABean {
1480 		public String foo;
1481 
1482 		public ABean init() {
1483 			foo = "bar";
1484 			return this;
1485 		}
1486 	}
1487 
1488 	public static class BBean {
1489 		private String foo;
1490 
1491 		public String getFoo() { return foo; }
1492 		public BBean setFoo(String v) {this.foo = v; return this;}
1493 		public BBean init() {
1494 			foo = "bar";
1495 			return this;
1496 		}
1497 	}
1498 
1499 	@Test void a18_getCandidateSystemDefaultConfigNames() {
1500 
1501 		System.setProperty("juneau.configFile", "foo.txt");
1502 		assertJson("['foo.txt']", Config.getCandidateSystemDefaultConfigNames());
1503 
1504 		System.clearProperty("juneau.configFile");
1505 		assertJson("['test.cfg','juneau.cfg','default.cfg','application.cfg','app.cfg','settings.cfg','application.properties']", Config.getCandidateSystemDefaultConfigNames());
1506 	}
1507 
1508 	//====================================================================================================
1509 	//	setSystemProperties
1510 	//====================================================================================================
1511 
1512 	@Test void setSystemProperties() {
1513 		var c = init("a=1", "[S]", "b=2");
1514 		c.setSystemProperties();
1515 		assertEquals("1", System.getProperty("a"));
1516 		assertEquals("2", System.getProperty("S/b"));
1517 	}
1518 }