001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau; 018 019import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 020import static org.apache.juneau.commons.utils.AssertionUtils.*; 021import static org.apache.juneau.commons.utils.CollectionUtils.*; 022import static org.apache.juneau.commons.utils.ThrowableUtils.*; 023import static org.apache.juneau.commons.utils.Utils.*; 024 025import java.lang.annotation.*; 026import java.lang.reflect.*; 027import java.util.*; 028import java.util.function.*; 029import org.apache.juneau.annotation.*; 030import org.apache.juneau.commons.collections.*; 031import org.apache.juneau.commons.reflect.*; 032import org.apache.juneau.commons.utils.*; 033import org.apache.juneau.csv.annotation.*; 034import org.apache.juneau.html.annotation.*; 035import org.apache.juneau.json.annotation.*; 036import org.apache.juneau.jsonschema.annotation.*; 037import org.apache.juneau.msgpack.annotation.*; 038import org.apache.juneau.oapi.annotation.*; 039import org.apache.juneau.parser.annotation.*; 040import org.apache.juneau.plaintext.annotation.*; 041import org.apache.juneau.serializer.annotation.*; 042import org.apache.juneau.soap.annotation.*; 043import org.apache.juneau.svl.*; 044import org.apache.juneau.uon.annotation.*; 045import org.apache.juneau.urlencoding.annotation.*; 046import org.apache.juneau.xml.annotation.*; 047 048/** 049 * Base class for all Context beans. 050 * 051 * <p> 052 * Context beans follow the convention of havinTg the following parts: 053 * <ul> 054 * <li>A {@link Builder} class for configuring the context bean. 055 * <ul> 056 * <li>This bean is non-thread-safe and meant for one-time use. 057 * </ul> 058 * <li>A {@link Context#Context(Builder)} constructor that takes in a builder object. 059 * <ul> 060 * <li>This bean is thread-safe and cacheable/reusable. 061 * </ul> 062 * <li>A {@link ContextSession} class for doing work. 063 * <ul> 064 * <li>This bean is non-thread-safe and meant for one-time use. 065 * </ul> 066 * 067 * <h5 class='section'>Notes:</h5><ul> 068 * <li class='note'>This class is thread safe and reusable. 069 * </ul> 070 * 071 */ 072public abstract class Context { 073 074 /** 075 * Builder class. 076 */ 077 public abstract static class Builder { 078 079 private boolean debug; 080 private final AnnotationWorkList applied = AnnotationWorkList.create(); 081 private Cache<HashKey,? extends Context> cache; 082 private Class<? extends Context> type; 083 private Context impl; 084 private List<Annotation> annotations; 085 private final List<Object> builders = list(); 086 087 /** 088 * Constructor. 089 * Default settings. 090 */ 091 @SuppressWarnings("unchecked") 092 protected Builder() { 093 debug = env("Context.debug", false); 094 annotations = list(); 095 registerBuilders(this); 096 097 // By default, the type being created should be the class declaring the builder. 098 var dc = getClass().getDeclaringClass(); 099 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}