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.objecttools;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.commons.utils.CollectionUtils.*;
21  import static org.apache.juneau.commons.utils.StringUtils.*;
22  import static org.apache.juneau.junit.bct.BctAssertions.*;
23  
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.commons.time.*;
28  import org.apache.juneau.commons.utils.*;
29  import org.apache.juneau.json.*;
30  import org.apache.juneau.serializer.*;
31  import org.apache.juneau.swaps.*;
32  import org.junit.jupiter.api.*;
33  
34  /**
35   * Tests the PojoSearcher class.
36   */
37  public class ObjectSearcher_Test extends TestBase {
38  
39  	private static BeanSession bs = BeanContext.DEFAULT_SESSION;
40  	private static ObjectSearcher os = ObjectSearcher.DEFAULT;
41  	private static WriterSerializer ws = JsonSerializer.create().json5().swaps(TemporalCalendarSwap.IsoLocalDateTime.class).build();
42  
43  	//-----------------------------------------------------------------------------------------------------------------
44  	// Utility
45  	//-----------------------------------------------------------------------------------------------------------------
46  
47  	static SearchArgs[] create(String...search) {
48  		var sa = new SearchArgs[search.length];
49  		for (var i = 0; i < search.length; i++)
50  			sa[i] = SearchArgs.create(search[i]);
51  		return sa;
52  	}
53  
54  	static SearchArgs create(String search) {
55  		return SearchArgs.create(search);
56  	}
57  
58  	static Object run(Object in, String search) {
59  		return os.run(bs, in, create(search));
60  	}
61  
62  	static Object run(Object in, SearchArgs sa) {
63  		return os.run(bs, in, sa);
64  	}
65  
66  	//-----------------------------------------------------------------------------------------------------------------
67  	// String search
68  	//-----------------------------------------------------------------------------------------------------------------
69  
70  	public static class A {
71  		public String f;
72  
73  		public static A create(String f) {
74  			var a = new A();
75  			a.f = f;
76  			return a;
77  		}
78  	}
79  
80  	public static List<A> A_LIST = l(A.create("foo"), A.create("bar"), A.create("baz"), A.create("q ux"), A.create("qu'ux"), null, A.create(null));
81  	public static Set<A> A_SET = set(A.create("foo"), A.create("bar"), A.create("baz"), A.create("q ux"), A.create("qu'ux"), null, A.create(null));
82  	public static A[] A_ARRAY = {A.create("foo"), A.create("bar"), A.create("baz"), A.create("q ux"), A.create("qu'ux"), null, A.create(null)};
83  
84  	@Test void a01_stringSearch_singleWord() {
85  		assertBeans(run(A_LIST, "f=foo"), "f", "foo");
86  		assertBeans(run(A_SET, "f=foo"), "f", "foo");
87  		assertBeans(run(A_ARRAY, "f=foo"), "f", "foo");
88  		assertBeans(os.run(A_LIST, "f=foo"), "f", "foo");
89  		assertBeans(os.run(A_SET, "f=foo"), "f", "foo");
90  		assertBeans(os.run(A_ARRAY, "f=foo"), "f", "foo");
91  	}
92  
93  	@Test void a02_stringSearch_pattern1() {
94  		assertBeans(run(A_LIST, "f=fo*"), "f", "foo");
95  		assertBeans(run(A_SET, "f=fo*"), "f", "foo");
96  		assertBeans(run(A_ARRAY, "f=fo*"), "f", "foo");
97  	}
98  
99  	@Test void a03_stringSearch_pattern2() {
100 		assertBeans(run(A_LIST, "f=*ar"), "f", "bar");
101 		assertBeans(run(A_SET, "f=*ar"), "f", "bar");
102 		assertBeans(run(A_ARRAY, "f=*ar"), "f", "bar");
103 	}
104 
105 	@Test void a04_stringSearch_pattern3() {
106 		assertBeans(run(A_LIST, "f=?ar"), "f", "bar");
107 		assertBeans(run(A_SET, "f=?ar"), "f", "bar");
108 		assertBeans(run(A_ARRAY, "f=?ar"), "f", "bar");
109 	}
110 
111 	@Test void a05_stringSearch_multiple() {
112 		assertBeans(run(A_LIST, "f=foo bar q ux"), "f", "foo", "bar");
113 		assertBeans(run(A_SET, "f=foo bar q ux"), "f", "foo", "bar");
114 		assertBeans(run(A_ARRAY, "f=foo bar q ux"), "f", "foo", "bar");
115 	}
116 
117 	@Test void a06_stringSearch_quoted() {
118 		assertBeans(run(A_LIST, "f='q ux'"), "f", "q ux");
119 		assertBeans(run(A_SET, "f='q ux'"), "f", "q ux");
120 		assertBeans(run(A_ARRAY, "f='q ux'"), "f", "q ux");
121 	}
122 
123 	@Test void a07_stringSearch_quotedWithPattern() {
124 		assertBeans(run(A_LIST, "f='q *x'"), "f", "q ux");
125 		assertBeans(run(A_SET, "f='q *x'"), "f", "q ux");
126 		assertBeans(run(A_ARRAY, "f='q *x'"), "f", "q ux");
127 	}
128 
129 	@Test void a08_stringSearch_unquotedContainingQuote() {
130 		assertBeans(run(A_LIST, "f=qu'ux"), "f", "qu'ux");
131 		assertBeans(run(A_SET, "f=qu'ux"), "f", "qu'ux");
132 		assertBeans(run(A_ARRAY, "f=qu'ux"), "f", "qu'ux");
133 	}
134 
135 	@Test void a09_stringSearch_quotedContainingQuote() {
136 		assertBeans(run(A_LIST, "f='qu\\'ux'"), "f", "qu'ux");
137 		assertBeans(run(A_SET, "f='qu\\'ux'"), "f", "qu'ux");
138 		assertBeans(run(A_ARRAY, "f='qu\\'ux'"), "f", "qu'ux");
139 	}
140 
141 	@Test void a10_stringSearch_regExp() {
142 		assertBeans(run(A_LIST, "f=/q\\sux/"), "f", "q ux");
143 		assertBeans(run(A_SET, "f=/q\\sux/"), "f", "q ux");
144 		assertBeans(run(A_ARRAY, "f=/q\\sux/"), "f", "q ux");
145 	}
146 
147 	@Test void a11_stringSearch_regExp_noEndSlash() {
148 		var in = l(A.create("/foo"), A.create("bar"));
149 		for (var s : a("f=/foo","f='/foo'"))
150 			assertBeans(run(in, s), "f", "/foo");
151 	}
152 
153 	@Test void a12_stringSearch_regExp_onlySlash() {
154 		var in = l(A.create("/"), A.create("bar"));
155 		for (var s : a("f=/", "f='/'"))
156 			assertBeans(run(in, s), "f", "/");
157 	}
158 
159 	@Test void a13_stringSearch_or_pattern() {
160 		var in = l(A.create("foo"), A.create("bar"), A.create("baz"));
161 		assertBeans(run(in, "f=f* *r"), "f", "foo", "bar");
162 		assertEmpty(run(in, "f='f* *r'"));
163 		assertBeans(run(in, "f='f*oo'"), "f", "foo");
164 	}
165 
166 	@Test void a14_stringSearch_explicit_or_pattern() {
167 		var in = l(A.create("foo"), A.create("bar"), A.create("baz"));
168 		assertBeans(run(in, "f=^f* ^*r"), "f", "foo", "bar");
169 		assertEmpty(run(in, "f=^'f* *r'"));
170 		assertBeans(run(in, "f=^'f*oo'"), "f", "foo");
171 	}
172 
173 	@Test void a15_stringSearch_and_pattern() {
174 		var in = l(A.create("foo"), A.create("bar"), A.create("baz"));
175 		assertBeans(run(in, "f=+b* +*r"), "f", "bar");
176 		assertBeans(run(in, "f=+'b*' +'*r'"), "f", "bar");
177 	}
178 
179 	@Test void a16_stringSearch_not_pattern() {
180 		var in = l(A.create("foo"), A.create("bar"), A.create("baz"));
181 		assertBeans(run(in, "f=b* -*r"), "f", "baz");
182 		assertBeans(run(in, "f=+'b*' -'*r'"), "f", "baz");
183 	}
184 
185 	@Test void a17_stringSearch_caseSensitive() {
186 		var in = l(A.create("foo"), A.create("bar"), A.create("baz"));
187 		assertEmpty(run(in, "f=F*"));
188 		assertEmpty(run(in, "f=\"F*\""));
189 		assertBeans(run(in, "f='F*'"), "f", "foo");
190 	}
191 
192 	@Test void a18_stringSearch_malformedQuotes() {
193 		var in = l(A.create("'foo"), A.create("\"bar"), A.create("baz"));
194 
195 		assertThrowsWithMessage(Exception.class, "Unmatched string quotes", ()->run(in, "f='*"));
196 
197 		assertThrowsWithMessage(Exception.class, "Unmatched string quotes", ()->run(in, "f=\"*"));
198 
199 		assertBeans(run(in, "f='\\'*'"), "f", "'foo");
200 		assertBeans(run(in, "f='\"*'"), "f", "\"bar");
201 		assertBeans(run(in, "f=\"\\\"*\""), "f", "\"bar");
202 	}
203 
204 	@Test void a19_stringSearch_regexChars() {
205 		var in = l(A.create("+\\[]{}()^$."), A.create("bar"), A.create("baz"));
206 		assertBeans(run(in, "f=*+*"), "f", "+\\[]{}()^$.");
207 		assertBeans(run(in, "f='+\\\\[]{}()^$.'"), "f", "+\\[]{}()^$.");
208 		assertBeans(run(in, "f=++\\\\[]{}()^$."), "f", "+\\[]{}()^$.");
209 	}
210 
211 	@Test void a20_stringSearch_metaChars() {
212 		var in = l(A.create("*?\\'\""), A.create("bar"), A.create("baz"));
213 		assertBeans(run(in, "f='\\*\\?\\\\\\'\"'"), "f", "*?\\'\"");
214 	}
215 
216 	@Test void a21_stringSearch_metaChars_escapedQuotes() {
217 		var in = l(A.create("'"), A.create("\""), A.create("baz"));
218 		assertBeans(run(in, "f=\\'"), "f", "'");
219 		assertBeans(run(in, "f=\\\""), "f", "\"");
220 	}
221 
222 	@Test void a22_stringSearch_metaChars_falseEscape() {
223 		var in = l(A.create("foo"), A.create("bar"), A.create("baz"));
224 		assertBeans(run(in, "f=\\f\\o\\o"), "f", "foo");
225 	}
226 
227 	//-----------------------------------------------------------------------------------------------------------------
228 	// Number search
229 	//-----------------------------------------------------------------------------------------------------------------
230 
231 	public static class C {
232 		public int f;
233 
234 		static C create(int f) {
235 			var c = new C();
236 			c.f = f;
237 			return c;
238 		}
239 	}
240 
241 	C[] INT_BEAN_ARRAY = {C.create(-2), C.create(-1), C.create(0), C.create(1), C.create(2), C.create(3)};
242 
243 	@Test void b01_intSearch_oneNumber() {
244 		for (var s : a("f=1", "f = 1"))
245 			assertBeans(run(INT_BEAN_ARRAY, s), "f", "1");
246 	}
247 
248 	@Test void b02_intSearch_twoNumbers() {
249 		for (var s : a("f=1 2", "f = 1  2 "))
250 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("1,2"));
251 	}
252 
253 	@Test void b03_intSearch_oneNegativeNumber() {
254 		for (var s : a("f=-1", "f = -1 "))
255 			assertBeans(run(INT_BEAN_ARRAY, s), "f", "-1");
256 	}
257 
258 	@Test void b04_intSearch_twoNegativeNumbers() {
259 		assertBeans(run(INT_BEAN_ARRAY, "f=-1 -2"), "f", splita("-2,-1"));
260 	}
261 
262 	@Test void b05_intSearch_simpleRange() {
263 		for (var s : a("f=1-2", "f = 1 - 2 ", "f = 1- 2 "))
264 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("1,2"));
265 	}
266 
267 	@Test void b06_intSearch_simpleRange_invalid() {
268 		assertEmpty(run(INT_BEAN_ARRAY, "f=2-1"));
269 	}
270 
271 	@Test void b07_intSearch_twoNumbersThatLookLikeRange() {
272 		assertBeans(run(INT_BEAN_ARRAY, "f = 1 -2 "), "f", splita("-2,1"));
273 	}
274 
275 	@Test void b08_intSearch_rangeWithNegativeNumbers() {
276 		assertBeans(run(INT_BEAN_ARRAY, "f = -2--1 "), "f", splita("-2,-1"));
277 	}
278 
279 	@Test void b09_intSearch_rangeWithNegativeNumbers_invalidRange() {
280 		assertEmpty(run(INT_BEAN_ARRAY, "f = -1--2 "));
281 	}
282 
283 	@Test void b10_intSearch_multipleRanges() {
284 		assertBeans(run(INT_BEAN_ARRAY, "f = 0-1 3-4"), "f", splita("0,1,3"));
285 	}
286 
287 	@Test void b11_intSearch_overlappingRanges() {
288 		assertBeans(run(INT_BEAN_ARRAY, "f = 0-0 2-2"), "f", splita("0,2"));
289 	}
290 
291 	@Test void b12_intSearch_LT() {
292 		for (var s : a("f = <0", "f<0", "f = < 0 ", "f < 0 "))
293 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("-2,-1"));
294 	}
295 
296 	@Test void b13_intSearch_LT_negativeNumber() {
297 		for (var s : a("f = <-1", "f<-1", "f = < -1 ", "f < -1 "))
298 			assertBeans(run(INT_BEAN_ARRAY, s), "f", "-2");
299 	}
300 
301 	@Test void b14_intSearch_GT() {
302 		for (var s : a("f = >1", "f>1", "f = > 1 ", "f > 1 "))
303 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("2,3"));
304 	}
305 
306 	@Test void b15_intSearch_GT_negativeNumber() {
307 		for (var s : a("f = >-1", "f>-1", "f = > -1 ", "f > -1 ", "f =  >  -1  ", "f >  -1  "))
308 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("0,1,2,3"));
309 	}
310 
311 	@Test void b16_intSearch_LTE() {
312 		for (var s : a("f = <=0", "f<=0", "f = <= 0 ", "f <= 0 ", "f =  <=  0  "))
313 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("-2,-1,0"));
314 	}
315 
316 	@Test void b17_intSearch_LTE_negativeNumber() {
317 		for (var s : a("f = <=-1", "f <=-1", "f = <= -1 ", "f =  <=  -1  ", "f <=  -1  "))
318 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("-2,-1"));
319 	}
320 
321 	@Test void b18_intSearch_GTE() {
322 		for (var s : a("f = >=1", "f >=1", "f = >= 1 ", "f >= 1 ", "f =  >=  1  "))
323 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("1,2,3"));
324 	}
325 
326 	@Test void b19_intSearch_GTE_negativeNumber() {
327 		for (var s : a("f = >=-1", "f >=-1", "f = >= -1 ", "f >= -1 ", "f =  >=  -1  "))
328 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("-1,0,1,2,3"));
329 	}
330 
331 	@Test void b20_intSearch_not_singleNumber() {
332 		for (var s : a("f = !1", "f = ! 1 ", "f =  !  1  "))
333 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("-2,-1,0,2,3"));
334 	}
335 
336 	@Test void b21_intSearch_not_range() {
337 		assertBeans(run(INT_BEAN_ARRAY, "f = !1-2"), "f", splita("-2,-1,0,3"));
338 	}
339 
340 	@Test void b22_intSearch_not_range_negativeNumbers() {
341 		for (var s : a("f = !-2--1", "f = ! -2 - -1", "f =  !  -2  -  -1 "))
342 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("0,1,2,3"));
343 	}
344 
345 	@Test void b23_intSearch_not_looksLikeRange() {
346 		assertBeans(run(INT_BEAN_ARRAY, "f = ! -2 -2"), "f", splita("-2,-1,0,1,2,3"));
347 	}
348 
349 	@Test void b24_intSearch_empty() {
350 		for (var s : a("f=", "f = ", "f =  "))
351 			assertBeans(run(INT_BEAN_ARRAY, s), "f", splita("-2,-1,0,1,2,3"));
352 	}
353 
354 	@Test void b25_intSearch_badSearches() {
355 		var ss = a(
356 			"f=x","(S1)",
357 			"f=>x","(S2)",
358 			"f=<x","(S3)",
359 			"f=>=x","(S4)",
360 			"f=>= x","(S5)",
361 			"f=1x","(S6)",
362 			"f=1 x","(S7)",
363 			"f=1-x","(S8)",
364 			"f=1 -x","(S9)",
365 			"f=1 - x","(S10)",
366 			"f=1 - 1x","(S11)",
367 			"f=>","(ES2)",
368 			"f=<","(ES3)",
369 			"f=>=","(ES4)",
370 			"f=123-","(ES8)",
371 			"f=123 -","(ES9)"
372 		);
373 
374 		for (var i = 0; i < ss.length; i+=2) {
375 			final int i2 = i;
376 			assertThrowsWithMessage(Exception.class, ss[i+1], ()->run(INT_BEAN_ARRAY, ss[i2]));
377 		}
378 	}
379 
380 	//-----------------------------------------------------------------------------------------------------------------
381 	// Date search
382 	//-----------------------------------------------------------------------------------------------------------------
383 
384 	public static class B {
385 		public Calendar f;
386 
387 		static B[] create(String...dates) {
388 			var bb = new B[dates.length];
389 			for (var i = 0; i < dates.length; i++) {
390 				bb[i] = new B();
391 				bb[i].f = opt(dates[i]).filter(x1 -> ! isBlank(x1)).map(x -> GranularZonedDateTime.of(x).getZonedDateTime()).map(GregorianCalendar::from).orElse(null);
392 			}
393 			return bb;
394 		}
395 	}
396 
397 	@Test void c01_dateSearch_singleDate_y() {
398 		var in = B.create("2010-01-01", "2011-01-01", "2011-01-31", "2012-01-01");
399 		for (var s : a(
400 				"f=2011",
401 				"f = 2011 ",
402 				"f = '2011' ",
403 				"f = \"2011\" "
404 			))
405 			assertSerialized(run(in, s), ws, "[{f:'2011-01-01T00:00:00'},{f:'2011-01-31T00:00:00'}]");
406 	}
407 
408 	@Test void c02_dateSearch_singleDate_ym() {
409 		var in = B.create("2010-01-01", "2011-01-01", "2011-01-31", "2012-01-01");
410 		for (var s : a(
411 				"f=2011-01",
412 				"f = 2011-01 ",
413 				"f='2011-01'",
414 				"f=\"2011-01\""
415 			))
416 			assertSerialized(run(in, s), ws, "[{f:'2011-01-01T00:00:00'},{f:'2011-01-31T00:00:00'}]");
417 	}
418 
419 	@Test void c03_dateSearch_singleDate_ymd() {
420 		var in = B.create("2010-01-01", "2011-01-01", "2011-01-31", "2012-01-01");
421 		assertSerialized(run(in, "f=2011-01-01"), ws, "[{f:'2011-01-01T00:00:00'}]");
422 	}
423 
424 	@Test void c04_dateSearch_singleDate_ymdh() {
425 		var in = B.create("2011-01-01T11:15:59", "2011-01-01T12:00:00", "2011-01-01T12:59:59", "2011-01-01T13:00:00");
426 		assertSerialized(run(in, "f=2011-01-01T12"), ws, "[{f:'2011-01-01T12:00:00'},{f:'2011-01-01T12:59:59'}]");
427 	}
428 
429 	@Test void c05_dateSearch_singleDate_ymdhm() {
430 		var in = B.create("2011-01-01T12:29:59", "2011-01-01T12:30:00", "2011-01-01T12:30:59", "2011-01-01T12:31:00");
431 		assertSerialized(run(in, "f=2011-01-01T12:30"), ws, "[{f:'2011-01-01T12:30:00'},{f:'2011-01-01T12:30:59'}]");
432 	}
433 
434 	@Test void c06_dateSearch_singleDate_ymdhms() {
435 		var in = B.create("2011-01-01T12:30:29", "2011-01-01T12:30:30", "2011-01-01T12:30:31");
436 		assertSerialized(run(in, "f=2011-01-01T12:30:30"), ws, "[{f:'2011-01-01T12:30:30'}]");
437 	}
438 
439 	@Test void c07_dateSearch_openEndedRanges_y() {
440 		var in = B.create("2000-12-31", "2001-01-01");
441 		for (var s : a(
442 				"f>2000",
443 				"f > 2000 ",
444 				"f>'2000'",
445 				"f > '2000' ",
446 				"f>\"2000\"",
447 				"f > \"2000\" ",
448 				"f>=2001",
449 				"f >= 2001 ",
450 				"f>='2001'",
451 				"f >= '2001' ",
452 				"f>=\"2001\"",
453 				"f >= \"2001\" "
454 			))
455 			assertSerialized(run(in, s), ws, "[{f:'2001-01-01T00:00:00'}]");
456 		for (var s : a(
457 				"f<2001",
458 				"f < 2001 ",
459 				"f<'2001'",
460 				"f < '2001'",
461 				"f<\"2001\"",
462 				"f < \"2001\" ",
463 				"f<=2000",
464 				"f <= 2000 ",
465 				"f<='2000'",
466 				"f <= '2000'",
467 				"f<=\"2000\"",
468 				"f <= \"2000\" "
469 			))
470 			assertSerialized(run(in, s), ws, "[{f:'2000-12-31T00:00:00'}]");
471 	}
472 
473 	@Test void c08_dateSearch_openEndedRanges_toMinute() {
474 		var in = B.create("2011-01-01T12:29:59", "2011-01-01T12:30:00");
475 		assertSerialized(run(in, "f>=2011-01-01T12:30"), ws, "[{f:'2011-01-01T12:30:00'}]");
476 		assertSerialized(run(in, "f<2011-01-01T12:30"), ws, "[{f:'2011-01-01T12:29:59'}]");
477 	}
478 
479 	@Test void c09_dateSearch_openEndedRanges_toSecond() {
480 		var in = B.create("2011-01-01T12:30:59", "2011-01-01T12:31:00");
481 		assertSerialized(run(in, "f>2011-01-01T12:30"), ws, "[{f:'2011-01-01T12:31:00'}]");
482 		assertSerialized(run(in, "f<=2011-01-01T12:30"), ws, "[{f:'2011-01-01T12:30:59'}]");
483 	}
484 
485 	@Test void c10_dateSearch_closedRanges() {
486 		var in = B.create("2000-12-31T23:59:59", "2001-01-01T00:00:00", "2003-06-30T23:59:59", "2003-07-01T00:00:00");
487 
488 		for (var s : a(
489 				"f= 2001 - 2003-06-30 ",
490 				"f= 2001 - 2003-06-30",
491 				"f='2001'-'2003-06-30'",
492 				"f= '2001' - '2003-06-30' ",
493 				"f=\"2001\"-\"2003-06-30\"",
494 				"f= \"2001\" - \"2003-06-30\" ",
495 				"f=2001 -'2003-06-30'",
496 				"f= 2001 - '2003-06-30' ",
497 				"f=2001 -\"2003-06-30\"",
498 				"f= 2001 - \"2003-06-30\" "
499 			))
500 			assertSerialized(run(in, s), ws, "[{f:'2001-01-01T00:00:00'},{f:'2003-06-30T23:59:59'}]");
501 
502 		for (var s : a(
503 			"f= 2001 - 2003-06-30 2000",
504 			"f= 2001 - 2003-06-30 '2000'",
505 			"f= 2001 - 2003-06-30 \"2000\"",
506 			"f='2001'-'2003-06-30' 2000",
507 			"f='2001'-'2003-06-30' '2000'",
508 			"f='2001'-'2003-06-30' \"2000\"",
509 			"f= '2001' - '2003-06-30'  2000",
510 			"f= '2001' - '2003-06-30'  '2000'",
511 			"f= '2001' - '2003-06-30'  \"2000\"",
512 			"f=\"2001\"-\"2003-06-30\" 2000",
513 			"f=\"2001\"-\"2003-06-30\" '2000'",
514 			"f=\"2001\"-\"2003-06-30\" \"2000\"",
515 			"f= \"2001\" - \"2003-06-30\"  2000",
516 			"f= \"2001\" - \"2003-06-30\"  '2000'",
517 			"f= \"2001\" - \"2003-06-30\"  \"2000\"",
518 			"f= 2001 - '2003-06-30'  2000",
519 			"f= 2001 - '2003-06-30'  '2000'",
520 			"f= 2001 - '2003-06-30'  \"2000\"",
521 			"f= 2001 - \"2003-06-30\"  2000",
522 			"f= 2001 - \"2003-06-30\"  '2000'",
523 			"f= 2001 - \"2003-06-30\"  \"2000\""
524 		))
525 			assertSerialized(run(in, s), ws, "[{f:'2000-12-31T23:59:59'},{f:'2001-01-01T00:00:00'},{f:'2003-06-30T23:59:59'}]");
526 	}
527 
528 	@Test void c11_dateSearch_or1() {
529 		var in = B.create("2000-12-31", "2001-01-01", "2001-12-31", "2002-01-01");
530 		for (var s : a(
531 				"f=2001 2003 2005",
532 				"f= 2001  2003  2005 ",
533 				"f='2001' '2003' '2005'",
534 				"f= '2001'  '2003'  '2005' ",
535 				"f=\"2001\" \"2003\" \"2005\"",
536 				"f= \"2001\"  \"2003\"  \"2005\" "
537 			))
538 			assertSerialized(run(in, s), ws, "[{f:'2001-01-01T00:00:00'},{f:'2001-12-31T00:00:00'}]");
539 	}
540 
541 	@Test void c12_dateSearch_or2() {
542 		var in = B.create("2002-12-31", "2003-01-01", "2003-12-31", "2004-01-01");
543 		for (var s : a(
544 				"f=2001 2003 2005",
545 				"f= 2001  2003  2005 ",
546 				"f='2001' '2003' '2005'",
547 				"f= '2001'  '2003'  '2005' ",
548 				"f=\"2001\" \"2003\" \"2005\"",
549 				"f= \"2001\"  \"2003\"  \"2005\" "
550 			))
551 			assertSerialized(run(in, s), ws, "[{f:'2003-01-01T00:00:00'},{f:'2003-12-31T00:00:00'}]");
552 	}
553 
554 	@Test void c13_dateSearch_or3() {
555 		var in = B.create("2004-12-31", "2005-01-01", "2005-12-31", "2006-01-01");
556 		for (var s : a(
557 				"f=2001 2003 2005",
558 				"f= 2001  2003  2005 ",
559 				"f='2001' '2003' '2005'",
560 				"f= '2001'  '2003'  '2005' ",
561 				"f=\"2001\" \"2003\" \"2005\"",
562 				"f= \"2001\"  \"2003\"  \"2005\" "
563 			))
564 			assertSerialized(run(in, s), ws, "[{f:'2005-01-01T00:00:00'},{f:'2005-12-31T00:00:00'}]");
565 	}
566 
567 	@Test void c14_dateSearch_or_singleAndRange() {
568 		var in = B.create("2000-12-31", "2001-01-01", "2002-12-31", "2003-01-01");
569 		for (var s : a(
570 				"f=2001 >2002",
571 				"f= 2001   >2002 ",
572 				"f='2001' >'2002'",
573 				"f= '2001'  >'2002' ",
574 				"f=\"2001\" >\"2002\"",
575 				"f= \"2001\"  >\"2002\" ",
576 				"f=>2002 2001",
577 				"f= >2002  2001 ",
578 				"f=>'2002' '2001'",
579 				"f= >'2002'  '2001' ",
580 				"f=>\"2002\" \"2001\"",
581 				"f= >\"2002\"  \"2001\" ",
582 				"f=2001 >=2003",
583 				"f= 2001  >=2003 ",
584 				"f='2001' >='2003'",
585 				"f= '2001'  >='2003' ",
586 				"f=\"2001\" >=\"2003\"",
587 				"f= \"2001\"  >=\"2003\" ",
588 				"f=>=2003 2001",
589 				"f= >=2003  2001 ",
590 				"f=>='2003' '2001'",
591 				"f= >='2003'  '2001' ",
592 				"f=>=\"2003\" \"2001\"",
593 				"f= >=\"2003\"  \"2001\" "
594 			))
595 			assertSerialized(run(in, s), ws, "[{f:'2001-01-01T00:00:00'},{f:'2003-01-01T00:00:00'}]");
596 		for (var s : a(
597 				"f=<2001 2003",
598 				"f= <2001  2003 ",
599 				"f=<'2001' '2003'",
600 				"f= <'2001'  '2003' ",
601 				"f=<\"2001\" \"2003\"",
602 				"f= <\"2001\"  \"2003\" ",
603 				"f=2003 <2001",
604 				"f= 2003  <2001 ",
605 				"f='2003' <'2001'",
606 				"f= '2003'  <'2001' ",
607 				"f=\"2003\" <\"2001\"",
608 				"f= \"2003\"  <\"2001\" ",
609 				"f=<=2000 2003",
610 				"f= <=2000  2003 ",
611 				"f=<='2000' '2003'",
612 				"f= <='2000'  '2003' ",
613 				"f=<=\"2000\" \"2003\"",
614 				"f= <=\"2000\"  \"2003\" ",
615 				"f=2003 <=2000",
616 				"f= 2003  <=2000 ",
617 				"f='2003' <='2000'",
618 				"f= '2003'  <='2000' ",
619 				"f=\"2003\" <=\"2000\"",
620 				"f= \"2003\"  <=\"2000\" "
621 			))
622 			assertSerialized(run(in, s), ws, "[{f:'2000-12-31T00:00:00'},{f:'2003-01-01T00:00:00'}]");
623 	}
624 
625 	//-----------------------------------------------------------------------------------------------------------------
626 	// Other data structures.
627 	//-----------------------------------------------------------------------------------------------------------------
628 
629 	@Test void d01_d2ListOfMaps() {
630 		var in = l(
631 			m("f","foo"),
632 			m("f","bar"),
633 			null,
634 			m(null,"qux"),
635 			m("quux",null),
636 			m(null,null)
637 		);
638 		assertBeans(run(in, "f=foo"), "f", "foo");
639 	}
640 
641 	@Test void d02_d2SetOfMaps() {
642 		var in = set(
643 			m("f","foo"),
644 			m("f","bar"),
645 			null,
646 			m(null,"qux"),
647 			m("quux",null),
648 			m(null,null)
649 		);
650 		assertBeans(run(in, "f=foo"), "f", "foo");
651 	}
652 
653 	@Test void d03_d2ArrayOfMaps() {
654 		var in = CollectionUtils.a(
655 			m("f","foo"),
656 			m("f","bar"),
657 			null,
658 			m(null,"qux"),
659 			m("quux",null),
660 			m(null,null)
661 		);
662 		assertBeans(run(in, "f=foo"), "f", "foo");
663 	}
664 
665 	@Test void d04_d2ListOfObjects() {
666 		var in = l(
667 			m("f","foo"),
668 			m("f","bar"),
669 			null,
670 			m(null,"qux"),
671 			m("quux",null),
672 			m(null,null),
673 			"xxx",
674 			123
675 		);
676 		assertBeans(run(in, "f=foo"), "f", "foo");
677 	}
678 
679 	@Test void d05_d2SetOfObjects() {
680 		var in = set(
681 			m("f","foo"),
682 			m("f","bar"),
683 			null,
684 			m(null,"qux"),
685 			m("quux",null),
686 			m(null,null),
687 			"xxx",
688 			123
689 		);
690 		assertBeans(run(in, "f=foo"), "f", "foo");
691 	}
692 
693 	@Test void d06_d2ArrayOfObjects() {
694 		var in = a(
695 			m("f","foo"),
696 			m("f","bar"),
697 			null,
698 			m(null,"qux"),
699 			m("quux",null),
700 			m(null,null),
701 			"xxx",
702 			123
703 		);
704 		assertBeans(run(in, "f=foo"), "f", "foo");
705 	}
706 
707 	@Test void d07_d2ListOfMapsWithLists() {
708 		var in = l(
709 			m("f",l("foo")),
710 			m("f",l("bar")),
711 			null,
712 			m(null,l("qux")),
713 			m("quux",l((Object)null)),
714 			m(null,l((Object)null))
715 		);
716 		assertBeans(run(in, "f=foo"), "f", "[foo]");
717 	}
718 
719 	@Test void d08_d2SetOfMapsWithSets() {
720 		var in = set(
721 			m("f",set("foo")),
722 			m("f",set("bar")),
723 			null,
724 			m(null,set("qux")),
725 			m("quux",set((Object)null)),
726 			m(null,set((Object)null))
727 		);
728 		assertBeans(run(in, "f=foo"), "f", "[foo]");
729 	}
730 
731 	@Test void d09_d2ArrayOfMapsWithArrays() {
732 		var in = a(
733 			m("f",ao("foo")),
734 			m("f",ao("bar")),
735 			null,
736 			m(null,ao("qux")),
737 			m("quux",ao((String)null)),
738 			m(null,ao((String)null))
739 		);
740 		assertBeans(run(in, "f=foo"), "f", "[foo]");
741 	}
742 
743 	@Test void d10_d2ListOfBeans() {
744 		var in = l(
745 			A.create("foo"),
746 			A.create("bar"),
747 			null,
748 			A.create(null)
749 		);
750 		assertBeans(run(in, "f=foo"), "f", "foo");
751 	}
752 
753 	@Test void d11_d3ListOfListOfMaps() {
754 		var in = l(
755 			l(m("f","foo")),
756 			l(m("f","bar")),
757 			l((Map<?,?>)null),
758 			l(m(null,"qux")),
759 			l(m("quux",null)),
760 			l(m(null,null)),
761 			null
762 		);
763 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
764 	}
765 
766 	@Test void d12_d3SetOfSetOfMaps() {
767 		var in = set(
768 			set(map("f","foo")),
769 			set(map("f","bar")),
770 			set(map("f","baz")),
771 			set((Map<?,?>)null),
772 			set(map(null,"qux")),
773 			set(map("quux",null)),
774 			set(map(null,null)),
775 			null
776 		);
777 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
778 	}
779 
780 	@Test void d13_d3ArrayOfArrayOfMaps() {
781 		var in = a(
782 			a(map("f","foo")),
783 			a(map("f","bar")),
784 			a(map("f","baz")),
785 			a((Map<?,?>)null),
786 			a(map(null,"qux")),
787 			a(map("quux",null)),
788 			a(map(null,null)),
789 			null
790 		);
791 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
792 	}
793 
794 	@Test void d14_d3ListOfListOfObjects() {
795 		var in = l(
796 			l(map("f","foo")),
797 			l(map("f","bar")),
798 			l((Object)null),
799 			l(map(null,"qux")),
800 			l(map("quux",null)),
801 			l(map(null,null)),
802 			l("xxx"),
803 			null
804 		);
805 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
806 	}
807 
808 	@Test void d15_d3SetOfSetOfObjects() {
809 		var in = set(
810 			set(map("f","foo")),
811 			set(map("f","bar")),
812 			set((Map<?,?>)null),
813 			set(map(null,"qux")),
814 			set(map("quux",null)),
815 			set(map(null,null)),
816 			set("xxx"),
817 			set(123),
818 			null
819 		);
820 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
821 	}
822 
823 	@Test void d16_d3ArrayOfArrayOfObjects() {
824 		var in = a(
825 			ao(map("f","foo")),
826 			ao(map("f","bar")),
827 			ao((Object)null),
828 			ao(map(null,"qux")),
829 			ao(map("quux",null)),
830 			ao(map(null,null)),
831 			ao("xxx"),
832 			ao(123),
833 			null
834 		);
835 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
836 	}
837 
838 	@Test void d17_d3ListOfListOfMapsWithCollections() {
839 		var in = l(
840 			l(map("f",l("foo"))),
841 			l(map("f",l("bar"))),
842 			l((Map<?,?>)null),
843 			l(map(null,l("qux"))),
844 			l(map("quux",l((Object)null))),
845 			l(map(null,l((Object)null))),
846 			null
847 		);
848 		assertBeans(run(in, "f=foo"), "#{f}", "[{[foo]}]");
849 	}
850 
851 	@Test void d18_d3SetOfSetOfMapsWithCollections() {
852 		var in = set(
853 			set(map("f",set("foo"))),
854 			set(map("f",set("bar"))),
855 			set((Map<?,?>)null),
856 			set(map(null,set("qux"))),
857 			set(map("quux",set((Object)null))),
858 			set(map(null,set((Object)null))),
859 			null
860 		);
861 		assertBeans(run(in, "f=foo"), "#{f}", "[{[foo]}]");
862 	}
863 
864 	@Test void d19_d3ArrayOfArrayOfMapsWithCollections() {
865 		var in = a(
866 			a(map("f",ao("foo"))),
867 			a(map("f",ao("bar"))),
868 			a((Map<?,?>)null),
869 			a(map(null,ao("qux"))),
870 			a(map("quux",ao((Object)null))),
871 			a(map(null,ao((Object)null))),
872 			null
873 		);
874 		assertBeans(run(in, "f=foo"), "#{f}", "[{[foo]}]");
875 	}
876 
877 	@Test void d20_d3ArrayOfArrayOfBeans() {
878 		var in = a(
879 			a(A.create("foo")),
880 			a(A.create("bar")),
881 			a((A)null),
882 			a(A.create(null)),
883 			null
884 		);
885 		assertBeans(run(in, "f=foo"), "#{f}", "[{foo}]");
886 	}
887 }