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.cp;
18  
19  import static java.lang.annotation.ElementType.*;
20  import static java.lang.annotation.RetentionPolicy.*;
21  import static org.apache.juneau.TestUtils.*;
22  import static org.junit.jupiter.api.Assertions.*;
23  
24  import java.lang.annotation.*;
25  import java.util.*;
26  import java.util.function.*;
27  
28  import org.apache.juneau.*;
29  import org.apache.juneau.annotation.*;
30  import org.apache.juneau.reflect.*;
31  import org.junit.jupiter.api.*;
32  
33  class BeanStore_Test extends TestBase {
34  
35  	@Documented
36  	@Target({PARAMETER})
37  	@Retention(RUNTIME)
38  	@Inherited
39  	public @interface Named {
40  		String value();
41  	}
42  
43  	//-----------------------------------------------------------------------------------------------------------------
44  	// Basic tests
45  	//-----------------------------------------------------------------------------------------------------------------
46  
47  	public static class A1 {}
48  	public static class A2 {}
49  
50  	public static final String A1n = A1.class.getSimpleName();  // NOSONAR
51  
52  	public static class A3 extends BeanStore {
53  		protected A3(Builder builder) {
54  			super(builder);
55  		}
56  	}
57  
58  	private static A1 a1a = new A1(), a1b = new A1(), a1c = new A1(), a1d = new A1(), a1e = new A1();
59  	private static A2 a2a = new A2();
60  
61  	@Test void a00_dummy() {
62  		assertDoesNotThrow(BeanStore.Void::new);
63  	}
64  
65  	@Test void a01_builderCopyConstructor() {
66  		var b1p = BeanStore.create().readOnly().threadSafe().build();
67  		var b1c = BeanStore.create().parent(b1p).build();
68  		assertContains("readOnly:true", b1c.toString());
69  		assertContains("threadSafe:true", b1c.toString());
70  	}
71  
72  	@Test void a02_readOnly() {
73  		var b1p = BeanStore.create().readOnly().build();
74  		var b1c = BeanStore.create().parent(b1p).build();
75  		var b2p = BeanStore.create().readOnly().threadSafe().build();
76  		var b2c = BeanStore.create().parent(b1p).threadSafe().build();
77  
78  		for (BeanStore b : array(b1p, b2p)) {
79  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.add(A1.class, a1a));
80  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.add(A1.class, a1a, "foo"));
81  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addBean(A1.class, a1a));
82  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addBean(A1.class, a1a, "foo"));
83  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addSupplier(A1.class, ()->a1a));
84  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.addSupplier(A1.class, ()->a1a, "foo"));
85  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", b::clear);
86  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.removeBean(A1.class));
87  			assertThrowsWithMessage(IllegalStateException.class, "Method cannot be used because BeanStore is read-only.", ()->b.removeBean(A1.class, "foo"));
88  		}
89  
90  		for (var b : array(b1c, b2c)) {
91  			b.add(A1.class, a1a);
92  			b.add(A1.class, a1a, "foo");
93  			b.addBean(A1.class, a1a);
94  			b.addBean(A1.class, a1a, "foo");
95  			b.addSupplier(A1.class, ()->a1a);
96  			b.addSupplier(A1.class, ()->a1a, "foo");
97  			b.clear();
98  			b.removeBean(A1.class);
99  			b.removeBean(A1.class, "foo");
100 		}
101 	}
102 
103 	@Test void a04_addBean() {
104 		var b1p = BeanStore.create().build();
105 		var b1c = BeanStore.of(b1p);
106 		var b2p = BeanStore.create().threadSafe().build();
107 		var b2c = BeanStore.create().threadSafe().parent(b2p).build();
108 
109 		for (var b : array(b1p, b1c, b2p, b2c)) {
110 			assertFalse(b.hasBean(A1.class));
111 			assertEmpty(b.getBean(A1.class));
112 		}
113 
114 		b1p.addBean(A1.class, a1a);
115 		b2p.addBean(A1.class, a1a);
116 		for (var b : array(b1p, b1c, b2p, b2c)) {
117 			assertTrue(b.hasBean(A1.class));
118 			assertEquals(a1a, b.getBean(A1.class).get());
119 		}
120 
121 		b1p.clear();
122 		b2p.clear();
123 		for (var b : array(b1p, b1c, b2p, b2c)) {
124 			assertFalse(b.hasBean(A1.class));
125 			assertEmpty(b.getBean(A1.class));
126 		}
127 
128 		b1p.addBean(A1.class, null);
129 		b2p.addBean(A1.class, null);
130 		for (var b : array(b1p, b1c, b2p, b2c)) {
131 			assertTrue(b.hasBean(A1.class));
132 			assertEmpty(b.getBean(A1.class));
133 		}
134 
135 		b1p.clear().addSupplier(A1.class, ()->a1a);
136 		b2p.clear().addSupplier(A1.class, ()->a1a);
137 		for (var b : array(b1p, b1c, b2p, b2c)) {
138 			assertTrue(b.hasBean(A1.class));
139 			assertEquals(a1a, b.getBean(A1.class).get());
140 		}
141 
142 		b1p.add(A1.class, a1b);
143 		b2p.add(A1.class, a1b);
144 		for (var b : array(b1p, b1c, b2p, b2c)) {
145 			assertTrue(b.hasBean(A1.class));
146 			assertEquals(a1b, b.getBean(A1.class).get());
147 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1b, a1a);
148 		}
149 
150 		b1c.add(A2.class, a2a);
151 		b2c.add(A2.class, a2a);
152 		for (var b : array(b1p, b2p)) {
153 			assertFalse(b.hasBean(A2.class));
154 			assertEmpty(b.getBean(A2.class));
155 			assertEmpty(b.stream(A2.class));
156 		}
157 		for (var b : array(b1c, b2c)) {
158 			assertTrue(b.hasBean(A2.class));
159 			assertEquals(a2a, b.getBean(A2.class).get());
160 			assertList(b.stream(A2.class).map(BeanStoreEntry::get), a2a);
161 		}
162 
163 		assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}", b1p);
164 		assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}}", b1c);
165 		assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true}", b2p);
166 		assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true},threadSafe:true}", b2c);
167 
168 		b1p.removeBean(A1.class);
169 		b1c.clear().addBean(A1.class, a1a);
170 		b2p.removeBean(A1.class);
171 		b2c.clear().addBean(A1.class, a1a);
172 
173 		for (var b : array(b1p, b2p)) {
174 			assertFalse(b.hasBean(A1.class));
175 			assertEmpty(b.getBean(A1.class));
176 			assertEmpty(b.stream(A1.class));
177 		}
178 		for (var b : array(b1c, b2c)) {
179 			assertTrue(b.hasBean(A1.class));
180 			assertEquals(a1a, b.getBean(A1.class).get());
181 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1a);
182 		}
183 
184 		b1c.removeBean(A1.class);
185 		b2c.removeBean(A1.class);
186 		for (var b : array(b1p, b1c, b2p, b2c)) {
187 			assertFalse(b.hasBean(A1.class));
188 			assertEmpty(b.getBean(A1.class));
189 			assertEmpty(b.stream(A1.class));
190 		}
191 	}
192 
193 	@Test void a05_addNamedBeans() {
194 		var b1p = BeanStore.create().build();
195 		var b1c = BeanStore.of(b1p);
196 		var b2p = BeanStore.create().threadSafe().build();
197 		var b2c = BeanStore.create().threadSafe().parent(b2p).build();
198 
199 		for (var b : array(b1p, b2p)) {
200 			b.addBean(A1.class, a1a).addBean(A1.class, a1b, "foo").addBean(A1.class, a1c, "bar").addBean(A1.class, a1d, "bar").addBean(A2.class, a2a, "foo");
201 		}
202 		for (var b : array(b1c, b2c)) {
203 			b.addBean(A1.class, a1e);
204 		}
205 
206 		for (var b : array(b1p, b2p)) {
207 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1d,a1c,a1b,a1a);
208 		}
209 		for (var b : array(b1c, b2c)) {
210 			assertList(b.stream(A1.class).map(BeanStoreEntry::get), a1e,a1d,a1c,a1b,a1a);
211 		}
212 
213 		for (var b : array(b1p, b1c, b2p, b2c)) {
214 			assertEquals(a1b, b.getBean(A1.class, "foo").get());
215 			assertEquals(a1d, b.getBean(A1.class, "bar").get());
216 			assertEmpty(b.getBean(A1.class, "baz"));
217 		}
218 		for (var b : array(b1p, b2p)) {
219 			assertEquals(a1a, b.getBean(A1.class, null).get());
220 		}
221 		for (var b : array(b1c, b2c)) {
222 			assertEquals(a1e, b.getBean(A1.class, null).get());
223 		}
224 	}
225 
226 	//-----------------------------------------------------------------------------------------------------------------
227 	// Parameter matching
228 	//-----------------------------------------------------------------------------------------------------------------
229 
230 	public static class B1 {
231 		A1 a1;
232 		Optional<A2> a2;
233 		BeanStore a3;
234 
235 		public B1(A1 a1, Optional<A2> a2, BeanStore a3) {
236 			this.a1 = a1;
237 			this.a2 = a2;
238 			this.a3 = a3;
239 		}
240 
241 		public B1(@Named("foo") A1 a1, @Named("bar") Optional<A2> a2) {
242 			this.a1 = a1;
243 			this.a2 = a2;
244 		}
245 
246 		public void m1(A1 a1, Optional<A2> a2, BeanStore a3) {
247 			this.a1 = a1;
248 			this.a2 = a2;
249 			this.a3 = a3;
250 		}
251 
252 		public void m2(@Named("foo") A1 a1, @Named("bar") Optional<A2> a2) {
253 			this.a1 = a1;
254 			this.a2 = a2;
255 			this.a3 = null;
256 		}
257 
258 		public static B1 m3(A1 a1, Optional<A2> a2, BeanStore a3) {
259 			return new B1(a1, a2, a3);
260 		}
261 	}
262 
263 	@Test void b01_getParams() {
264 
265 		Predicate<Object> pEmptyOptional = x -> !((Optional<?>)x).isPresent();
266 		Predicate<Object> pIsBeanStore = BeanStore.class::isInstance;
267 		Predicate<Object> pNull = x -> x == null;
268 		Predicate<Object> pA1a = x -> x==a1a;
269 		Predicate<Object> pA2a = x -> ((Optional<?>)x).get()==a2a;
270 
271 		var outer = new B1(null, null, null);
272 
273 		var b1p = BeanStore.create().outer(outer).build();
274 		var b1c = BeanStore.create().outer(outer).parent(b1p).build();
275 		var b2p = BeanStore.create().outer(outer).threadSafe().build();
276 		var b2c = BeanStore.create().outer(outer).parent(b1p).threadSafe().build();
277 
278 		var ci = ClassInfo.of(B1.class);
279 		var c1 = ci.getPublicConstructor(x -> x.hasParamTypes(A1.class, Optional.class, BeanStore.class));
280 		var c2 = ci.getPublicConstructor(x -> x.hasParamTypes(A1.class, Optional.class));
281 		var m1 = ci.getPublicMethod(x-> x.hasName("m1"));
282 		var m2 = ci.getPublicMethod(x-> x.hasName("m2"));
283 		var m3 = ci.getPublicMethod(x-> x.hasName("m3"));
284 
285 		for (var b : array(b1p, b1c, b2p, b2c)) {
286 			for (var e : array(c1, m1, m3)) {
287 				assertString(A1n, b.getMissingParams(e));
288 				assertFalse(b.hasAllParams(e));
289 			}
290 			for (var e : array(c2, m2)) {
291 				assertString(A1n+"@foo", b.getMissingParams(e));
292 				assertFalse(b.hasAllParams(e));
293 			}
294 		}
295 
296 		for (var b : array(b1p, b1c, b2p, b2c)) {
297 			assertList(b.getParams(c1), pNull, pEmptyOptional, pIsBeanStore);
298 			assertList(b.getParams(c2), pNull, pEmptyOptional);
299 			assertList(b.getParams(m1), pNull, pEmptyOptional, pIsBeanStore);
300 			assertList(b.getParams(m2), pNull, pEmptyOptional);
301 			assertList(b.getParams(m3), pNull, pEmptyOptional, pIsBeanStore);
302 		}
303 
304 		b1p.add(A1.class, a1a);
305 		b2p.add(A1.class, a1a);
306 		for (var b : array(b1p, b1c, b2p, b2c)) {
307 			assertNull(b.getMissingParams(c1));
308 			assertString(A1n+"@foo", b.getMissingParams(c2));
309 			assertNull(b.getMissingParams(m1));
310 			assertString(A1n+"@foo", b.getMissingParams(m2));
311 			assertNull(b.getMissingParams(m3));
312 			assertTrue(b.hasAllParams(c1));
313 			assertFalse(b.hasAllParams(c2));
314 			assertTrue(b.hasAllParams(m1));
315 			assertFalse(b.hasAllParams(m2));
316 			assertTrue(b.hasAllParams(m3));
317 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
318 			assertList(b.getParams(c2), pNull, pEmptyOptional);
319 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
320 			assertList(b.getParams(m2), pNull, pEmptyOptional);
321 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
322 		}
323 
324 		b1p.add(A1.class, a1a, "foo");
325 		b2p.add(A1.class, a1a, "foo");
326 		for (var b : array(b1p, b1c, b2p, b2c)) {
327 			for (var e : array(c1, c2, m1, m2, m3)) {
328 				assertNull(b.getMissingParams(e));
329 				assertTrue(b.hasAllParams(e));
330 			}
331 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
332 			assertList(b.getParams(c2), pA1a, pEmptyOptional);
333 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
334 			assertList(b.getParams(m2), pA1a, pEmptyOptional);
335 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
336 		}
337 
338 		b1p.add(A1.class, a1b, "bar");
339 		b2p.add(A1.class, a1b, "bar");
340 		for (var b : array(b1p, b1c, b2p, b2c)) {
341 			for (var e : array(c1, c2, m1, m2, m3)) {
342 				assertNull(b.getMissingParams(e));
343 				assertTrue(b.hasAllParams(e));
344 			}
345 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
346 			assertList(b.getParams(c2), pA1a, pEmptyOptional);
347 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
348 			assertList(b.getParams(m2), pA1a, pEmptyOptional);
349 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
350 		}
351 
352 		b1p.add(A2.class, a2a, "bar");
353 		b2p.add(A2.class, a2a, "bar");
354 		for (var b : array(b1p, b1c, b2p, b2c)) {
355 			for (var e : array(c1, c2, m1, m2, m3)) {
356 				assertNull(b.getMissingParams(e));
357 				assertTrue(b.hasAllParams(e));
358 			}
359 			assertList(b.getParams(c1), pA1a, pEmptyOptional, pIsBeanStore);
360 			assertList(b.getParams(c2), pA1a, pA2a);
361 			assertList(b.getParams(m1), pA1a, pEmptyOptional, pIsBeanStore);
362 			assertList(b.getParams(m2), pA1a, pA2a);
363 			assertList(b.getParams(m3), pA1a, pEmptyOptional, pIsBeanStore);
364 		}
365 
366 		b1p.add(A2.class, a2a, null);
367 		b2p.add(A2.class, a2a, null);
368 		for (var b : array(b1p, b1c, b2p, b2c)) {
369 			for (var e : array(c1, c2, m1, m2, m3)) {
370 				assertNull(b.getMissingParams(e));
371 				assertTrue(b.hasAllParams(e));
372 			}
373 			assertList(b.getParams(c1), pA1a, pA2a, pIsBeanStore);
374 			assertList(b.getParams(c2), pA1a, pA2a);
375 			assertList(b.getParams(m1), pA1a, pA2a, pIsBeanStore);
376 			assertList(b.getParams(m2), pA1a, pA2a);
377 			assertList(b.getParams(m3), pA1a, pA2a, pIsBeanStore);
378 		}
379 	}
380 
381 	public class B2 {
382 		A1 a1;
383 		Optional<A2> a2;
384 		BeanStore a3;
385 
386 		public B2(A1 a1, Optional<A2> a2, BeanStore a3) {
387 			this.a1 = a1;
388 			this.a2 = a2;
389 			this.a3 = a3;
390 		}
391 
392 		public B2(@Named("foo") A1 a1, @Named("bar") Optional<A2> a2) {
393 			this.a1 = a1;
394 			this.a2 = a2;
395 		}
396 	}
397 
398 	@Test void b02_getParams_innerClass() {
399 
400 		Predicate<Object> pEmptyOptional = x -> !((Optional<?>)x).isPresent();
401 		Predicate<Object> pIsBeanStore = BeanStore.class::isInstance;
402 		Predicate<Object> pNull = x -> x == null;
403 		Predicate<Object> pThis = x -> x == this;
404 		Predicate<Object> pA1a = x -> x==a1a;
405 		Predicate<Object> pA2a = x -> ((Optional<?>)x).get()==a2a;
406 
407 		var b1p = BeanStore.create().outer(this).build();
408 		var b1c = BeanStore.create().outer(this).parent(b1p).build();
409 		var b2p = BeanStore.create().outer(this).threadSafe().build();
410 		var b2c = BeanStore.create().outer(this).parent(b1p).threadSafe().build();
411 
412 		var ci = ClassInfo.of(B2.class);
413 		var c1 = ci.getPublicConstructor(x -> x.hasParamTypes(BeanStore_Test.class, A1.class, Optional.class, BeanStore.class));
414 		var c2 = ci.getPublicConstructor(x -> x.hasParamTypes(BeanStore_Test.class, A1.class, Optional.class));
415 
416 		for (var b : array(b1p, b1c, b2p, b2c)) {
417 			assertString(A1n, b.getMissingParams(c1));
418 			assertString(A1n+"@foo", b.getMissingParams(c2));
419 			assertFalse(b.hasAllParams(c1));
420 			assertFalse(b.hasAllParams(c2));
421 		}
422 
423 		for (var b : array(b1p, b1c, b2p, b2c)) {
424 			assertList(b.getParams(c1), pThis, pNull, pEmptyOptional, pIsBeanStore);
425 			assertList(b.getParams(c2), pThis, pNull, pEmptyOptional);
426 		}
427 
428 		b1p.add(A1.class, a1a);
429 		b2p.add(A1.class, a1a);
430 		for (var b : array(b1p, b1c, b2p, b2c)) {
431 			assertNull(b.getMissingParams(c1));
432 			assertString(A1n+"@foo", b.getMissingParams(c2));
433 			assertTrue(b.hasAllParams(c1));
434 			assertFalse(b.hasAllParams(c2));
435 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
436 			assertList(b.getParams(c2), pThis, pNull, pEmptyOptional);
437 		}
438 
439 		b1p.add(A1.class, a1a, "foo");
440 		b2p.add(A1.class, a1a, "foo");
441 		for (var b : array(b1p, b1c, b2p, b2c)) {
442 			for (var e : array(c1, c2)) {
443 				assertNull(b.getMissingParams(e));
444 				assertTrue(b.hasAllParams(e));
445 			}
446 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
447 			assertList(b.getParams(c2), pThis, pA1a, pEmptyOptional);
448 		}
449 
450 		b1p.add(A1.class, a1b, "bar");
451 		b2p.add(A1.class, a1b, "bar");
452 		for (var b : array(b1p, b1c, b2p, b2c)) {
453 			for (var e : array(c1, c2)) {
454 				assertNull(b.getMissingParams(e));
455 				assertTrue(b.hasAllParams(e));
456 			}
457 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
458 			assertList(b.getParams(c2), pThis, pA1a, pEmptyOptional);
459 		}
460 
461 		b1p.add(A2.class, a2a, "bar");
462 		b2p.add(A2.class, a2a, "bar");
463 		for (var b : array(b1p, b1c, b2p, b2c)) {
464 			for (var e : array(c1, c2)) {
465 				assertNull(b.getMissingParams(e));
466 				assertTrue(b.hasAllParams(e));
467 			}
468 			assertList(b.getParams(c1), pThis, pA1a, pEmptyOptional, pIsBeanStore);
469 			assertList(b.getParams(c2), pThis, pA1a, pA2a);
470 		}
471 
472 		b1p.add(A2.class, a2a, null);
473 		b2p.add(A2.class, a2a, null);
474 		for (var b : array(b1p, b1c, b2p, b2c)) {
475 			for (var e : array(c1, c2)) {
476 				assertNull(b.getMissingParams(e));
477 				assertTrue(b.hasAllParams(e));
478 			}
479 			assertList(b.getParams(c1), pThis, pA1a, pA2a, pIsBeanStore);
480 			assertList(b.getParams(c2), pThis, pA1a, pA2a);
481 		}
482 	}
483 
484 	//-----------------------------------------------------------------------------------------------------------------
485 	// createMethodFinder()
486 	//-----------------------------------------------------------------------------------------------------------------
487 
488 	public static class C {
489 		public A1 a;
490 	}
491 
492 	@Test void c00_createMethodFinder_invalidArgs() {
493 		var b = BeanStore.create().build();
494 		assertThrowsWithMessage(IllegalArgumentException.class, "Method cannot be used without outer bean definition.", ()->b.createMethodFinder(null));
495 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'beanType' cannot be null.", ()->b.createMethodFinder((Class<?>)null,""));
496 		assertThrowsWithMessage(IllegalArgumentException.class, "Argument 'resourceClass' cannot be null.", ()->b.createMethodFinder(String.class,null));
497 	}
498 
499 	// Instance methods.
500 	public static class C1 {
501 		public C createA1() { return new C(); }
502 		public A1 createA2() { return new A1(); }
503 		protected C createA3() { return new C(); }
504 		@Deprecated public C createA4() { return new C(); }
505 		@BeanIgnore public C createA5() { return new C(); }
506 		public C createA6() { return null; }
507 		public C createA7() { throw new RuntimeException("foo"); }
508 	}
509 
510 	@Test void c01_createMethodFinder_instanceMethods() throws Exception {
511 		var x = new C1();
512 		var b1p = BeanStore.create().build();
513 		var b1c = BeanStore.create().parent(b1p).build();
514 		var b2p = BeanStore.create().build();
515 		var b2c = BeanStore.create().outer(x).parent(b2p).build();
516 		var b3p = BeanStore.create().build();
517 		var b3c = BeanStore.create().outer(this).parent(b3p).build();
518 
519 		for (var m : array("createA0", "createA2", "createA3", "createA4", "createA5", "createA6")) {
520 			for (var b : array(b1c, b2c, b3c)) {
521 				assertNull(b.createMethodFinder(C.class, x).find(m).run());
522 				assertNull(b.createMethodFinder(C.class, C1.class).find(m).run());
523 			}
524 			for (var b : array(b2c, b3c)) {
525 				assertNull(b.createMethodFinder(C.class).find(m).run());
526 			}
527 		}
528 		assertNotNull(b1c.createMethodFinder(C.class, x).find("createA1").run());
529 		assertNull(b1c.createMethodFinder(C.class, C1.class).find("createA1").run());
530 		assertNotNull(b2c.createMethodFinder(C.class).find("createA1").run());
531 		assertNull(b3c.createMethodFinder(C.class).find("createA1").run());
532 		assertThrowsWithMessage(Exception.class, "foo", ()->b1c.createMethodFinder(C.class, x).find("createA7").run());
533 		assertNull(b1c.createMethodFinder(C.class, C1.class).find("createA7").run());
534 		assertThrowsWithMessage(Exception.class, "foo", ()->b2c.createMethodFinder(C.class).find("createA7").run());
535 		assertNull(b3c.createMethodFinder(C.class).find("createA7").run());
536 	}
537 
538 	// Static methods.
539 	public static class C2 {
540 		public static C createB1() { return new C(); }
541 		public static A1 createB2() { return new A1(); }
542 		protected static C createB3() { return new C(); }
543 		@Deprecated public static C createB4() { return new C(); }
544 		@BeanIgnore public static C createB5() { return new C(); }
545 		public static C createB6() { return null; }
546 		public static C createB7() { throw new RuntimeException("foo"); }
547 	}
548 
549 	@Test void c02_createMethodFinder_staticMethods() throws Exception {
550 		var x = new C2();
551 		var b1p = BeanStore.create().build();
552 		var b1c = BeanStore.create().parent(b1p).build();
553 		var b2p = BeanStore.create().build();
554 		var b2c = BeanStore.create().outer(x).parent(b2p).build();
555 		var b3p = BeanStore.create().build();
556 		var b3c = BeanStore.create().outer(this).parent(b3p).build();
557 
558 		for (var m : array("createB0", "createB2", "createB3", "createB4", "createB5", "createB6")) {
559 			for (var b : array(b1c, b2c, b3c)) {
560 				assertNull(b.createMethodFinder(C.class, x).find(m).run());
561 				assertNull(b.createMethodFinder(C.class, C2.class).find(m).run());
562 			}
563 			for (var b : array(b2c, b3c)) {
564 				assertNull(b.createMethodFinder(C.class).find(m).run());
565 			}
566 		}
567 		assertNotNull(b1c.createMethodFinder(C.class, x).find("createB1").run());
568 		assertNotNull(b1c.createMethodFinder(C.class, C2.class).find("createB1").run());
569 		assertNotNull(b2c.createMethodFinder(C.class).find("createB1").run());
570 		assertNull(b3c.createMethodFinder(C.class).find("createB1").run());
571 		assertThrowsWithMessage(Exception.class, "foo", ()->b1c.createMethodFinder(C.class, x).find("createB7").run());
572 		assertThrowsWithMessage(Exception.class, "foo", ()->b1c.createMethodFinder(C.class, C2.class).find("createB7").run());
573 		assertThrowsWithMessage(Exception.class, "foo", ()->b2c.createMethodFinder(C.class).find("createB7").run());
574 		assertNull(b3c.createMethodFinder(C.class).find("createB7").run());
575 	}
576 
577 	// Bean matching.
578 	public static class C3 {
579 		public C createC1(A1 a) { return new C(); }
580 		public static C createC2(A1 a) { return new C(); }
581 		public static C createC3(Optional<A1> a) { C e = new C(); e.a = a.orElse(null); return e; }
582 		public static C createC4(@Named("Foo") A1 a) { return new C(); }
583 		public static C createC5(@Named("Foo") Optional<A1> a) { C e = new C(); e.a = a.orElse(null); return e; }
584 		public static C createC6(BeanStore bs) { assertNotNull(bs); return new C(); }
585 	}
586 
587 	@Test void c03_createMethodFinder_beanMatching() throws Exception {
588 		var x = new C3();
589 		var b1p = BeanStore.create().build();
590 		var b1c = BeanStore.create().parent(b1p).build();
591 		var b2p = BeanStore.create().build();
592 		var b2c = BeanStore.create().outer(x).parent(b2p).build();
593 		var b3p = BeanStore.create().build();
594 		var b3c = BeanStore.create().outer(this).parent(b3p).build();
595 
596 		for (var b : array(b1c, b2c, b3c)) {
597 			assertNull(b.createMethodFinder(C.class, x).find("createC1").run());
598 			assertNull(b.createMethodFinder(C.class, x).find("createC2").run());
599 			assertNull(b.createMethodFinder(C.class, x).find("createC3").run().a);
600 			assertNull(b.createMethodFinder(C.class, x).find("createC4").run());
601 			assertNull(b.createMethodFinder(C.class, x).find("createC5").run().a);
602 			assertNotNull(b.createMethodFinder(C.class, x).find("createC6").run());
603 		}
604 
605 		b1p.addBean(A1.class, new A1());
606 		b2p.addBean(A1.class, new A1());
607 		b3p.addBean(A1.class, new A1());
608 		for (var b : array(b1c, b2c)) {
609 			assertNotNull(b.createMethodFinder(C.class, x).find("createC1").run());
610 			assertNotNull(b.createMethodFinder(C.class, x).find("createC2").run());
611 			assertNotNull(b.createMethodFinder(C.class, x).find("createC3").run().a);
612 			assertNull(b.createMethodFinder(C.class, x).find("createC4").run());
613 			assertNull(b.createMethodFinder(C.class, x).find("createC5").run().a);
614 			assertNotNull(b.createMethodFinder(C.class, x).find("createC6").run());
615 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC1").run());
616 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC2").run());
617 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC3").run().a);
618 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC4").run());
619 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC5").run().a);
620 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC6").run());
621 		}
622 		assertNotNull(b2c.createMethodFinder(C.class).find("createC1").run());
623 		assertNotNull(b2c.createMethodFinder(C.class).find("createC2").run());
624 		assertNotNull(b2c.createMethodFinder(C.class).find("createC3").run().a);
625 		assertNull(b2c.createMethodFinder(C.class).find("createC4").run());
626 		assertNull(b2c.createMethodFinder(C.class).find("createC5").run().a);
627 		assertNotNull(b2c.createMethodFinder(C.class).find("createC6").run());
628 		for (var m : array("createC1","createC2","createC3","createC4","createC5","createC6")) {
629 			assertNull(b3c.createMethodFinder(C.class).find(m).run());
630 		}
631 
632 		b1p.clear().addSupplier(A1.class, A1::new);
633 		b2p.clear().addSupplier(A1.class, A1::new);
634 		for (var b : array(b1c, b2c)) {
635 			assertNotNull(b.createMethodFinder(C.class, x).find("createC1").run());
636 			assertNotNull(b.createMethodFinder(C.class, x).find("createC2").run());
637 			assertNotNull(b.createMethodFinder(C.class, x).find("createC3").run().a);
638 			assertNull(b.createMethodFinder(C.class, x).find("createC4").run());
639 			assertNull(b.createMethodFinder(C.class, x).find("createC5").run().a);
640 			assertNotNull(b.createMethodFinder(C.class, x).find("createC6").run());
641 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC1").run());
642 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC2").run());
643 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC3").run().a);
644 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC4").run());
645 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC5").run().a);
646 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC6").run());
647 		}
648 		assertNotNull(b2c.createMethodFinder(C.class).find("createC1").run());
649 		assertNotNull(b2c.createMethodFinder(C.class).find("createC2").run());
650 		assertInstanceOf(A1.class, b2c.createMethodFinder(C.class).find("createC3").run().a);
651 		assertNull(b2c.createMethodFinder(C.class).find("createC4").run());
652 		assertNull(b2c.createMethodFinder(C.class).find("createC5").run().a);
653 		assertNotNull(b2c.createMethodFinder(C.class).find("createC6").run());
654 		for (var m : array("createC1","createC2","createC3","createC4","createC5","createC6")) {
655 			assertNull(b3c.createMethodFinder(C.class).find(m).run());
656 		}
657 
658 		b1p.clear().add(A1.class, null);
659 		b2p.clear().add(A1.class, null);
660 		for (var b : array(b1c, b2c)) {
661 			assertNotNull(b.createMethodFinder(C.class, x).find("createC1").run());
662 			assertNotNull(b.createMethodFinder(C.class, x).find("createC2").run());
663 			assertNull(b.createMethodFinder(C.class, x).find("createC3").run().a);
664 			assertNull(b.createMethodFinder(C.class, x).find("createC4").run());
665 			assertNull(b.createMethodFinder(C.class, x).find("createC5").run().a);
666 			assertNotNull(b.createMethodFinder(C.class, x).find("createC6").run());
667 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC1").run());
668 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC2").run());
669 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC3").run().a);
670 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC4").run());
671 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC5").run().a);
672 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC6").run());
673 		}
674 		assertNotNull(b2c.createMethodFinder(C.class).find("createC1").run());
675 		assertNotNull(b2c.createMethodFinder(C.class).find("createC2").run());
676 		assertNull(b2c.createMethodFinder(C.class).find("createC3").run().a);
677 		assertNull(b2c.createMethodFinder(C.class).find("createC4").run());
678 		assertNull(b2c.createMethodFinder(C.class).find("createC5").run().a);
679 		assertNotNull(b2c.createMethodFinder(C.class).find("createC6").run());
680 		for (var m : array("createC1","createC2","createC3","createC4","createC5","createC6")) {
681 			assertNull(b3c.createMethodFinder(C.class).find(m).run());
682 		}
683 
684 		b1p.clear().addBean(A1.class, new A1()).add(A1.class, new A1(), "Foo");
685 		b2p.clear().addBean(A1.class, new A1()).add(A1.class, new A1(), "Foo");
686 		for (var b : array(b1c, b2c)) {
687 			assertNotNull(b.createMethodFinder(C.class, x).find("createC1").run());
688 			assertNotNull(b.createMethodFinder(C.class, x).find("createC2").run());
689 			assertNotNull(b.createMethodFinder(C.class, x).find("createC3").run().a);
690 			assertNotNull(b.createMethodFinder(C.class, x).find("createC4").run());
691 			assertNotNull(b.createMethodFinder(C.class, x).find("createC5").run().a);
692 			assertNotNull(b.createMethodFinder(C.class, x).find("createC6").run());
693 			assertNull(b.createMethodFinder(C.class, C3.class).find("createC1").run());
694 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC2").run());
695 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC3").run().a);
696 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC4").run());
697 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC5").run().a);
698 			assertNotNull(b.createMethodFinder(C.class, C3.class).find("createC6").run());
699 		}
700 		assertNotNull(b2c.createMethodFinder(C.class).find("createC1").run());
701 		assertNotNull(b2c.createMethodFinder(C.class).find("createC2").run());
702 		assertNotNull(b2c.createMethodFinder(C.class).find("createC3").run().a);
703 		assertNotNull(b2c.createMethodFinder(C.class).find("createC4").run());
704 		assertNotNull(b2c.createMethodFinder(C.class).find("createC5").run().a);
705 		assertNotNull(b2c.createMethodFinder(C.class).find("createC6").run());
706 		for (var m : array("createC1","createC2","createC3","createC4","createC5","createC6")) {
707 			assertNull(b3c.createMethodFinder(C.class).find(m).run());
708 		}
709 	}
710 
711 	// Bean matching.
712 	public static class C4 {
713 		public static String createC1(A1 a) { return "createC1"; }
714 		public static String createC2() { return "createC2"; }
715 	}
716 
717 	@Test void c04_createMethodFinder_beanMatching_requiredArgs() throws Exception {
718 		var x = new C4();
719 		var b1p = BeanStore.create().build();
720 		var b1c = BeanStore.create().outer(x).parent(b1p).build();
721 
722 		assertString("createC2", b1c.createMethodFinder(String.class).find("createC1").thenFind("createC2").run());
723 		assertString("createC2", b1c.createMethodFinder(String.class).find("createC2").thenFind("createC1").run());
724 
725 		b1p.add(A1.class, null);
726 		assertString("createC1", b1c.createMethodFinder(String.class).find("createC1").thenFind("createC2").run());
727 		assertString("createC2", b1c.createMethodFinder(String.class).find("createC2").thenFind("createC1").run());
728 		assertString("createC1", b1c.createMethodFinder(String.class).find(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).run());
729 		assertString("createC1", b1c.createMethodFinder(String.class).find(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).run());
730 
731 		b1p.clear();
732 		assertString("createC1", b1c.createMethodFinder(String.class).addBean(A1.class, null).find("createC1").thenFind("createC2").run());
733 		assertString("createC2", b1c.createMethodFinder(String.class).addBean(A1.class, null).find("createC2").thenFind("createC1").run());
734 		assertString("createC1", b1c.createMethodFinder(String.class).addBean(A1.class, null).find(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).run());
735 		assertString("createC1", b1c.createMethodFinder(String.class).addBean(A1.class, null).find(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).run());
736 
737 		assertString("X", b1c.createMethodFinder(String.class).withDefault("X").run());
738 		assertString("X", b1c.createMethodFinder(String.class).withDefault(()->"X").run());
739 
740 		b1c.createMethodFinder(String.class).withDefault("X").run(y -> assertString("X", y));
741 	}
742 
743 	// Bean matching.
744 	public class C5 {
745 		public String createC1(A1 a) { return "createC1"; }
746 		public String createC2() { return "createC2"; }
747 	}
748 
749 	@Test void c05_createMethodFinder_beanMatching_requiredArgs_innerClass() throws Exception {
750 		var x = new C5();
751 		var b1p = BeanStore.create().build();
752 		var b1c = BeanStore.create().outer(x).parent(b1p).build();
753 
754 		assertString("createC2", b1c.createMethodFinder(String.class).find("createC1").thenFind("createC2").run());
755 		assertString("createC2", b1c.createMethodFinder(String.class).find("createC2").thenFind("createC1").run());
756 
757 		b1p.add(A1.class, null);
758 		assertString("createC1", b1c.createMethodFinder(String.class).find("createC1").thenFind("createC2").run());
759 		assertString("createC2", b1c.createMethodFinder(String.class).find("createC2").thenFind("createC1").run());
760 		assertString("createC1", b1c.createMethodFinder(String.class).find(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).run());
761 		assertString("createC1", b1c.createMethodFinder(String.class).find(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).run());
762 
763 		b1p.clear();
764 		assertString("createC1", b1c.createMethodFinder(String.class).addBean(A1.class, null).find("createC1").thenFind("createC2").run());
765 		assertString("createC2", b1c.createMethodFinder(String.class).addBean(A1.class, null).find("createC2").thenFind("createC1").run());
766 		assertString("createC1", b1c.createMethodFinder(String.class).addBean(A1.class, null).find(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).run());
767 		assertString("createC1", b1c.createMethodFinder(String.class).addBean(A1.class, null).find(x2->x2.hasName("createC2") && x2.hasAllArgs(A1.class)).thenFind(x2->x2.hasName("createC1") && x2.hasAllArgs(A1.class)).run());
768 
769 		assertString("X", b1c.createMethodFinder(String.class).withDefault("X").run());
770 		assertString("X", b1c.createMethodFinder(String.class).withDefault(()->"X").run());
771 
772 		b1c.createMethodFinder(String.class).withDefault("X").run(y -> assertString("X", y));
773 	}
774 
775 	//-----------------------------------------------------------------------------------------------------------------
776 	// createBean()
777 	//-----------------------------------------------------------------------------------------------------------------
778 
779 	public static class D1a {}
780 	public class D1b {}
781 
782 	@Test void d01_createBean_basic() {
783 		var bs = BeanStore.create().outer(new BeanStore_Test()).build();
784 		assertNotNull(bs.createBean(D1a.class).run());
785 		assertNotNull(bs.createBean(D1b.class).run());
786 		assertNull(bs.createBean(null).run());
787 	}
788 
789 	public static class D2 {
790 		public static D2 create() { return d2; }
791 	}
792 	public static D2 d2 = new D2();
793 
794 	@Test void d02_createBean_staticCreator_create() {
795 		var bs = BeanStore.INSTANCE;
796 		assertEquals(d2, bs.createBean(D2.class).run());
797 	}
798 
799 	public abstract static class D3 {
800 		public static D3 getInstance() { return d3; }
801 	}
802 	public static D3 d3 = new D3() {};
803 
804 	@Test void d03_createBean_staticCreator_getInstance() {
805 		var bs = BeanStore.INSTANCE;
806 		assertEquals(d3, bs.createBean(D3.class).run());
807 	}
808 
809 	public static class D4a {
810 		public static D4a getFoo() { return d4a1; }
811 		protected static D4a create() { return d4a2; }
812 		public static D4a create(String foo) { return d4a3; }
813 		@Deprecated protected static D4a getInstance() { return d4a4; }
814 		protected D4a() {}
815 	}
816 	public static D4a d4a1 = new D4a(), d4a2 = new D4a(), d4a3 = new D4a(), d4a4 = new D4a();
817 
818 	public static class D4b {
819 		public D4b create() { return d4b1; }
820 		@BeanIgnore public static D4b getInstance() { return d4b2; }
821 		protected D4b() {}
822 	}
823 	public static D4b d4b1 = new D4b(), d4b2 = new D4b();
824 
825 	public static class D4c {
826 		public static String create() { return null; }
827 		protected D4c() {}
828 	}
829 
830 	@Test void d04_createBean_staticCreator_invalidSignatures() {
831 		var bs = BeanStore.INSTANCE;
832 		assertNotEqualsAny(bs.createBean(D4a.class).run(), d4a1, d4a2, d4a3, d4a4);
833 		assertNotEqualsAny(bs.createBean(D4b.class).run(), d4b1, d4b2);
834 		assertNotNull(bs.createBean(D4c.class).run());
835 	}
836 
837 	public static class D5 {
838 		public static D5 create() { return d5a; }
839 		public static D5 create(Integer i, String s) { return d5b; }
840 		public static D5 create(Integer i) { return d5c; }
841 		protected D5() {}
842 	}
843 	public static D5 d5a = new D5(), d5b = new D5(), d5c = new D5();
844 
845 	@Test void d05_createBean_staticCreator_withBeans() {
846 		var bs = BeanStore.create().build();
847 		assertEquals(d5a, bs.createBean(D5.class).run());
848 		bs.add(Integer.class, 1);
849 		assertEquals(d5c, bs.createBean(D5.class).run());
850 		bs.add(String.class, "x");
851 		assertEquals(d5b, bs.createBean(D5.class).run());
852 		bs.removeBean(Integer.class);
853 		assertEquals(d5a, bs.createBean(D5.class).run());
854 	}
855 
856 	public static class D6 {
857 		public String s;
858 		public static D6 create() { return new D6("0"); }
859 		protected D6(String s) {
860 			this.s = s;
861 		}
862 	}
863 
864 	@Test void d06_createBean_staticCreator_ignoredWithBuilder() {
865 		var bs = BeanStore.INSTANCE;
866 		assertString("1", bs.createBean(D6.class).builder(String.class, "1").run().s);
867 	}
868 
869 	public static class D7 {
870 		public String a;
871 		public static D7 create(Optional<String> s) { return new D7(s.orElse("X")); }
872 		protected D7(String s) { a = s; }
873 	}
874 
875 	@Test void d07_createBean_staticCreator_withOptional() {
876 		var bs = BeanStore.create().build();
877 		assertString("X", bs.createBean(D7.class).run().a);
878 		bs.add(String.class, "bar");
879 		assertString("bar", bs.createBean(D7.class).run().a);
880 	}
881 
882 	public static class D8 {
883 		public String a = "foo";
884 		public static D8 create(Optional<String> s, Integer i) { return new D8(s.orElse(null) + "," + i); }
885 		private D8(String s) { a = s; }
886 	}
887 
888 	@Test void d08_createBean_staticCreator_missingPrereqs() {
889 		var bs = BeanStore.create().build();
890 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class org.apache.juneau.cp.BeanStore_Test$D8: Static creator found but could not find prerequisites: Integer.", ()->bs.createBean(D8.class).run());
891 		bs.add(Integer.class, 1);
892 		assertString("null,1", bs.createBean(D8.class).run().a);
893 		bs.add(String.class, "bar");
894 		assertString("bar,1", bs.createBean(D8.class).run().a);
895 	}
896 
897 	public abstract static class D9a {
898 		public D9a() {}
899 	}
900 
901 	public interface D9b {}
902 
903 	@Test void d09_createBean_staticCreator_withBeans() {
904 		var bs = BeanStore.INSTANCE;
905 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D9a.class.getName()+": Class is abstract.", ()->bs.createBean(D9a.class).run());
906 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D9b.class.getName()+": Class is an interface.", ()->bs.createBean(D9b.class).run());
907 	}
908 
909 	public static class D10 {
910 		public String a;
911 		public D10(String s) { a = "s="+s; }
912 		public D10(Integer i) { a = "i="+i; }
913 		public D10(String s, Integer i) { a = "s="+s+",i="+i; }
914 	}
915 
916 	@Test void d10_createBean_constructors_public() {
917 		var bs = BeanStore.create().build();
918 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D10.class.getName()+": Public constructor found but could not find prerequisites: Integer or Integer,String or String.", ()->bs.createBean(D10.class).run());
919 		bs.add(String.class, "foo");
920 		assertString("s=foo", bs.createBean(D10.class).run().a);
921 		bs.add(Integer.class, 1);
922 		assertString("s=foo,i=1", bs.createBean(D10.class).run().a);
923 		bs.removeBean(String.class);
924 		assertString("i=1", bs.createBean(D10.class).run().a);
925 	}
926 
927 	public static class D11 {
928 		public String a;
929 		protected D11(String s) { a = "s="+s; }
930 		protected D11(Integer i) { a = "i="+i; }
931 		protected D11(String s, Integer i) { a = "s="+s+",i="+i; }
932 	}
933 
934 	@Test void d11_createBean_constructors_protected() {
935 		var bs = BeanStore.create().build();
936 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D11.class.getName()+": Protected constructor found but could not find prerequisites: Integer or Integer,String or String.", ()->bs.createBean(D11.class).run());
937 		bs.add(String.class, "foo");
938 		assertString("s=foo", bs.createBean(D11.class).run().a);
939 		bs.add(Integer.class, 1);
940 		assertString("s=foo,i=1", bs.createBean(D11.class).run().a);
941 		bs.removeBean(String.class);
942 		assertString("i=1", bs.createBean(D11.class).run().a);
943 	}
944 
945 	public static class D12 {
946 		public String a;
947 		public D12(String s) { a = "s="+s; }
948 		protected D12(String s, Integer i) { a = "s="+s+",i="+i; }
949 	}
950 
951 	@Test void d12_createBean_constructors_publicOverProtected() {
952 		var bs = BeanStore.create().build();
953 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D12.class.getName()+": Public constructor found but could not find prerequisites: String.", ()->bs.createBean(D12.class).run());
954 		bs.add(String.class, "foo");
955 		bs.add(Integer.class, 1);
956 		assertString("s=foo", bs.createBean(D12.class).run().a);
957 	}
958 
959 	public static class D13 {
960 		private D13() {}
961 	}
962 
963 	@Test void d13_createBean_constructors_private() {
964 		var bs = BeanStore.INSTANCE;
965 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D13.class.getName()+": No public/protected constructors found.", ()->bs.createBean(D13.class).run());
966 	}
967 
968 	public static class D14 {
969 		public String a;
970 		public D14(@Named("foo") String o) { a = o; }
971 		public D14(@Named("foo") String o, Integer i) { a = o + "," + i; }
972 	}
973 
974 	@Test void d14_createBean_constructors_namedBean() {
975 		var bs = BeanStore.create().build();
976 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D14.class.getName()+": Public constructor found but could not find prerequisites: Integer,String@foo or String@foo.", ()->bs.createBean(D14.class).run());
977 		bs.add(String.class, "bar", "foo");
978 		assertString("bar", bs.createBean(D14.class).run().a);
979 	}
980 
981 	public class D15 {
982 		public String a;
983 		public D15(@Named("foo") String o) { a = o; }
984 		public D15(@Named("foo") String o, Integer i) { a = o + "," + i; }
985 	}
986 
987 	@Test void d15_createBean_constructors_namedBean_withOuter() {
988 		var bs = BeanStore.create().outer(new BeanStore_Test()).build();
989 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D15.class.getName()+": Public constructor found but could not find prerequisites: Integer,String@foo or String@foo.", ()->bs.createBean(D15.class).run());
990 		bs.add(String.class, "bar", "foo");
991 		assertString("bar", bs.createBean(D15.class).run().a);
992 	}
993 
994 	public static class D16 {
995 		public String a;
996 		public static Builder create() { return new Builder(); }
997 		public static class Builder {
998 			public String b;
999 		}
1000 		protected D16(Builder b) { a = b.b; }
1001 	}
1002 
1003 	@Test void d16_createBean_builders() {
1004 		var bs = BeanStore.create().build();
1005 		var b = D16.create();
1006 		b.b = "foo";
1007 		assertString("foo", bs.createBean(D16.class).builder(D16.Builder.class, b).run().a);
1008 	}
1009 
1010 	public static class D17 {
1011 		public String a;
1012 		public static Builder create() { return new Builder(); }
1013 		public static class Builder {
1014 			public String b;
1015 		}
1016 		protected D17(Builder b, Integer i) { a = b.b; }
1017 		protected D17(Integer i) {}  // NOSONAR
1018 		D17(String s) {}  // NOSONAR
1019 		protected D17(Builder b) { a = b.b; }
1020 	}
1021 
1022 	@Test void d17_createBean_builders_inherent() {
1023 		var bs = BeanStore.create().build();
1024 		assertNull(bs.createBean(D17.class).run().a);
1025 		assertThrowsWithMessage(ExecutableException.class, "Could not instantiate class "+D17.class.getName()+": Protected constructor found but could not find prerequisites: Builder or Builder,Integer or Integer.", ()->bs.createBean(D17.class).builder(Boolean.class, true).run());
1026 	}
1027 
1028 	//-----------------------------------------------------------------------------------------------------------------
1029 	// Helpers
1030 	//-----------------------------------------------------------------------------------------------------------------
1031 
1032 	@SafeVarargs
1033 	private static <T> T[] array(T...t) {
1034 		return t;
1035 	}
1036 }