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;
18
19 import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
20 import static org.apache.juneau.commons.utils.AssertionUtils.*;
21 import static org.apache.juneau.commons.utils.CollectionUtils.*;
22 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
23 import static org.apache.juneau.commons.utils.Utils.*;
24
25 import java.lang.annotation.*;
26 import java.lang.reflect.*;
27 import java.util.*;
28 import java.util.function.*;
29 import org.apache.juneau.annotation.*;
30 import org.apache.juneau.commons.collections.*;
31 import org.apache.juneau.commons.reflect.*;
32 import org.apache.juneau.commons.utils.*;
33 import org.apache.juneau.csv.annotation.*;
34 import org.apache.juneau.html.annotation.*;
35 import org.apache.juneau.json.annotation.*;
36 import org.apache.juneau.jsonschema.annotation.*;
37 import org.apache.juneau.msgpack.annotation.*;
38 import org.apache.juneau.oapi.annotation.*;
39 import org.apache.juneau.parser.annotation.*;
40 import org.apache.juneau.plaintext.annotation.*;
41 import org.apache.juneau.serializer.annotation.*;
42 import org.apache.juneau.soap.annotation.*;
43 import org.apache.juneau.svl.*;
44 import org.apache.juneau.uon.annotation.*;
45 import org.apache.juneau.urlencoding.annotation.*;
46 import org.apache.juneau.xml.annotation.*;
47
48 /**
49 * Base class for all Context beans.
50 *
51 * <p>
52 * Context beans follow the convention of havinTg the following parts:
53 * <ul>
54 * <li>A {@link Builder} class for configuring the context bean.
55 * <ul>
56 * <li>This bean is non-thread-safe and meant for one-time use.
57 * </ul>
58 * <li>A {@link Context#Context(Builder)} constructor that takes in a builder object.
59 * <ul>
60 * <li>This bean is thread-safe and cacheable/reusable.
61 * </ul>
62 * <li>A {@link ContextSession} class for doing work.
63 * <ul>
64 * <li>This bean is non-thread-safe and meant for one-time use.
65 * </ul>
66 *
67 * <h5 class='section'>Notes:</h5><ul>
68 * <li class='note'>This class is thread safe and reusable.
69 * </ul>
70 *
71 */
72 public abstract class Context {
73
74 /**
75 * Builder class.
76 */
77 public abstract static class Builder {
78
79 private boolean debug;
80 private final AnnotationWorkList applied = AnnotationWorkList.create();
81 private Cache<HashKey,? extends Context> cache;
82 private Class<? extends Context> type;
83 private Context impl;
84 private List<Annotation> annotations;
85 private final List<Object> builders = list();
86
87 /**
88 * Constructor.
89 * Default settings.
90 */
91 @SuppressWarnings("unchecked")
92 protected Builder() {
93 debug = env("Context.debug", false);
94 annotations = list();
95 registerBuilders(this);
96
97 // By default, the type being created should be the class declaring the builder.
98 var dc = getClass().getDeclaringClass();
99 if (Context.class.isAssignableFrom(dc))
100 type((Class<? extends Context>)dc);
101 }
102
103 /**
104 * Copy constructor.
105 *
106 * @param copyFrom The builder to copy from.
107 * <br>Cannot be <jk>null</jk>.
108 */
109 protected Builder(Builder copyFrom) {
110 assertArgNotNull("copyFrom", copyFrom);
111 annotations = copyOf(copyFrom.annotations);
112 debug = copyFrom.debug;
113 type = copyFrom.type;
114 registerBuilders(this);
115 }
116
117 /**
118 * Copy constructor.
119 *
120 * @param copyFrom The bean to copy from.
121 * <br>Cannot be <jk>null</jk>.
122 */
123 protected Builder(Context copyFrom) {
124 assertArgNotNull("copyFrom", copyFrom);
125 annotations = copyOf(copyFrom.annotations);
126 debug = copyFrom.debug;
127 type = copyFrom.getClass();
128 registerBuilders(this);
129 }
130
131 /**
132 * Defines annotations to apply to specific classes and methods.
133 *
134 * <p>
135 * Allows you to dynamically apply Juneau annotations typically applied directly to classes and methods.
136 * Useful in cases where you want to use the functionality of the annotation on beans and bean properties but
137 * do not have access to the code to do so.
138 *
139 * <p>
140 * As a rule, any Juneau annotation with an <l>on()</l> method can be used with this setting.
141 *
142 * <p>
143 * The following example shows the equivalent methods for applying the {@link Bean @Bean} annotation:
144 * <p class='bjava'>
145 * <jc>// Class with explicit annotation.</jc>
146 * <ja>@Bean</ja>(properties=<js>"street,city,state"</js>)
147 * <jk>public class</jk> A {...}
148 *
149 * <jc>// Class with annotation applied via @BeanConfig</jc>
150 * <jk>public class</jk> B {...}
151 *
152 * <jc>// Java REST method with @BeanConfig annotation.</jc>
153 * <ja>@RestGet</ja>(...)
154 * <ja>@Bean</ja>(on=<js>"B"</js>, properties=<js>"street,city,state"</js>)
155 * <jk>public void</jk> doFoo() {...}
156 * </p>
157 *
158 * <p>
159 * In general, the underlying framework uses this method when it finds dynamically applied annotations on
160 * config annotations. However, concrete implementations of annotations are also provided that can be passed
161 * directly into builder classes like so:
162 * <p class='bjava'>
163 * <jc>// Create a concrete @Bean annotation.</jc>
164 * <ja>Bean</ja> <jv>annotation</jv> = BeanAnnotation.<jsm>create</jsm>(B.<jk>class</jk>).properties(<js>"street,city,state"</js>).build();
165 *
166 * <jc>// Apply it to a serializer.</jc>
167 * WriterSerializer <jv>serializer</jv> = JsonSerializer.<jsm>create</jsm>().annotations(<jv>annotation</jv>).build();
168 *
169 * <jc>// Serialize a bean with the dynamically applied annotation.</jc>
170 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> B());
171 * </p>
172 *
173 * <p>
174 * The following is the list of annotations builders provided that can be constructed
175 * and passed into the builder class:
176 * <ul class='javatreec'>
177 * <li class='ja'>{@link org.apache.juneau.annotation.BeanAnnotation}
178 * <li class='ja'>{@link org.apache.juneau.annotation.BeancAnnotation}
179 * <li class='ja'>{@link org.apache.juneau.annotation.BeanIgnoreAnnotation}
180 * <li class='ja'>{@link org.apache.juneau.annotation.BeanpAnnotation}
181 * <li class='ja'>{@link org.apache.juneau.annotation.ExampleAnnotation}
182 * <li class='ja'>{@link org.apache.juneau.annotation.NamePropertyAnnotation}
183 * <li class='ja'>{@link org.apache.juneau.annotation.ParentPropertyAnnotation}
184 * <li class='ja'>{@link org.apache.juneau.annotation.SwapAnnotation}
185 * <li class='ja'>{@link org.apache.juneau.annotation.UriAnnotation}
186 * <li class='ja'>{@link org.apache.juneau.csv.annotation.CsvAnnotation}
187 * <li class='ja'>{@link org.apache.juneau.html.annotation.HtmlAnnotation}
188 * <li class='ja'>{@link org.apache.juneau.json.annotation.JsonAnnotation}
189 * <li class='ja'>{@link org.apache.juneau.annotation.SchemaAnnotation}
190 * <li class='ja'>{@link org.apache.juneau.msgpack.annotation.MsgPackAnnotation}
191 * <li class='ja'>{@link org.apache.juneau.oapi.annotation.OpenApiAnnotation}
192 * <li class='ja'>{@link org.apache.juneau.plaintext.annotation.PlainTextAnnotation}
193 * <li class='ja'>{@link org.apache.juneau.soap.annotation.SoapXmlAnnotation}
194 * <li class='ja'>{@link org.apache.juneau.uon.annotation.UonAnnotation}
195 * <li class='ja'>{@link org.apache.juneau.urlencoding.annotation.UrlEncodingAnnotation}
196 * <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlAnnotation}
197 * </ul>
198 *
199 * <p>
200 * The syntax for the <l>on()</l> pattern match parameter depends on whether it applies to a class, method, field, or constructor.
201 * The valid pattern matches are:
202 * <ul class='spaced-list'>
203 * <li>Classes:
204 * <ul>
205 * <li>Fully qualified:
206 * <ul>
207 * <li><js>"com.foo.MyClass"</js>
208 * </ul>
209 * <li>Fully qualified inner class:
210 * <ul>
211 * <li><js>"com.foo.MyClass$Inner1$Inner2"</js>
212 * </ul>
213 * <li>Simple:
214 * <ul>
215 * <li><js>"MyClass"</js>
216 * </ul>
217 * <li>Simple inner:
218 * <ul>
219 * <li><js>"MyClass$Inner1$Inner2"</js>
220 * <li><js>"Inner1$Inner2"</js>
221 * <li><js>"Inner2"</js>
222 * </ul>
223 * </ul>
224 * <li>Methods:
225 * <ul>
226 * <li>Fully qualified with args:
227 * <ul>
228 * <li><js>"com.foo.MyClass.myMethod(String,int)"</js>
229 * <li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js>
230 * <li><js>"com.foo.MyClass.myMethod()"</js>
231 * </ul>
232 * <li>Fully qualified:
233 * <ul>
234 * <li><js>"com.foo.MyClass.myMethod"</js>
235 * </ul>
236 * <li>Simple with args:
237 * <ul>
238 * <li><js>"MyClass.myMethod(String,int)"</js>
239 * <li><js>"MyClass.myMethod(java.lang.String,int)"</js>
240 * <li><js>"MyClass.myMethod()"</js>
241 * </ul>
242 * <li>Simple:
243 * <ul>
244 * <li><js>"MyClass.myMethod"</js>
245 * </ul>
246 * <li>Simple inner class:
247 * <ul>
248 * <li><js>"MyClass$Inner1$Inner2.myMethod"</js>
249 * <li><js>"Inner1$Inner2.myMethod"</js>
250 * <li><js>"Inner2.myMethod"</js>
251 * </ul>
252 * </ul>
253 * <li>Fields:
254 * <ul>
255 * <li>Fully qualified:
256 * <ul>
257 * <li><js>"com.foo.MyClass.myField"</js>
258 * </ul>
259 * <li>Simple:
260 * <ul>
261 * <li><js>"MyClass.myField"</js>
262 * </ul>
263 * <li>Simple inner class:
264 * <ul>
265 * <li><js>"MyClass$Inner1$Inner2.myField"</js>
266 * <li><js>"Inner1$Inner2.myField"</js>
267 * <li><js>"Inner2.myField"</js>
268 * </ul>
269 * </ul>
270 * <li>Constructors:
271 * <ul>
272 * <li>Fully qualified with args:
273 * <ul>
274 * <li><js>"com.foo.MyClass(String,int)"</js>
275 * <li><js>"com.foo.MyClass(java.lang.String,int)"</js>
276 * <li><js>"com.foo.MyClass()"</js>
277 * </ul>
278 * <li>Simple with args:
279 * <ul>
280 * <li><js>"MyClass(String,int)"</js>
281 * <li><js>"MyClass(java.lang.String,int)"</js>
282 * <li><js>"MyClass()"</js>
283 * </ul>
284 * <li>Simple inner class:
285 * <ul>
286 * <li><js>"MyClass$Inner1$Inner2()"</js>
287 * <li><js>"Inner1$Inner2()"</js>
288 * <li><js>"Inner2()"</js>
289 * </ul>
290 * </ul>
291 * <li>A comma-delimited list of anything on this list.
292 * </ul>
293 *
294 * <h5 class='section'>See Also:</h5><ul>
295 * <li class='ja'>{@link BeanConfig}
296 * </ul>
297 *
298 * @param values
299 * The annotations to register with the context.
300 * <br>Cannot contain <jk>null</jk> values.
301 * @return This object.
302 */
303 public Builder annotations(Annotation...values) {
304 assertArgNoNulls("values", values);
305 annotations(l(values));
306 return this;
307 }
308
309 /**
310 * Same as {@link #annotations(Annotation...)} but uses a list as input.
311 *
312 * @param values
313 * The annotations to register with the context.
314 * <br>Cannot be <jk>null</jk> or contain <jk>null</jk> values.
315 * @return This object.
316 */
317 public Builder annotations(List<Annotation> values) {
318 annotations.addAll(assertArgNoNulls("values", values));
319 return this;
320 }
321
322 /**
323 * Applies a set of applied to this builder.
324 *
325 * <p>
326 * An {@link AnnotationWork} consists of a single pair of {@link AnnotationInfo} that represents an annotation instance,
327 * and {@link AnnotationApplier} which represents the code used to apply the values in that annotation to a specific builder.
328 *
329 * <h5 class='section'>Example:</h5>
330 * <p class='bjava'>
331 * <jc>// A class annotated with a config annotation.</jc>
332 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>)
333 * <jk>public class</jk> MyClass {...}
334 *
335 * <jc>// Find all annotations that themselves are annotated with @ContextPropertiesApply.</jc>
336 * Stream<AnnotationInfo<?>> <jv>annotations</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getAnnotations().stream().filter(<jsf>CONTEXT_APPLY_FILTER</jsf>);
337 * VarResolverSession <jv>vrs</jv> = VarResolver.<jsf>DEFAULT</jsf>.createSession();
338 * AnnotationWorkList <jv>work</jv> = AnnotationWorkList.of(<jv>vrs</jv>, <jv>annotations</jv>);
339 *
340 * <jc>// Apply any settings found on the annotations.</jc>
341 * WriterSerializer <jv>serializer</jv> = JsonSerializer
342 * .<jsm>create</jsm>()
343 * .apply(<jv>work</jv>)
344 * .build();
345 * </p>
346 *
347 * @param work The list of annotations and appliers to apply to this builder.
348 * <br>Cannot be <jk>null</jk>.
349 * @return This object.
350 */
351 public Builder apply(AnnotationWorkList work) {
352 assertArgNotNull("work", work);
353 applied.addAll(work);
354 work.forEach(x -> builders.forEach(x::apply));
355 return this;
356 }
357
358 /**
359 * Same as {@link #applyAnnotations(Object...)} but explicitly specifies a class varargs to avoid compilation warnings.
360 *
361 * @param from The classes or methods on which the annotations are defined.
362 * <br>Cannot contain <jk>null</jk> values.
363 * @return This object.
364 */
365 public Builder applyAnnotations(Class<?>...from) {
366 assertArgNoNulls("from", from);
367 return applyAnnotations((Object[])from);
368 }
369
370 /**
371 * Applies any of the various <ja>@XConfig</ja> annotations on the specified classes or methods to this context.
372 *
373 * <p>
374 * Any annotations found that themselves are annotated with {@link ContextApply} will be resolved and
375 * applied as properties to this builder. These annotations include:
376 * <ul class='javatreec'>
377 * <li class ='ja'>{@link BeanConfig}
378 * <li class ='ja'>{@link CsvConfig}
379 * <li class ='ja'>{@link HtmlConfig}
380 * <li class ='ja'>{@link HtmlDocConfig}
381 * <li class ='ja'>{@link JsonConfig}
382 * <li class ='ja'>{@link JsonSchemaConfig}
383 * <li class ='ja'>{@link MsgPackConfig}
384 * <li class ='ja'>{@link OpenApiConfig}
385 * <li class ='ja'>{@link ParserConfig}
386 * <li class ='ja'>{@link PlainTextConfig}
387 * <li class ='ja'>{@link SerializerConfig}
388 * <li class ='ja'>{@link SoapXmlConfig}
389 * <li class ='ja'>{@link UonConfig}
390 * <li class ='ja'>{@link UrlEncodingConfig}
391 * <li class ='ja'>{@link XmlConfig}
392 * <li class ='ja'><c>RdfConfig</c>
393 * </ul>
394 *
395 * <p>
396 * Annotations on classes are appended in the following order:
397 * <ol>
398 * <li>On the package of this class.
399 * <li>On interfaces ordered parent-to-child.
400 * <li>On parent classes ordered parent-to-child.
401 * <li>On this class.
402 * </ol>
403 *
404 * <p>
405 * Annotations on methods are appended in the following order:
406 * <ol>
407 * <li>On the package of the method class.
408 * <li>On interfaces ordered parent-to-child.
409 * <li>On parent classes ordered parent-to-child.
410 * <li>On the method class.
411 * <li>On this method and matching methods ordered parent-to-child.
412 * </ol>
413 *
414 * <p>
415 * The default var resolver {@link VarResolver#DEFAULT} is used to resolve any variables in annotation field values.
416 *
417 * <h5 class='section'>Example:</h5>
418 * <p class='bjava'>
419 * <jc>// A class annotated with a config annotation.</jc>
420 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>)
421 * <jk>public class</jk> MyClass {...}
422 *
423 * <jc>// Apply any settings found on the annotations.</jc>
424 * WriterSerializer <jv>serializer</jv> = JsonSerializer
425 * .<jsm>create</jsm>()
426 * .applyAnnotations(MyClass.<jk>class</jk>)
427 * .build();
428 *
429 * <jc>// A method annotated with a config annotation.</jc>
430 * <jk>public class</jk> MyClass {
431 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>)
432 * <jk>public void</jk> myMethod() {...}
433 * }
434 *
435 * <jc>// Apply any settings found on the annotations.</jc>
436 * WriterSerializer <jv>serializer</jv> = JsonSerializer
437 * .<jsm>create</jsm>()
438 * .applyAnnotations(MyClass.<jk>class</jk>.getMethod(<js>"myMethod"</js>))
439 * .build();
440 * </p>
441 *
442 * @param from The classes or methods on which the annotations are defined.
443 * Can be any of the following types:
444 * <ul>
445 * <li>{@link Class}
446 * <li>{@link ClassInfo}
447 * <li>{@link Method}
448 * <li>{@link MethodInfo}
449 * <li>A collection/stream/array of anything on this list.
450 * <br>Cannot contain <jk>null</jk> values.
451 * @return This object.
452 */
453 public Builder applyAnnotations(Object...from) {
454 assertArgNoNulls("from", from);
455 var work = AnnotationWorkList.create();
456 Arrays.stream(from).forEach(x -> traverse(work, x));
457 return apply(work);
458 }
459
460 /**
461 * Returns this builder cast to the specified subtype if it is an instance of that type.
462 *
463 * <p>
464 * This is a type-safe way to check if this builder is an instance of a specific builder subtype
465 * and cast it accordingly. Returns an empty {@link Optional} if this builder is not an instance
466 * of the specified subtype.
467 *
468 * <h5 class='section'>Example:</h5>
469 * <p class='bjava'>
470 * Builder <jv>b</jv> = JsonSerializer.<jsm>create</jsm>();
471 * Optional<JsonSerializer.Builder> <jv>jsonBuilder</jv> = <jv>b</jv>.asSubtype(JsonSerializer.Builder.<jk>class</jk>);
472 * <jk>if</jk> (<jv>jsonBuilder</jv>.isPresent()) {
473 * <jc>// Use JsonSerializer.Builder-specific methods</jc>
474 * <jv>jsonBuilder</jv>.get().pretty();
475 * }
476 * </p>
477 *
478 * @param <T> The builder subtype.
479 * @param subtype The builder subtype class to cast to.
480 * <br>Cannot be <jk>null</jk>.
481 * @return An {@link Optional} containing this builder cast to the subtype, or empty if not an instance.
482 */
483 public <T extends Builder> Optional<T> asSubtype(Class<T> subtype) {
484 return opt(assertArgNotNull("subtype", subtype).isInstance(this) ? subtype.cast(this) : null);
485 }
486
487 /**
488 * Build the object.
489 *
490 * @return The built object.
491 */
492 public Context build() {
493 return innerBuild();
494 }
495
496 /**
497 * Convenience method for calling {@link #build()} while avoiding a cast.
498 *
499 * @param <T> The type to cast the built object to.
500 * @param c The type to cast the built object to.
501 * <br>Cannot be <jk>null</jk>.
502 * @return The built context bean.
503 */
504 @SuppressWarnings("unchecked")
505 public final <T extends Context> T build(Class<T> c) {
506 if (type == null || ! assertArgNotNull("c", c).isAssignableFrom(type))
507 type = c;
508 return (T)innerBuild();
509 }
510
511 /**
512 * Specifies a cache to use for hashkey-based caching.
513 *
514 * <p>
515 * When a cache is specified, contexts with the same hash key will be reused from the cache
516 * instead of creating new instances. This improves performance when building multiple contexts
517 * with identical configurations.
518 *
519 * <p>
520 * If <jk>null</jk> is specified, caching is disabled and each call to {@link #build()} will
521 * create a new context instance.
522 *
523 * @param value The cache.
524 * <br>Can be <jk>null</jk> (disables caching, each build creates a new instance).
525 * @return This object.
526 */
527 public Builder cache(Cache<HashKey,? extends Context> value) {
528 cache = value;
529 return this;
530 }
531
532 /**
533 * Returns <jk>true</jk> if any of the annotations/appliers can be applied to this builder.
534 *
535 * @param work The work to check.
536 * <br>Cannot be <jk>null</jk>.
537 * @return <jk>true</jk> if any of the annotations/appliers can be applied to this builder.
538 */
539 public boolean canApply(AnnotationWorkList work) {
540 return assertArgNotNull("work", work).stream().anyMatch(x -> builders.stream().anyMatch(b -> x.canApply(b)));
541 }
542
543 /**
544 * Copy creator.
545 *
546 * @return A new mutable copy of this builder.
547 */
548 public abstract Builder copy();
549
550 /**
551 * <i><l>Context</l> configuration property: </i> Debug mode.
552 *
553 * <p>
554 * Enables the following additional information during serialization:
555 * <ul class='spaced-list'>
556 * <li>
557 * When bean getters throws exceptions, the exception includes the object stack information
558 * in order to determine how that method was invoked.
559 * <li>
560 * Enables {@link BeanTraverseContext.Builder#detectRecursions()}.
561 * </ul>
562 *
563 * <p>
564 * Enables the following additional information during parsing:
565 * <ul class='spaced-list'>
566 * <li>
567 * When bean setters throws exceptions, the exception includes the object stack information
568 * in order to determine how that method was invoked.
569 * </ul>
570 *
571 * <h5 class='section'>Example:</h5>
572 * <p class='bjava'>
573 * <jc>// Create a serializer with debug enabled.</jc>
574 * WriterSerializer <jv>serializer</jv> = JsonSerializer
575 * .<jsm>create</jsm>()
576 * .debug()
577 * .build();
578 *
579 * <jc>// Create a POJO model with a recursive loop.</jc>
580 * <jk>public class</jk> MyBean {
581 * <jk>public</jk> Object <jf>f</jf>;
582 * }
583 * MyBean <jv>bean</jv> = <jk>new</jk> MyBean();
584 * <jv>bean</jv>.<jf>f</jf> = <jv>bean</jv>;
585 *
586 * <jc>// Throws a SerializeException and not a StackOverflowError</jc>
587 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jv>bean</jv>);
588 * </p>
589 *
590 * <h5 class='section'>See Also:</h5><ul>
591 * <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#debug()}
592 * <li class='jm'>{@link org.apache.juneau.ContextSession.Builder#debug(Boolean)}
593 * </ul>
594 *
595 * @return This object.
596 */
597 public Builder debug() {
598 return debug(true);
599 }
600
601 /**
602 * Same as {@link #debug()} but allows you to explicitly specify the value.
603 *
604 * @param value The value for this setting.
605 * @return This object.
606 */
607 public Builder debug(boolean value) {
608 debug = value;
609 return this;
610 }
611
612 /**
613 * Returns all the annotations that have been applied to this builder.
614 *
615 * @return All the annotations that have been applied to this builder.
616 */
617 public AnnotationWorkList getApplied() { return applied; }
618
619 /**
620 * Returns the context class that this builder should create.
621 *
622 * @return The context class if it was specified.
623 */
624 public Optional<Class<?>> getType() { return opt(type); }
625
626 /**
627 * Returns the hashkey of this builder.
628 *
629 * <p>
630 * Used to return previously instantiated context beans that have matching hashkeys.
631 * The {@link HashKey} object is suitable for use as a hashmap key of a map of context beans.
632 * A context bean is considered equivalent if the {@link HashKey#equals(Object)} method is the same.
633 *
634 * @return The hashkey of this builder.
635 */
636 public HashKey hashKey() {
637 return HashKey.of(debug, type, annotations);
638 }
639
640 /**
641 * Specifies a pre-instantiated bean for the {@link #build()} method to return.
642 *
643 * <p>
644 * If a non-null value is provided and it's an instance of the context type, {@link #build()} will return
645 * that instance instead of creating a new one. If <jk>null</jk>, the normal build process continues.
646 *
647 * @param value The value for this setting.
648 * <br>Can be <jk>null</jk> (normal build process will continue).
649 * @return This object.
650 */
651 public Builder impl(Context value) {
652 impl = value;
653 return this;
654 }
655
656 /**
657 * Returns <jk>true</jk> if debug is enabled.
658 *
659 * @return <jk>true</jk> if debug is enabled.
660 */
661 public boolean isDebug() { return debug; }
662
663 /**
664 * Associates a context class with this builder.
665 *
666 * <p>
667 * This is the type of object that this builder creates when the {@link #build()} method is called.
668 *
669 * <p>
670 * By default, it's the outer class of where the builder class is defined.
671 *
672 * <p>
673 * If <jk>null</jk> is set, {@link #build()} will throw an exception. The default constructor automatically
674 * sets this to the outer class, so <jk>null</jk> should only be set explicitly if you want to override the default.
675 *
676 * @param value The context class that this builder should create.
677 * <br>Can be <jk>null</jk> (will cause {@link #build()} to throw an exception).
678 * @return This object.
679 */
680 public Builder type(Class<? extends Context> value) {
681 type = value;
682 return this;
683 }
684
685 /**
686 * Registers the specified secondary builders with this context builder.
687 *
688 * <p>
689 * When {@link #apply(AnnotationWorkList)} is called, it gets called on all registered builders.
690 *
691 * @param values The builders to add to the list of builders.
692 * <br>Cannot contain <jk>null</jk> values.
693 */
694 protected void registerBuilders(Object...values) {
695 assertArgNoNulls("values", values);
696 for (var b : values) {
697 if (b == this)
698 builders.add(b);
699 else if (b instanceof Builder b2)
700 builders.addAll(b2.builders);
701 else
702 builders.add(b);
703 }
704 }
705
706 private ConstructorInfo getContextConstructor() {
707 return CONTEXT_CONSTRUCTORS.get(type, getClass());
708 }
709
710 private Context innerBuild() {
711 if (type == null)
712 throw rex("Type not specified for context builder {0}", cn(getClass()));
713 if (nn(impl) && type.isInstance(impl))
714 return type.cast(impl);
715 if (nn(cache))
716 return cache.get(hashKey(), () -> getContextConstructor().newInstance(this));
717 return getContextConstructor().newInstance(this);
718 }
719 }
720
721 /*
722 * Cache of static <c>create</c> methods that return builder instances for context classes.
723 *
724 * <p>
725 * This cache stores {@link MethodInfo} objects for public static methods named <c>create</c> that return
726 * builder objects. The methods are discovered by:
727 * <ol>
728 * <li>Finding public constructors that take a single parameter (the builder type)
729 * <li>Looking for a matching static <c>create</c> method that returns the builder type
730 * <li>Caching the result for future lookups
731 * </ol>
732 *
733 * <p>
734 * Used by {@link #createBuilder(Class)} to efficiently locate and invoke builder creation methods.
735 *
736 * @see #createBuilder(Class)
737 */
738 private static final Cache<Class<?>,MethodInfo> BUILDER_CREATE_METHODS = Cache.<Class<?>,MethodInfo>create()
739 .supplier(type -> {
740 var c = info(type);
741 // @formatter:off
742 return c.getPublicConstructors().stream()
743 .filter(ci -> ci.hasNumParameters(1) && ! ci.getParameter(0).getParameterType().is(type))
744 .map(ci -> c.getPublicMethod(
745 x -> x.isStatic()
746 && x.isNotDeprecated()
747 && x.hasName("create")
748 && x.hasReturnType(ci.getParameter(0).getParameterType())
749 ).orElse(null))
750 .filter(Objects::nonNull)
751 .findFirst()
752 .orElseThrow(() -> rex("Could not find builder create method on class {0}", cn(type)));
753 // @formatter:on
754 })
755 .build();
756
757
758 /*
759 * Cache of public constructors on context classes that accept builder instances.
760 *
761 * <p>
762 * This cache stores {@link ConstructorInfo} objects for public constructors on context classes that take
763 * a single parameter of the builder type. The constructor is discovered by:
764 * <ol>
765 * <li>Finding public constructors on the context type that take exactly one parameter
766 * <li>Matching constructors where the parameter type is a parent of (or equal to) the builder type
767 * <li>Caching the result for future lookups
768 * </ol>
769 *
770 * <p>
771 * Used by {@link Builder#getContextConstructor()} to efficiently locate and invoke context constructors
772 * when building context instances from builders.
773 *
774 * @see Builder#getContextConstructor()
775 * @see Builder#innerBuild()
776 */
777 private static final Cache2<Class<? extends Context>,Class<? extends Builder>,ConstructorInfo> CONTEXT_CONSTRUCTORS = Cache2.<Class<? extends Context>,Class<? extends Builder>,ConstructorInfo>create()
778 .supplier((cacheType, builderType) -> {
779 var ct = info(cacheType);
780 var bt = info(builderType);
781 return ct
782 .getPublicConstructor(x -> x.hasNumParameters(1) && x.getParameter(0).getParameterType().isParentOf(builderType))
783 .orElseThrow(() -> rex("Public constructor not found: {0}({1})", ct.getName(), bt.getName()));
784 })
785 .build();
786
787 /*
788 * Default annotation provider instance for finding annotations on classes, methods, fields, and constructors.
789 *
790 * <p>
791 * This is a static reference to {@link AnnotationProvider#INSTANCE}, used by the {@link Builder#traverse(AnnotationWorkList, Object)}
792 * method to discover annotations that can be applied to context builders.
793 *
794 * <p>
795 * The annotation provider supports:
796 * <ul>
797 * <li>Finding annotations on classes, methods, fields, and constructors
798 * <li>Traversing class hierarchies (parent-to-child or child-to-parent order)
799 * <li>Supporting runtime annotations (annotations added programmatically)
800 * <li>Caching results for performance
801 * </ul>
802 *
803 * @see AnnotationProvider
804 * @see Builder#traverse(AnnotationWorkList, Object)
805 */
806 private static final AnnotationProvider AP = AnnotationProvider.INSTANCE;
807
808 /**
809 * Predicate for annotations that themselves are annotated with {@link ContextApply}.
810 */
811 public static final Predicate<AnnotationInfo<?>> CONTEXT_APPLY_FILTER = x -> x.hasAnnotation(ContextApply.class);
812
813 /**
814 * Instantiates a builder of the specified context class.
815 *
816 * <p>
817 * Looks for a public static method called <c>create</c> that returns an object that can be passed into a public
818 * or protected constructor of the class.
819 *
820 * @param type The builder to create.
821 * @return A new builder.
822 */
823 public static Builder createBuilder(Class<? extends Context> type) {
824 assertArgNotNull("type", type);
825 try {
826 return ((Builder)BUILDER_CREATE_METHODS.get(type).invoke(null)).type(type);
827 } catch (ExecutableException e) {
828 throw toRex(e);
829 }
830 }
831
832 private static AnnotationWorkList traverse(AnnotationWorkList work, Object x) {
833 var ap = AP;
834 CollectionUtils.traverse(x, y -> {
835 if (x instanceof Class<?> x2)
836 work.add(rstream(ap.find(info(x2))).filter(CONTEXT_APPLY_FILTER));
837 else if (x instanceof ClassInfo x2)
838 work.add(rstream(ap.find(x2)).filter(CONTEXT_APPLY_FILTER));
839 else if (x instanceof Method x2)
840 work.add(rstream(ap.find(info(x2))).filter(CONTEXT_APPLY_FILTER));
841 else if (x instanceof MethodInfo x2)
842 work.add(rstream(ap.find(x2)).filter(CONTEXT_APPLY_FILTER));
843 else
844 illegalArg("Invalid type passed to applyAnnotations: {0}", cn(x));
845 });
846 return work;
847 }
848
849 private final AnnotationProvider annotationProvider;
850 private final boolean debug;
851 private final List<Annotation> annotations;
852
853 /**
854 * Constructor for this class.
855 *
856 * @param builder The builder for this class.
857 * <br>Cannot be <jk>null</jk>.
858 */
859 protected Context(Builder builder) {
860 assertArgNotNull("builder", builder);
861 init(builder);
862 annotations = copyOf(builder.annotations);
863 annotationProvider = AnnotationProvider.create().addRuntimeAnnotations(annotations).build();
864 debug = builder.debug;
865 }
866
867 /**
868 * Copy constructor.
869 *
870 * @param copyFrom The context to copy from.
871 */
872 protected Context(Context copyFrom) {
873 annotationProvider = copyFrom.annotationProvider;
874 annotations = copyOf(copyFrom.annotations);
875 debug = copyFrom.debug;
876 }
877
878 /**
879 * Creates a builder from this context object.
880 *
881 * <p>
882 * Builders are used to define new contexts (e.g. serializers, parsers) based on existing configurations.
883 *
884 * @return A new Builder object.
885 */
886 public Builder copy() {
887 throw unsupportedOp();
888 }
889
890 /**
891 * Create a session builder based on the properties defined on this context.
892 *
893 * <p>
894 * Use this method for creating sessions where you want to override basic settings.
895 * Otherwise, use {@link #getSession()} directly.
896 *
897 * @return A new session builder.
898 */
899 public ContextSession.Builder createSession() {
900 throw unsupportedOp();
901 }
902
903 /**
904 * Returns the annotation provider for this context.
905 *
906 * @return The annotation provider for this context.
907 */
908 public AnnotationProvider getAnnotationProvider() { return annotationProvider; }
909
910 /**
911 * Returns a session to use for this context.
912 *
913 * <p>
914 * Note that subclasses may opt to return a reusable non-modifiable session.
915 *
916 * @return A new session object.
917 */
918 public ContextSession getSession() { return createSession().build(); }
919
920 /**
921 * Debug mode.
922 *
923 * @see Context.Builder#debug()
924 * @return
925 * <jk>true</jk> if debug mode is enabled.
926 */
927 public boolean isDebug() { return debug; }
928
929 @Override /* Overridden from Object */
930 public String toString() {
931 return r(properties());
932 }
933
934 /**
935 * Perform optional initialization on builder before it is used.
936 *
937 * <p>
938 * Default behavior is a no-op.
939 *
940 * @param builder The builder to initialize.
941 */
942 protected void init(Builder builder) {}
943
944 /**
945 * Returns the properties on this bean as a map for debugging.
946 *
947 * @return The properties on this bean as a map for debugging.
948 */
949 protected FluentMap<String,Object> properties() {
950 return filteredBeanPropertyMap()
951 .a("annotations", annotations)
952 .a("debug", debug);
953 }
954 }