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.commons.inject;
018
019import static java.util.Collections.*;
020import static java.util.Comparator.*;
021import static org.apache.juneau.commons.function.Suppliers.*;
022import static org.apache.juneau.commons.reflect.ElementFlag.*;
023import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
024import static org.apache.juneau.commons.utils.CollectionUtils.*;
025import static org.apache.juneau.commons.utils.Shorts.*;
026
027import java.util.*;
028import java.util.function.*;
029
030import org.apache.juneau.commons.concurrent.*;
031import org.apache.juneau.commons.function.*;
032import org.apache.juneau.commons.logging.*;
033import org.apache.juneau.commons.reflect.*;
034
035/**
036 * Utility class for creating beans through constructors, creator methods, and builders.
037 *
038 * <p>
039 * Uses a {@link BeanStore} to find available ways to construct beans via injection of beans from the store.
040 *
041 * <p>
042 * This class is instantiated through the following method:
043 * <ul class='javatree'>
044 *    <li class='jm'>{@link BeanInstantiator#of(Class)}
045 * </ul>
046 *
047 * <h5 class='section'>Example:</h5>
048 * <p class='bjava'>
049 *    <jc>// Bean with builder pattern</jc>
050 *    <jk>public class</jk> MyBean {
051 *       <jk>private final</jk> String <jf>value</jf>;
052 *
053 *       <jk>private</jk> MyBean(String <jv>value</jv>) {
054 *          <jk>this</jk>.<jf>value</jf> = <jv>value</jv>;
055 *       }
056 *
057 *       <jk>public static</jk> Builder <jsm>create</jsm>() {
058 *          <jk>return new</jk> Builder();
059 *       }
060 *
061 *       <jk>public static class</jk> Builder {
062 *          <jk>private</jk> String <jf>value</jf>;
063 *
064 *          <jk>public</jk> Builder value(String <jv>value</jv>) {
065 *             <jk>this</jk>.<jf>value</jf> = <jv>value</jv>;
066 *             <jk>return this</jk>;
067 *          }
068 *
069 *          <jk>public</jk> MyBean build() {
070 *             <jk>return new</jk> MyBean(<jf>value</jf>);
071 *          }
072 *       }
073 *
074 *    <jc>// Builder with @Inject on build() method</jc>
075 *    <jk>public class</jk> MyBean2 {
076 *       <jk>private final</jk> String <jf>value</jf>;
077 *       <jk>private final</jk> MyDependency <jf>dependency</jf>;
078 *
079 *       <jk>private</jk> MyBean2(String <jv>value</jv>, MyDependency <jv>dependency</jv>) {
080 *          <jk>this</jk>.<jf>value</jf> = <jv>value</jv>;
081 *          <jk>this</jk>.<jf>dependency</jf> = <jv>dependency</jv>;
082 *       }
083 *
084 *       <jk>public static class</jk> Builder {
085 *          <jk>private</jk> String <jf>value</jf>;
086 *
087 *          <jk>public</jk> Builder value(String <jv>value</jv>) {
088 *             <jk>this</jk>.<jf>value</jf> = <jv>value</jv>;
089 *             <jk>return this</jk>;
090 *          }
091 *
092 *          <ja>@Inject</ja>
093 *          <jk>public</jk> MyBean2 build(MyDependency <jv>dependency</jv>) {
094 *             <jk>return new</jk> MyBean2(<jv>value</jv>, <jv>dependency</jv>);
095 *          }
096 *       }
097 *    }
098 *    }
099 *
100 *    <jc>// Create bean using BeanInstantiator</jc>
101 *    MyBean <jv>bean</jv> = BeanInstantiator
102 *       .<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>myBeanStore</jv>)
103 *       .run();
104 * </p>
105 *
106 * <h5 class='section'>Bean Creation Order:</h5>
107 * <p>
108 * The creator attempts to instantiate beans in the following order:
109 * <ol class='spaced-list'>
110 *    <li><b>Using a Builder</b> (if a builder is found or specified):
111 *       <ol>
112 *          <li>Calls a <c>build()</c>/<c>create()</c>/<c>get()</c> method on the builder that returns the bean type.
113 *             <ul>
114 *                <li>The method is <b>required</b> - if no such method exists, bean creation fails.
115 *                <li>If the method has <c>@Inject</c> or <c>@Autowired</c> annotation, its parameters are resolved from the bean store.
116 *                <li>If the method does not have <c>@Inject</c> annotation, it must have no parameters.
117 *             </ul>
118 *       </ol>
119 *    <li><b>Without a Builder</b>:
120 *       <ol>
121 *          <li>Calls a static no-arg <c>getInstance()</c> method on the bean class.
122 *          <li>Calls a public constructor with parameters that can be resolved from the bean store.
123 *          <li>Falls back to a protected constructor with parameters that can be resolved from the bean store.
124 *          <li>Falls back to a package-private constructor with parameters that can be resolved from the bean store.
125 *             Private constructors are deliberately excluded.
126 *       </ol>
127 * </ol>
128 *
129 * <h5 class='section'>Builder Detection:</h5>
130 * <p>
131 * Builders are detected in the following priority order:
132 * <ol class='spaced-list'>
133 *    <li>Explicitly set via {@link Builder#builder(Object)} or {@link Builder#builder(Class)}.
134 *    <li>{@link org.apache.juneau.commons.Builder @Builder} annotation on the bean subtype.
135 *    <li>{@link org.apache.juneau.commons.Builder @Builder} annotation on the bean type.
136 *    <li>Auto-detection:
137 *       <ul>
138 *          <li>Static <c>create()</c> or <c>builder()</c> method that returns a builder type
139 *             (method names can be customized via {@link Builder#builderMethodNames(String...)}).
140 *          <li>Inner class named <c>Builder</c>.
141 *       </ul>
142 * </ol>
143 *
144 * <h5 class='section'>Dependency Injection:</h5>
145 * <p>
146 * Both builder instances and the final bean have automatic dependency injection performed via
147 * {@link ClassInfo#inject(Object, BeanStore)}, which processes {@code @Inject} and {@code @Autowired} annotations
148 * on fields and methods.
149 *
150 * <p>
151 * This means that after a bean is created, all of its fields and methods annotated with {@code @Inject} or {@code @Autowired}
152 * will automatically be populated with beans from the bean store.
153 *
154 * <h5 class='section'>Caching:</h5>
155 * <p>
156 * By default, each call to {@link #run()} creates a new bean instance. To enable caching, call {@link Builder#cached()}.
157 * When caching is enabled, bean instances are cached using {@link Memoizer}, so multiple calls to {@link #run()}
158 * will return the same instance unless {@link #reset()} is called or configuration changes. Explicit implementations set via
159 * {@link Builder#impl(Object)} and explicit builder instances set via {@link Builder#builder(Object)} are preserved
160 * across resets.
161 *
162 * <h5 class='section'>Thread Safety:</h5>
163 * <p>
164 * Configuration is performed on the inner {@link Builder} which is <b>not</b> thread-safe. Configure on a single
165 * thread, then call {@link Builder#build()} to produce an immutable {@link BeanInstantiator} that is safe for
166 * shared concurrent use. The {@link Builder#run()} shorthand is equivalent to {@code build().run()} and is
167 * appropriate for one-shot bean creation from a single thread.
168 *
169 * <h5 class='section'>Notes:</h5><ul>
170 *    <li class='note'>Builders must have a <c>build()</c>, <c>create()</c>, or <c>get()</c> method that returns the bean type.
171 *       This method is required - constructors accepting builders are no longer supported.
172 *    <li class='note'>The builder's <c>build()</c>/<c>create()</c>/<c>get()</c> method may have <c>@Inject</c> or <c>@Autowired</c>
173 *       annotation to allow dependency injection of parameters from the bean store.
174 *    <li class='note'>If the builder's <c>build()</c>/<c>create()</c>/<c>get()</c> method does not have <c>@Inject</c> annotation,
175 *       it must have no parameters.
176 *    <li class='note'>All constructors and methods except {@link Optional} and {@link List} parameters
177 *       must have beans available in the store.
178 *    <li class='note'>If multiple constructors/methods are found, the one with the most matching parameters is used.
179 *    <li class='note'>Deprecated and <a class="doclink" href="https://juneau.apache.org/site/apidocs/org/apache/juneau/commons/bean/BeanIgnore.html">@BeanIgnore</a>-annotated methods/constructors are ignored.
180 * </ul>
181 *
182 * <h5 class='section'>See Also:</h5><ul>
183 *    <li class='jc'>{@link BasicBeanStore}
184 * </ul>
185 *
186 * @param <T> The bean type being created.
187 */
188@SuppressWarnings({
189   "java:S115", // Constants use UPPER_snakeCase convention
190   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
191   "java:S3776", // findBeanImpl() nests fallbacks across instantiation strategies; splitting it would obscure the ordering
192   "java:S6539", // Monster class; BeanInstantiator intentionally centralizes multi-strategy bean-instantiation auto-wiring
193   "java:S6541", // findBeanImpl() tries subtype, builder, factory-method and constructor strategies in sequence, so it is one long method by design
194   "resource", // transient build-time scratch store; lifetime is bounded by the Builder itself, no foreign resources are captured
195   "unchecked" // (T) beanType.cast(...) results and the (Class<? extends T>) narrowing in type() are guarded by the runtime beanType check
196})
197public class BeanInstantiator<T> {
198
199   private static final String CONST_usingFallbackSupplier = "Using fallback supplier";
200
201   /** Default factory method names used for bean instantiation. */
202   protected static final Set<String> DEFAULT_FACTORY_METHOD_NAMES = u(set("getInstance"));
203
204   /** Default builder method names used for builder factory methods (static methods that create/return a builder). */
205   protected static final Set<String> DEFAULT_BUILDER_METHOD_NAMES = u(set("create", "builder"));
206
207   /** Default build method names used for builder build methods (instance methods on builder that build/return the bean). */
208   protected static final Set<String> DEFAULT_BUILD_METHOD_NAMES = u(set("build", "create", "get"));
209
210   /** Default builder class names for auto-detection. */
211   protected static final Set<String> DEFAULT_BUILDER_CLASS_NAMES = u(set("Builder"));
212
213   private static final RichLogger logger = RichLogger.getLogger(BeanInstantiator.class);
214
215   /**
216    * Lifecycle scope for the bean being created.
217    *
218    * <p>
219    * Maps to {@link Builder#cached()} so that {@link Builder#scope(Scope)} provides a more declarative alternative.
220    *
221    * @see Builder#scope(Scope)
222    */
223   public enum Scope {
224      /** A new bean instance is produced on every call to {@link Builder#run()}. */
225      PROTOTYPE,
226      /** A single bean instance is created and reused for the lifetime of the builder. */
227      SINGLETON
228   }
229
230   /**
231    * Creates a new bean creator builder.
232    *
233    * @param <T> The bean type to create.
234    * @param beanType The bean type to create.  Must not be <jk>null</jk>.
235    * @return A new bean creator builder.
236    */
237   public static <T> Builder<T> of(Class<T> beanType) {
238      return new Builder<>(beanType, null, null, null);
239   }
240
241   /**
242    * Creates a new bean creator builder with a parent bean store.
243    *
244    * @param <T> The bean type to create.
245    * @param beanType The bean type to create.  Must not be <jk>null</jk>.
246    * @param parentStore The parent bean store to use for resolving dependencies. Can be <jk>null</jk> (the internal bean store then has no parent fallback).
247    * @return A new bean creator builder.
248    */
249   public static <T> Builder<T> of(Class<T> beanType, BeanStore parentStore) {
250      return new Builder<>(beanType, parentStore, null, null);
251   }
252
253   /**
254    * Creates a new bean creator builder with a parent bean store, name, and enclosing instance.
255    *
256    * @param <T> The bean type to create.
257    * @param beanType The bean type to create.  Must not be <jk>null</jk>.
258    * @param parentStore The parent bean store to use for resolving dependencies. Can be <jk>null</jk> (the internal bean store then has no parent fallback).
259    * @param name The bean name. Can be <jk>null</jk> for unnamed beans.
260    * @param enclosingInstance The enclosing instance object. Can be <jk>null</jk> (the outer-instance skip rule is then never applied).
261    * @return A new bean creator builder.
262    */
263   public static <T> Builder<T> of(Class<T> beanType, BeanStore parentStore, String name, Object enclosingInstance) {
264      return new Builder<>(beanType, parentStore, name, enclosingInstance);
265   }
266
267   /**
268    * Instantiates a bean of the given type, or returns <jk>null</jk> if the type is <jk>null</jk> or instantiation fails.
269    *
270    * <p>
271    * Shorthand for the common pattern:
272    * <p class='bjava'>
273    *    <jv>cls</jv> == <jk>null</jk> ? <jk>null</jk> : BeanInstantiator.<jsm>of</jsm>(<jv>cls</jv>).fallback(() -> <jk>null</jk>).run()
274    * </p>
275    *
276    * @param <T> The bean type to create.
277    * @param beanType The bean type to create.  Can be <jk>null</jk>.
278    * @return A new bean instance, or <jk>null</jk> if the type is <jk>null</jk> or instantiation fails.
279    */
280   public static <T> T createOrNull(Class<T> beanType) {
281      return beanType == null ? null : of(beanType).fallback(() -> null).run();
282   }
283
284   /**
285    * Instantiates a bean of the given type and wraps it in an {@link Optional}, or returns {@link Optional#empty()}
286    * if the type is <jk>null</jk> or instantiation fails.
287    *
288    * <p>
289    * Shorthand for the common pattern:
290    * <p class='bjava'>
291    *    <jv>cls</jv> == <jk>null</jk> ? Optional.empty() : BeanInstantiator.<jsm>of</jsm>(<jv>cls</jv>).asOptional()
292    * </p>
293    *
294    * @param <T> The bean type to create.
295    * @param beanType The bean type to create.  Can be <jk>null</jk>.
296    * @return An {@link Optional} containing the created bean, or {@link Optional#empty()} if the type is <jk>null</jk> or instantiation fails.
297    */
298   public static <T> Optional<T> optionalOf(Class<T> beanType) {
299      return beanType == null ? oe() : of(beanType).asOptional();
300   }
301
302   /**
303    * Instantiates a bean of the given type, or returns a default value if the type is <jk>null</jk>.
304    *
305    * <p>
306    * Shorthand for the common pattern:
307    * <p class='bjava'>
308    *    <jv>cls</jv> == <jk>null</jk> ? <jv>defaultValue</jv> : BeanInstantiator.<jsm>of</jsm>(<jv>cls</jv>).run()
309    * </p>
310    *
311    * @param <T> The bean type to create.
312    * @param beanType The bean type to create.  Can be <jk>null</jk>.
313    * @param defaultValue The value to return if {@code beanType} is <jk>null</jk>.  Can be <jk>null</jk>.
314    * @return A new bean instance, or {@code defaultValue} if the type is <jk>null</jk>.
315    */
316   public static <T> T createOrDefault(Class<T> beanType, T defaultValue) {
317      return beanType == null ? defaultValue : of(beanType).run();
318   }
319
320   // =========================================================================
321   // Outer (immutable) wrapper state and methods
322   // =========================================================================
323
324   private final Builder<T> b;
325
326   BeanInstantiator(Builder<T> b) {
327      this.b = b;
328   }
329
330   /**
331    * Creates the bean.
332    *
333    * <p>
334    * After creating the bean, this method automatically injects dependencies into fields and methods
335    * annotated with {@code @Inject} or {@code @Autowired} using {@link ClassInfo#inject(Object, BeanStore)}.
336    *
337    * @return A new bean with all dependencies injected.
338    * @throws ExecutableException if bean could not be created.
339    */
340   public T run() {
341      return b.runImpl();
342   }
343
344   /**
345    * Attempts to create the bean, returning an {@link Optional} instead of throwing an exception on failure.
346    *
347    * @return An {@link Optional} containing the created bean, or {@link Optional#empty()} if creation failed.
348    */
349   public Optional<T> asOptional() {
350      try {
351         return o(run());
352      } catch (@SuppressWarnings("unused") ExecutableException e) {
353         return oe();
354      }
355   }
356
357   /**
358    * Converts this creator into a memoizer.
359    *
360    * @return A resettable supplier that caches the created bean until reset.
361    */
362   public Memoizer<T> asMemoizer() {
363      return new Memoizer<>(this::run);
364   }
365
366   /**
367    * Converts this creator into a supplier.
368    *
369    * @return A supplier that returns the results of the {@link #run()} method.
370    */
371   public Supplier<T> asSupplier() {
372      return this::run;
373   }
374
375   /**
376    * Resets the cached bean instance and the inner Builder's resolved memoizers.
377    *
378    * @return This object.
379    */
380   public BeanInstantiator<T> reset() {
381      b.reset();
382      return this;
383   }
384
385   /**
386    * Returns the name of the bean being created.
387    *
388    * @return The bean name, or <jk>null</jk> if not set.
389    */
390   public String getName() {
391      return b.name;
392   }
393
394   /**
395    * Returns the bean subtype class info if specified, or the normal bean type if not.
396    *
397    * @return The bean subtype class info, or the bean type class info if no subtype was specified.
398    */
399   protected ClassInfo getBeanSubType() {
400      return b.beanSubType;
401   }
402
403   /**
404    * Returns the list of bean subtype classes, including the bean subtype, bean type, and all classes in between.
405    *
406    * @return A list of bean subtype class info objects, never <jk>null</jk>.
407    */
408   protected List<ClassInfo> getBeanSubTypes() {
409      return b.getBeanSubTypes();
410   }
411
412   /**
413    * Returns the builder instance if one has been created or specified.
414    *
415    * @param <B> The builder type.
416    * @return The builder instance wrapped in an {@link Optional}, or an empty optional if no builder exists.
417    */
418   public <B> Optional<B> getBuilder() {
419      return b.getBuilder();
420   }
421
422   /**
423    * Returns the builder type class info determined by builder type detection.
424    *
425    * @return The builder type class info, or <jk>null</jk> if no builder type was determined.
426    */
427   protected ClassInfo getBuilderType() {
428      return b.getBuilderType();
429   }
430
431   /**
432    * Returns the list of builder types, including the primary builder type and any builder types found in the builder's parent hierarchy.
433    *
434    * @return A list of builder type class info objects, never <jk>null</jk>.
435    */
436   protected List<ClassInfo> getBuilderTypes() {
437      return b.getBuilderTypes();
438   }
439
440   /**
441    * Returns an unmodifiable list of debug log entries from the last bean creation attempt.
442    *
443    * @return An unmodifiable list of debug log entries.
444    */
445   public List<String> getDebugLog() {
446      return b.getDebugLog();
447   }
448
449   // =========================================================================
450   // Builder (mutable, single-thread configuration) inner class
451   // =========================================================================
452
453   /**
454    * Builder for {@link BeanInstantiator}.
455    *
456    * <p>
457    * Holds all mutable configuration. Configure on a single thread, then call {@link #build()} to obtain
458    * an immutable {@link BeanInstantiator} suitable for shared concurrent use. Most call sites use the
459    * {@link #run()} shorthand which is equivalent to {@code build().run()}.
460    *
461    * <h5 class='section'>Thread Safety:</h5>
462    * <p>
463    * Builder instances are <b>not</b> thread-safe. Configure on one thread, then either call {@link #build()}
464    * to produce a thread-safe immutable {@link BeanInstantiator}, or call {@link #run()} to one-shot create a
465    * bean from a single thread.
466    *
467    * @param <T> The bean type being created.
468    */
469   public static class Builder<T> {
470
471   final BeanStore parentStore;
472   final BasicBeanStore store;
473   final ClassInfoTyped<T> beanType;
474   final NullableReference<List<String>> debug = NullableReference.empty();
475
476   private ClassInfoTyped<? extends T> beanSubType;
477   private ClassInfo explicitBuilderType = null;
478   private Object explicitBuilder = null;
479   private final Object enclosingInstance;
480   private T explicitImplementation = null;
481   private List<Consumer<T>> postCreateHooks = l();
482   private Set<String> factoryMethodNames = DEFAULT_FACTORY_METHOD_NAMES;
483   private Set<String> builderMethodNames = DEFAULT_BUILDER_METHOD_NAMES;
484   private Set<String> buildMethodNames = DEFAULT_BUILD_METHOD_NAMES;
485   private Set<String> builderClassNames = DEFAULT_BUILDER_CLASS_NAMES;
486   private Supplier<? extends T> fallbackSupplier = null;
487   private final String name;
488   private boolean cached = false;
489   private boolean factoryAbstainOnNull = false;
490   private boolean preferZeroArgConstructor = false;
491   private boolean silent = false;
492   private String description = null;
493   private List<UnaryOperator<T>> wrappers = l();
494   private List<Validation<T>> validators = l();
495   private List<Builder<? extends T>> alternatives = l();
496   private List<Consumer<Object>> builderInitializers = l();
497   private boolean injectBuilder = false;
498   private boolean autoWireBuilder = false;
499   boolean autoWireUnwrapSuppliers = true;
500   private boolean noBuilder = false;
501
502   private final Memoizer<ClassInfo> builderType = memoizer(() -> findBuilderType());
503   private final Memoizer<List<ClassInfo>> builderTypes = memoizer(() -> findBuilderTypes());
504   private final Memoizer<List<ClassInfo>> beanSubTypes = memoizer(() -> findBeanSubTypes());
505   private final Memoizer<Object> builderMemoizer = memoizer(() -> findBuilder());
506   private final Memoizer<T> beanImpl = memoizer(() -> processBean(findBeanImpl()));
507
508   /**
509    * Constructor.
510    *
511    * @param beanType The bean type being created.  Must not be <jk>null</jk>.
512    * @param parentStore The parent bean store to use for resolving dependencies. Can be <jk>null</jk> (the internal bean store then has no parent fallback).
513    * @param name The bean name. Can be <jk>null</jk> for unnamed beans.
514    * @param enclosingInstance The enclosing instance object. Can be <jk>null</jk> (the outer-instance skip rule is then never applied).
515    */
516   protected Builder(Class<T> beanType, BeanStore parentStore, String name, Object enclosingInstance) {
517      this.beanType = info(reqnn("beanType", beanType));
518      this.beanSubType = this.beanType;
519      this.parentStore = parentStore;
520      this.store = new BasicBeanStore(this.parentStore);
521      this.name = name;
522      this.enclosingInstance = enclosingInstance;
523   }
524
525   /**
526    * Adds a bean to the internal bean store and returns it.
527    *
528    * @param <T2> The bean type.
529    * @param type The bean type.  Must not be <jk>null</jk>.
530    * @param bean The bean instance.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
531    * @return The bean that was added.
532    */
533   public <T2> T2 add(Class<T2> type, T2 bean) {
534      store.add(type, bean);
535      reset();
536      return bean;
537   }
538
539   /**
540    * Adds a bean to the internal bean store.
541    *
542    * @param <T2> The bean type.
543    * @param type The bean type.  Must not be <jk>null</jk>.
544    * @param bean The bean instance.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
545    * @return This object.
546    */
547   public <T2> Builder<T> addBean(Class<T2> type, T2 bean) {
548      store.add(type, bean);
549      reset();
550      return this;
551   }
552
553   /**
554    * Adds a named bean to the internal bean store.
555    *
556    * @param <T2> The bean type.
557    * @param type The bean type.  Must not be <jk>null</jk>.
558    * @param bean The bean instance.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
559    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
560    * @return This object.
561    */
562   public <T2> Builder<T> addBean(Class<T2> type, T2 bean, String name) {
563      store.add(type, bean, name);
564      reset();
565      return this;
566   }
567
568   /**
569    * Attempts to create the bean, returning an {@link Optional} instead of throwing an exception on failure.
570    *
571    * <p>
572    * This is a non-throwing variant of {@link #run()} that wraps the result in an {@link Optional}.
573    * If bean creation fails for any reason (missing dependencies, abstract class, interface, no matching constructor, etc.),
574    * this method returns {@link Optional#empty()} instead of throwing {@link ExecutableException}.
575    *
576    * <p>
577    * This method is useful when bean creation failure is expected and should not be treated as exceptional,
578    * such as when implementing optional features or trying multiple creation strategies.
579    *
580    * <h5 class='section'>Notes:</h5><ul>
581    *    <li class='note'>Unlike {@link #run()}, this method catches {@link ExecutableException} and returns empty.
582    *    <li class='note'>If a fallback is registered via {@link #fallback(Supplier)},
583    *       it will be used before returning empty, so this method will only return empty if both
584    *       normal creation AND fallback fail.
585    *    <li class='note'>The created bean is cached via {@link Memoizer}, so subsequent calls return the same instance
586    *       unless {@link #reset()} is called or configuration changes.
587    *    <li class='note'>Post-creation hooks are still executed if creation succeeds.
588    *    <li class='note'>Other exceptions (e.g., from post-creation hooks or fallback suppliers) are NOT caught
589    *       and will propagate to the caller.
590    * </ul>
591    *
592    * <h5 class='section'>Example:</h5>
593    * <p class='bjava'>
594    *    <jc>// Try to create, use default if it fails</jc>
595    *    MyBean <jv>bean</jv> = BeanInstantiator
596    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>myBeanStore</jv>)
597    *       .asOptional()
598    *       .orElse(<jk>new</jk> DefaultMyBean());
599    *
600    *    <jc>// Chain multiple creation attempts</jc>
601    *    MyService <jv>service</jv> = BeanInstantiator.<jsm>of</jsm>(AdvancedService.<jk>class</jk>, <jv>store</jv>)
602    *       .asOptional()
603    *       .or(() -&gt; BeanInstantiator.<jsm>of</jsm>(BasicService.<jk>class</jk>, <jv>store</jv>)
604    *          .asOptional())
605    *       .orElseGet(() -&gt; <jk>new</jk> FallbackService());
606    *
607    *    <jc>// Check if optional feature is available</jc>
608    *    Optional&lt;OptionalFeature&gt; <jv>feature</jv> = BeanInstantiator
609    *       .<jsm>of</jsm>(OptionalFeature.<jk>class</jk>, <jv>store</jv>)
610    *       .asOptional();
611    *    <jk>if</jk> (<jv>feature</jv>.isPresent()) {
612    *       <jv>feature</jv>.get().enable();
613    *    }
614    * </p>
615    *
616    * @return An {@link Optional} containing the created bean, or {@link Optional#empty()} if creation failed.
617    */
618   public Optional<T> asOptional() {
619      return build().asOptional();
620   }
621
622   /**
623    * Converts this creator into a memoizer.
624    *
625    * <p>
626    * A {@link Memoizer} caches the result of the first {@link Memoizer#get()} call
627    * and returns that cached value for subsequent calls. The {@link Memoizer#reset()} method
628    * can be used to clear the cache, forcing the bean to be recreated on the next get() call.
629    *
630    * <p>
631    * This is particularly useful when the bean store contents change and you want to recreate the bean
632    * with updated dependencies.
633    *
634    * <h5 class='section'>Notes:</h5><ul>
635    *    <li class='note'>The returned {@link Memoizer} is thread-safe.
636    *    <li class='note'>Each call to {@link Memoizer#get()} after a reset() will invoke
637    *       {@link #run()}, which may trigger dependency injection and post-creation hooks.
638    *    <li class='note'>The memoizer inherits all Optional-like methods from {@link NullableSupplier}.
639    * </ul>
640    *
641    * <h5 class='section'>Example:</h5>
642    * <p class='bjava'>
643    *    <jc>// Create a memoizer for a bean with dependencies</jc>
644    *    BeanStore <jv>store</jv> = <jk>new</jk> BasicBeanStore(<jk>null</jk>);
645    *    <jv>store</jv>.addBean(String.<jk>class</jk>, <js>"initial"</js>);
646    *
647    *    Memoizer&lt;MyBean&gt; <jv>supplier</jv> = BeanInstantiator
648    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>store</jv>)
649    *       .asMemoizer();
650    *
651    *    <jc>// First call creates and caches the bean</jc>
652    *    MyBean <jv>bean1</jv> = <jv>supplier</jv>.get();
653    *
654    *    <jc>// Subsequent calls return the cached bean</jc>
655    *    MyBean <jv>bean2</jv> = <jv>supplier</jv>.get();
656    *    <jsm>assertSame</jsm>(<jv>bean1</jv>, <jv>bean2</jv>);
657    *
658    *    <jc>// Update the bean store</jc>
659    *    <jv>store</jv>.addBean(String.<jk>class</jk>, <js>"updated"</js>);
660    *
661    *    <jc>// Reset forces recreation with updated dependencies</jc>
662    *    <jv>supplier</jv>.reset();
663    *    MyBean <jv>bean3</jv> = <jv>supplier</jv>.get();
664    *    <jsm>assertNotSame</jsm>(<jv>bean1</jv>, <jv>bean3</jv>);  <jc>// New instance with updated deps</jc>
665    * </p>
666    *
667    * @return A resettable supplier that caches the created bean until reset.
668    */
669   public Memoizer<T> asMemoizer() {
670      return build().asMemoizer();
671   }
672
673   /**
674    * Converts this creator into a supplier.
675    *
676    * @return A supplier that returns the results of the {@link #run()} method.
677    */
678   public Supplier<T> asSupplier() {
679      return build().asSupplier();
680   }
681
682   /**
683    * Specifies a subclass of the bean type to create.
684    *
685    * <p>
686    * This allows you to create a specific implementation when the bean type is an interface, abstract class,
687    * or when you want to instantiate a customized subclass of a concrete class.
688    *
689    * @param value The subclass type to create. Cannot be <jk>null</jk>. Must be a subclass of {@code beanType}.
690    * @return This object.
691    * @throws IllegalArgumentException If value is not a subclass of {@code beanType}.
692    */
693   public Builder<T> type(Class<? extends T> value) {
694      reqnn("value", value);
695      beanSubType = info(value);
696      req(beanType.isAssignableFrom(beanSubType), "type must be a subclass of beanType. beanType=%s, type=%s", cn(beanType), cn(beanSubType));
697      reset();
698      return this;
699   }
700
701   /**
702    * Specifies a subclass of the bean type to create, via a {@link ClassInfo} reference.
703    *
704    * <p>
705    * Convenience overload of {@link #type(Class)} that accepts {@link ClassInfo} (or any subtype such
706    * as {@code ClassMeta}).  The underlying class is extracted via {@link ClassInfo#inner()} and the
707    * subtype-relationship check happens at runtime.
708    *
709    * @param value The subclass type to create.  Cannot be <jk>null</jk>.
710    * @return This object.
711    * @throws IllegalArgumentException If {@code value} is <jk>null</jk> or its underlying class is not a subclass of {@code beanType}.
712    */
713   public Builder<T> type(ClassInfo value) {
714      reqnn("value", value);
715      return type((Class<? extends T>) value.inner());
716   }
717
718   /**
719    * Specifies the builder type to use for creating the bean.
720    *
721    * <p>
722    * This method triggers builder type detection and instantiation for the specified type.
723    *
724    * <p>
725    * The builder type must be valid, meaning it must have a <c>build()</c>, <c>create()</c>, or <c>get()</c> method
726    * that returns the bean type (or a parent of the bean type). The method may have <c>@Inject</c> or <c>@Autowired</c>
727    * annotation to allow injected parameters; otherwise, it must have no parameters.
728    *
729    * <p>
730    * If the builder type is invalid (does not have a valid build method), this method throws an
731    * {@link IllegalArgumentException}.
732    *
733 * <p>
734 * The builder will be instantiated by looking for:
735 * <ol>
736 *    <li>A static <c>create()</c> or <c>builder()</c> method that returns the builder type.
737 *    <li>A public constructor on the builder type with parameters that can be resolved from the bean store.
738 *    <li>A protected constructor on the builder type with parameters that can be resolved from the bean store.
739 *    <li>A package-private constructor on the builder type with parameters that can be resolved from the bean store.
740 *       Private constructors are deliberately excluded.
741 * </ol>
742    *
743    * @param value The builder type. Cannot be <jk>null</jk>.
744    * @return This object.
745    * @throws IllegalArgumentException If the builder type is invalid (does not have a valid build/create/get method).
746    */
747   public Builder<T> builder(Class<?> value) {
748      explicitBuilderType = info(reqnn("value", value));
749      builderType.set(explicitBuilderType);
750      req(isValidBuilderType(explicitBuilderType), "Invalid builder type %1$s for bean type %2$s. Builder must have a build(), create(), or get() method that returns %2$s (or a parent of %2$s). The method may have @Inject annotation to allow injected parameters; otherwise, it must have no parameters.", cn(explicitBuilderType), cn(beanType));
751      builderTypes.get();  // Triggers validation on type hierarchy.
752      reset();
753      return this;
754   }
755
756   /**
757    * Specifies an existing builder instance to use for creating the bean.
758    *
759    * <p>
760    * The builder instance's class must be a valid builder type, meaning it must have a <c>build()</c>, <c>create()</c>,
761    * or <c>get()</c> method that returns the bean type (or a parent of the bean type). The method may have
762    * <c>@Inject</c> or <c>@Autowired</c> annotation to allow injected parameters; otherwise, it must have no parameters.
763    *
764    * <p>
765    * If the builder instance's class is invalid (does not have a valid build method), this method throws an
766    * {@link IllegalArgumentException}.
767    *
768    * <h5 class='section'>Notes:</h5><ul>
769    *    <li class='note'>When specified, the builder type detection is bypassed.
770    *    <li class='note'>The builder will be used to create the bean via:
771    *       <ul>
772    *          <li>A <c>build()</c>/<c>create()</c>/<c>get()</c> method on the builder (required).
773    *             The method may have <c>@Inject</c> annotation to allow injected parameters.
774    *       </ul>
775    *    <li class='note'>The explicit builder instance is preserved even when {@link #reset()} is called,
776    *       unlike cached results from suppliers.
777    * </ul>
778    *
779    * @param value The builder instance. Cannot be <jk>null</jk>.
780    * @return This object.
781    * @throws IllegalArgumentException If the builder instance's class is invalid (does not have a valid build/create/get method).
782    */
783   public Builder<T> builder(Object value) {
784      builder(value.getClass());
785      explicitBuilder = reqnn("value", value);
786      reset();
787      return this;
788   }
789
790   /**
791    * Specifies custom class names to look for when auto-detecting builder inner classes.
792    *
793    * <p>
794    * By default, {@code BeanInstantiator} looks for inner classes named {@code "Builder"}.
795    * This method allows you to specify alternative class names.
796    *
797    * <h5 class='section'>Notes:</h5><ul>
798    *    <li class='note'>Inner builder classes must be public, static, and pass builder validation
799    *       (have a build/create/get method or be accepted by a bean constructor).
800    *    <li class='note'>If multiple inner classes match, the first valid one found will be used.
801    *    <li class='note'>This only affects inner class detection. It does not affect builders specified via
802    *       {@link #builder(Class)}, {@link #builder(Object)}, or the {@link Builder @Builder} annotation.
803    *    <li class='note'>Calling this method replaces the default builder class names.
804    * </ul>
805    *
806    * <h5 class='section'>Example:</h5>
807    * <p class='bjava'>
808    *    <jc>// Support alternative builder class naming conventions</jc>
809    *    MyBean <jv>bean</jv> = BeanInstantiator
810    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>myBeanStore</jv>)
811    *       .builderClassNames(<js>"BuilderImpl"</js>, <js>"Factory"</js>)
812    *       .run();
813    * </p>
814    *
815    * @param names The builder class names to look for. Cannot be <jk>null</jk> or contain <jk>null</jk> elements.
816    * @return This object.
817    */
818   public Builder<T> builderClassNames(String... names) {
819      builderClassNames = set(reqnns("names", names));
820      reset();
821      return this;
822   }
823
824   /**
825    * Specifies custom method names to look for when auto-detecting builder factory methods.
826    *
827    * <p>
828    * By default, {@code BeanInstantiator} looks for static methods named {@code "create"} or {@code "builder"}
829    * that return a builder type. This method allows you to specify alternative method names.
830    *
831    * <h5 class='section'>Notes:</h5><ul>
832    *    <li class='note'>Builder factory methods must be public, static, non-deprecated, and return a builder type
833    *       (not the bean type itself).
834    *    <li class='note'>If multiple builder factory methods match, the one with the most parameters that can be
835    *       resolved from the bean store will be used.
836    *    <li class='note'>This affects both builder type detection and builder instance creation.
837    *    <li class='note'>Calling this method replaces the default builder method names.
838    * </ul>
839    *
840    * <h5 class='section'>Example:</h5>
841    * <p class='bjava'>
842    *    <jc>// Support alternative builder factory method naming conventions</jc>
843    *    MyBean <jv>bean</jv> = BeanInstantiator
844    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>)
845    *       .builderMethodNames(<js>"newBuilder"</js>, <js>"instance"</js>)
846      .run();
847    * </p>
848    *
849    * @param names The builder factory method names to look for. Cannot be <jk>null</jk> or contain <jk>null</jk> elements.
850    * @return This object.
851    */
852   public Builder<T> builderMethodNames(String... names) {
853      builderMethodNames = set(reqnns("names", names));
854      reset();
855      return this;
856   }
857
858   /**
859    * Specifies custom method names to look for when calling build methods on builder instances.
860    *
861    * <p>
862    * By default, {@code BeanInstantiator} looks for instance methods named {@code "build"}, {@code "create"}, or {@code "get"}
863    * on the builder that return the bean type. This method allows you to specify alternative method names.
864    *
865    * <h5 class='section'>Notes:</h5><ul>
866    *    <li class='note'>Build methods must be public, non-static, non-deprecated, and return the bean type
867    *       (or a parent type of the bean type).
868    *    <li class='note'>If the method has {@code @Inject} or {@code @Autowired} annotation, its parameters are resolved from the bean store.
869    *    <li class='note'>If the method does not have {@code @Inject} annotation, it must have no parameters.
870    *    <li class='note'>Calling this method replaces the default build method names.
871    * </ul>
872    *
873    * <h5 class='section'>Example:</h5>
874    * <p class='bjava'>
875    *    <jc>// Support alternative build method naming conventions</jc>
876    *    MyBean <jv>bean</jv> = BeanInstantiator
877    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>)
878    *       .buildMethodNames(<js>"execute"</js>, <js>"make"</js>)
879      .run();
880    * </p>
881    *
882    * @param names The build method names to look for. Cannot be <jk>null</jk> or contain <jk>null</jk> elements.
883    * @return This object.
884    */
885   public Builder<T> buildMethodNames(String... names) {
886      buildMethodNames = set(reqnns("names", names));
887      reset();
888      return this;
889   }
890
891   /**
892    * Enables caching mode.
893    *
894    * <p>
895    * When caching mode is enabled, bean instances are cached using {@link Memoizer},
896    * so multiple calls to {@link #run()} will return the same instance unless {@link #reset()} is called
897    * or configuration changes.
898    *
899    * <p>
900    * When caching mode is disabled (the default), each call to {@link #run()} will create a new bean instance
901    * by calling {@link #findBeanImpl()} directly.
902    *
903    * <h5 class='section'>Example:</h5>
904    * <p class='bjava'>
905    *    <jc>// Enable caching mode - same instance returned on each run()</jc>
906    *    <jk>var</jk> <jv>creator</jv> = BeanInstantiator
907    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>)
908    *       .<jsm>cached</jsm>();
909    *    MyBean <jv>bean1</jv> = <jv>creator</jv>.<jsm>run</jsm>();
910    *    MyBean <jv>bean2</jv> = <jv>creator</jv>.<jsm>run</jsm>();
911    *    <jc>// bean1 and bean2 are the same instance</jc>
912    * </p>
913    *
914    * @return This object.
915    */
916   public Builder<T> cached() {
917      cached = true;
918      return this;
919   }
920
921   /**
922    * Treat a {@code null} return value from a static factory method as a deliberate "abstain" signal.
923    *
924    * <p>
925    * By default, when a static factory method (one of {@link #factoryMethodNames(String...)}) matches and is
926    * invoked, but returns {@code null}, {@link #run()} falls through to constructor lookup as if the factory
927    * method weren't there. This works well when the factory method is just an alternate construction path.
928    *
929    * <p>
930    * Some factory-method patterns instead use a {@code null} return value as a deliberate "this implementation
931    * does not handle the input — try the next strategy" signal, expecting the caller to interpret the {@code null}
932    * as a definitive answer. The classic example is a {@code RestOpArg} subclass whose
933    * {@code static create(ParameterInfo)} returns {@code null} when the parameter isn't annotated with the
934    * marker the subclass handles, so the caller can move on to the next subclass in the chain. Falling through
935    * to the (always-present) constructor in that case would silently materialize an arg that doesn't apply.
936    *
937    * <p>
938    * When this flag is enabled, a {@code null} return from the matching factory method is propagated out of
939    * {@link #run()} unchanged — constructor lookup is skipped. This matches legacy {@code BeanCreator}
940    * semantics for static {@code create()} / {@code builder()} / {@code getInstance()} methods.
941    *
942    * <h5 class='section'>Example:</h5>
943    * <p class='bjava'>
944    *    <jc>// AttributeArg.create(ParameterInfo) returns null if @Attr is not present.</jc>
945    *    <jc>// Without factoryAbstainOnNull, BeanInstantiator would then invoke the AttributeArg(ParameterInfo)</jc>
946    *    <jc>// constructor and return a bogus instance.</jc>
947    *    RestOpArg <jv>arg</jv> = BeanInstantiator
948    *       .<jsm>of</jsm>(RestOpArg.<jk>class</jk>, <jv>store</jv>)
949    *       .type(AttributeArg.<jk>class</jk>)
950    *       .factoryMethodNames(<js>"getInstance"</js>, <js>"create"</js>)
951    *       .factoryAbstainOnNull()
952    *       .run();
953    * </p>
954    *
955    * @return This object.
956    */
957   public Builder<T> factoryAbstainOnNull() {
958      factoryAbstainOnNull = true;
959      return this;
960   }
961
962   /**
963    * Prefer a zero-argument constructor when one exists, instead of injecting via the longest constructor.
964    *
965    * <p>
966    * By default, {@code BeanInstantiator} sorts public constructors by parameter count <i>descending</i>: the
967    * constructor with the most resolvable parameters wins, encouraging full constructor injection. This is the
968    * right default for most beans, but it doesn't fit every use case.
969    *
970    * <p>
971    * The classic mismatch is parameterized container types like {@link TreeMap}, which expose:
972    * <ul>
973    *    <li>{@code TreeMap()}
974    *    <li>{@code TreeMap(Comparator)}
975    *    <li>{@code TreeMap(Map<? extends K,? extends V>)}
976    *    <li>{@code TreeMap(SortedMap)}
977    * </ul>
978    * The default selection picks the {@code Map} copy-constructor — but that {@code Map<? extends K,? extends V>}
979    * parameter doesn't satisfy v2 inject-collection auto-resolution (which only handles {@code Map<String,T>}),
980    * so injection fails at runtime even though {@link ParameterInfo#canResolve(BeanStore, Object...)} optimistically
981    * said yes. Callers that just want "give me a fresh empty container" should use this flag.
982    *
983    * <p>
984    * When this flag is enabled, if {@code beanSubType} declares a public no-arg constructor, that constructor is
985    * used unconditionally and other constructors are ignored. This matches legacy {@code BeanCreator} behavior for
986    * the same use case (legacy filtered out the multi-arg ctors via {@code hasAllParams} returning false, since the
987    * legacy bean store didn't auto-resolve raw {@code Map}/{@code Collection} parameter types).
988    *
989    * <h5 class='section'>Example:</h5>
990    * <p class='bjava'>
991    *    <jc>// Instantiating an arbitrary Map subclass — prefer the no-arg ctor over copy-ctors.</jc>
992    *    Map&lt;?,?&gt; <jv>m</jv> = BeanInstantiator
993    *       .<jsm>of</jsm>(Map.<jk>class</jk>)
994    *       .type(treeMapClass)
995    *       .preferZeroArgConstructor()
996    *       .run();
997    * </p>
998    *
999    * @return This object.
1000    */
1001   public Builder<T> preferZeroArgConstructor() {
1002      preferZeroArgConstructor = true;
1003      return this;
1004   }
1005
1006   /**
1007    * Compiles this builder into a {@link BeanInstantiator}.
1008    *
1009    * <p>
1010    * The returned instance exposes only run/read methods (no setters). The compiled instance shares
1011    * its caching state with this builder so that {@link #cached()} memoization is consistent across
1012    * both compiled and shorthand call paths.
1013    *
1014    * @return A new {@link BeanInstantiator} backed by this builder's configuration.
1015    */
1016   public BeanInstantiator<T> build() {
1017      return new BeanInstantiator<>(this);
1018   }
1019
1020   /**
1021    * Shortcut for {@code build().run()}.
1022    *
1023    * <p>
1024    * Most call sites do not store the {@link BeanInstantiator} and therefore call this shortcut.
1025    * Repeated calls share caching with any compiled {@link BeanInstantiator} produced by {@link #build()}.
1026    *
1027    * @return A new bean with all dependencies injected.
1028    * @throws ExecutableException if bean could not be created.
1029    */
1030   public T run() {
1031      return runImpl();
1032   }
1033
1034   T runImpl() {
1035      debug.ifPresent(x -> x.clear());
1036
1037      try {
1038         return runImplOnce();
1039      } catch (RuntimeException e) {
1040         if (alternatives.isEmpty())
1041            throw e;
1042         for (var alt : alternatives) {
1043            try {
1044               return processBean(alt.runImpl());
1045            } catch (@SuppressWarnings("unused") RuntimeException ignored) {
1046               // Try next alternative
1047            }
1048         }
1049         throw e;
1050      }
1051   }
1052
1053   private T runImplOnce() {
1054      if (neq(beanSubType, beanType))
1055         log("Subtype specified: %s", beanSubType.getName());
1056
1057      if (explicitImplementation != null) {
1058         log("Using pre-configured impl() instance");
1059         return processBean(runPostCreateHooks(inject(explicitImplementation)));
1060      }
1061
1062      if (cached) {
1063         log("Using cached instance");
1064         return beanImpl.get();
1065      }
1066
1067      log("Using new instance");
1068      return processBean(findBeanImpl());
1069   }
1070
1071   private T processBean(T bean) {
1072      for (var w : wrappers)
1073         bean = w.apply(bean);
1074      for (var v : validators) {
1075         if (! v.predicate().test(bean))
1076            throw new ExecutableException(v.message() + " (bean=" + bean + ")");
1077      }
1078      return bean;
1079   }
1080
1081   /**
1082    * Enables debug mode to track the bean creation process.
1083    *
1084    * <p>
1085    * When debug mode is enabled, the creator logs each step of the bean creation process
1086    * to an internal list. This log can be retrieved via {@link #getDebugLog()} to understand
1087    * which creation path was taken, why creation might have failed, or to troubleshoot
1088    * unexpected behavior.
1089    *
1090    * <h5 class='section'>Notes:</h5><ul>
1091    *    <li class='note'>Debug logs include: builder detection, factory method attempts, constructor attempts,
1092    *       dependency resolution, injection, hooks execution, validation, and fallback usage.
1093    *    <li class='note'>The debug log is reset each time {@link #run()} or {@link #asOptional()} is called.
1094    *    <li class='note'>Debug mode has minimal performance impact but should generally be disabled in production.
1095    *    <li class='note'>The debug log is stored in memory and can be retrieved after bean creation.
1096    *    <li class='note'>When caching is enabled via {@link #cached()}, the log reflects the first creation only
1097    *       (subsequent calls return the cached instance unless {@link #reset()} is called).
1098    * </ul>
1099    *
1100    * <h5 class='section'>Example:</h5>
1101    * <p class='bjava'>
1102    *    <jc>// Enable debug mode</jc>
1103    *    <jk>var</jk> <jv>creator</jv> = BeanInstantiator
1104    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>store</jv>)
1105    *       .debug();
1106    *
1107    *    MyBean <jv>bean</jv> = <jv>creator</jv>.run();
1108    *
1109    *    <jc>// Print debug log</jc>
1110    *    <jv>creator</jv>.getDebugLog().forEach(System.<jf>out</jf>::println);
1111    * </p>
1112    *
1113    * @return This object.
1114    */
1115   public Builder<T> debug() {
1116      debug.set(synchronizedList(new ArrayList<>()));
1117      return this;
1118   }
1119
1120
1121   /**
1122    * Specifies custom static factory method names to look for when creating the bean.
1123    *
1124    * <p>
1125    * By default, {@link BeanInstantiator} looks for a static {@code getInstance()} method when attempting
1126    * to create beans without a builder. This method allows you to specify alternative
1127    * factory method names to support non-standard naming conventions.
1128    *
1129    * <h5 class='section'>Notes:</h5><ul>
1130    *    <li class='note'>Factory methods must be public, static, non-deprecated, and return the bean type.
1131    *    <li class='note'>If multiple factory methods match, the one with the most parameters that can be
1132    *       resolved from the bean store will be used.
1133    *    <li class='note'>Factory methods are attempted before constructors in the bean creation order.
1134    *    <li class='note'>This setting only affects factory method detection; it does not affect builder detection.
1135    *    <li class='note'>Calling this method replaces the default factory method names.
1136    * </ul>
1137    *
1138    * <h5 class='section'>Example:</h5>
1139    * <p class='bjava'>
1140    *    <jc>// Support multiple factory method naming conventions</jc>
1141    *    MyBean <jv>bean</jv> = BeanInstantiator
1142    *       .<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>myBeanStore</jv>)
1143    *       .factoryMethodNames(<js>"of"</js>, <js>"from"</js>, <js>"create"</js>, <js>"newInstance"</js>)
1144    *       .run();
1145    * </p>
1146    *
1147    * @param names The factory method names to look for. Cannot be <jk>null</jk> or contain <jk>null</jk> elements.
1148    * @return This object.
1149    */
1150   public Builder<T> factoryMethodNames(String... names) {
1151      factoryMethodNames = set(reqnns("names", names));
1152      reset();
1153      return this;
1154   }
1155
1156   /**
1157    * Specifies a fallback supplier to use if bean creation fails.
1158    *
1159    * <p>
1160    * When a fallback supplier is registered, if bean creation fails (throws {@link ExecutableException}),
1161    * instead of propagating the exception, the fallback supplier will be invoked to provide a default instance.
1162    *
1163    * <p>
1164    * This enables graceful degradation when bean creation cannot succeed, such as when optional features
1165    * or dependencies are not available.
1166    *
1167    * <h5 class='section'>Notes:</h5><ul>
1168    *    <li class='note'>The fallback supplier is only invoked if bean creation fails.
1169    *       If bean creation succeeds normally, the fallback is never called.
1170    *    <li class='note'>Fallbacks are checked after all normal creation attempts (builders, factory methods, constructors).
1171    *    <li class='note'>The fallback-provided instance will have post-creation hooks executed on it.
1172    *    <li class='note'>If the fallback is used, that instance is cached via {@link Memoizer} and returned on subsequent calls
1173    *       unless {@link #reset()} is called or configuration changes.
1174    *    <li class='note'>Only one fallback can be registered. Subsequent calls to this method will replace
1175    *       the previous fallback.
1176    *    <li class='note'>If the fallback supplier itself throws an exception, that exception will propagate to the caller.
1177    *    <li class='note'>To use a pre-created instance as a fallback, wrap it in a supplier: <c>fallback(() -&gt; myInstance)</c>.
1178    * </ul>
1179    *
1180    * <h5 class='section'>Example:</h5>
1181    * <p class='bjava'>
1182    *    <jc>// Provide a default instance if creation fails</jc>
1183    *    MyService <jv>service</jv> = BeanInstantiator
1184    *       .<jsm>of</jsm>(MyService.<jk>class</jk>, <jv>myBeanStore</jv>)
1185    *       .fallback(() -&gt; <jk>new</jk> DefaultMyService())
1186    *       .run();
1187    *
1188    *    <jc>// Use a pre-created instance as fallback</jc>
1189    *    MyService <jv>defaultService</jv> = <jk>new</jk> DefaultMyService();
1190    *    MyService <jv>service2</jv> = BeanInstantiator
1191    *       .<jsm>of</jsm>(MyService.<jk>class</jk>, <jv>myBeanStore</jv>)
1192    *       .fallback(() -&gt; <jv>defaultService</jv>)
1193    *       .run();
1194    * </p>
1195    *
1196    * @param fallback The fallback supplier. Cannot be <jk>null</jk>.
1197    * @return This object.
1198    */
1199   public Builder<T> fallback(Supplier<? extends T> fallback) {
1200      reqnn("fallback", fallback);
1201      this.fallbackSupplier = fallback;
1202      return this;
1203   }
1204
1205   /**
1206    * Returns the bean subtype class info if specified, or the normal bean type if not.
1207    *
1208    * <p>
1209    * If {@link #type(Class)} was called to specify a subtype, returns that subtype.
1210    * Otherwise, returns the original bean type passed to {@link #of(Class)}.
1211    *
1212    * @return The bean subtype class info, or the bean type class info if no subtype was specified.
1213    */
1214   protected ClassInfo getBeanSubType() {
1215      return beanSubType;
1216   }
1217
1218   /**
1219    * Returns the name of the bean being created.
1220    *
1221    * <p>
1222    * Returns the name that was passed to {@link #of(Class, BeanStore, String, Object)}, or <jk>null</jk> if no name was specified.
1223    *
1224    * @return The bean name, or <jk>null</jk> if not set.
1225    */
1226   public String getName() {
1227      return name;
1228   }
1229
1230   /**
1231    * Returns the list of bean subtype classes, including the bean subtype, bean type, and all classes in between.
1232    *
1233    * <p>
1234    * This method returns a list that starts with the bean subtype (if different from bean type) and includes
1235    * all parent classes up to and including the bean type. If no subtype was specified, the list will contain
1236    * only the bean type.
1237    *
1238    * <h5 class='section'>Example:</h5>
1239    * <p class='bjava'>
1240    *    <jc>// If beanType is A and beanSubType is C (extends B extends A)</jc>
1241    *    <jc>// getBeanSubTypes() returns: [C, B, A]</jc>
1242    * </p>
1243    *
1244    * @return A list of bean subtype class info objects, never <jk>null</jk>.
1245    */
1246   protected List<ClassInfo> getBeanSubTypes() {
1247      return beanSubTypes.get();
1248   }
1249
1250   /**
1251    * Returns the builder instance if one has been created or specified.
1252    *
1253    * @param <B> The builder type.
1254    * @return The builder instance wrapped in an {@link Optional}, or an empty optional if no builder exists.
1255    */
1256   public <B> Optional<B> getBuilder() {
1257      beanImpl.reset();
1258      return (Optional<B>)builderMemoizer.toOptional();
1259   }
1260
1261   /**
1262    * Returns the builder type class info determined by {@link #findBuilderType()}.
1263    *
1264    * @return The builder type class info, or <jk>null</jk> if no builder type was determined.
1265    */
1266   protected ClassInfo getBuilderType() {
1267      return builderType.get();
1268   }
1269
1270   /**
1271    * Returns the list of builder types, including the primary builder type and any builder types found in the builder's parent hierarchy.
1272    *
1273    * <p>
1274    * This method returns a list that includes the primary builder type and all builder types found by traversing
1275    * the builder's parent hierarchy. This allows parent builders to be resolved as injectable types.
1276    *
1277    * <h5 class='section'>Example:</h5>
1278    * <p class='bjava'>
1279    *    <jc>// Class A with builder</jc>
1280    *    <jk>class</jk> A {
1281    *       <jk>static class</jk> Builder {
1282    *          A create() { <jk>return new</jk> A(); }
1283    *       }
1284    *    }
1285    *
1286    *    <jc>// Class B extends A with builder</jc>
1287    *    <jk>class</jk> B <jk>extends</jk> A {
1288    *       <jk>static class</jk> Builder <jk>extends</jk> A.Builder {
1289    *          B create() { <jk>return new</jk> B(); }
1290    *       }
1291    *    }
1292    *
1293    *    <jc>// When creating B, getBuilderTypes() returns: [B.Builder, A.Builder]</jc>
1294    * </p>
1295    *
1296    * @return A list of builder type class info objects, never <jk>null</jk>.
1297    */
1298   protected List<ClassInfo> getBuilderTypes() {
1299      return builderTypes.get();
1300   }
1301
1302   /**
1303    * Returns an unmodifiable list of debug log entries from the last bean creation attempt.
1304    *
1305    * <p>
1306    * This method returns the detailed log of steps taken during the most recent call to
1307    * {@link #run()} or {@link #asOptional()}. The log is only populated when {@link #debug()}
1308    * mode is enabled.
1309    *
1310    * <h5 class='section'>Notes:</h5><ul>
1311    *    <li class='note'>Returns an empty list if {@link #debug()} was not called or if no creation has been attempted.
1312    *    <li class='note'>The log is reset at the beginning of each {@link #run()} call.
1313    *    <li class='note'>Since {@link #run()} uses a cached {@link Memoizer}, the log reflects only the first creation
1314    *       (subsequent calls return the cached instance unless {@link #reset()} is called).
1315    *    <li class='note'>The returned list is unmodifiable.
1316    * </ul>
1317    *
1318    * <h5 class='section'>Example:</h5>
1319    * <p class='bjava'>
1320    *    <jk>var</jk> <jv>creator</jv> = BeanInstantiator.<jsm>of</jsm>(MyBean.<jk>class</jk>, <jv>store</jv>)
1321    *       .debug();
1322    *
1323    *    <jk>try</jk> {
1324      <jv>creator</jv>.run();
1325    *    } <jk>catch</jk> (ExecutableException <jv>e</jv>) {
1326    *       <jc>// Print debug log to understand why creation failed</jc>
1327    *       <jv>creator</jv>.getDebugLog().forEach(System.<jf>err</jf>::println);
1328    *    }
1329    * </p>
1330    *
1331    * @return An unmodifiable list of debug log entries.
1332    */
1333   public List<String> getDebugLog() {
1334      return debug.map(x -> u(cp(debug.get()))).orElse(emptyList());
1335   }
1336
1337   /**
1338    * Specifies an existing bean implementation to use instead of creating a new instance.
1339    *
1340    * <p>
1341    * When specified, the bean creation logic is bypassed and this implementation is returned
1342    * directly from {@link #run()}, after performing dependency injection on it.
1343    *
1344    * <h5 class='section'>Notes:</h5><ul>
1345    *    <li class='note'>The bean implementation will still have its fields and methods injected via {@link ClassInfo#inject(Object, BeanStore)}.
1346    *    <li class='note'>This is useful when you want to provide a pre-configured instance but still benefit from dependency injection.
1347    *    <li class='note'>The explicit implementation is preserved even when {@link #reset()} is called, unlike cached results from suppliers.
1348    * </ul>
1349    *
1350    * @param value The bean implementation instance.  Can be <jk>null</jk>, in which case no explicit implementation is used.
1351    * @return This object.
1352    */
1353   public Builder<T> impl(T value) {
1354      this.explicitImplementation = value;
1355      return this;
1356   }
1357
1358   /**
1359    * Registers a post-creation hook to run after the bean is created.
1360    *
1361    * <p>
1362    * Post-creation hooks are executed after the bean is instantiated and after dependency injection
1363    * has been performed via {@link ClassInfo#inject(Object, BeanStore)}.
1364    *
1365    * <p>
1366    * Multiple hooks can be registered and they will be executed in the order they were added.
1367    *
1368    * <h5 class='section'>Notes:</h5><ul>
1369    *    <li class='note'>Hooks are executed every time {@link #run()} is called. Since {@link #run()} uses a cached
1370    *       {@link Memoizer}, hooks are only executed once unless {@link #reset()} is called.
1371    *    <li class='note'>Hooks are executed after dependency injection, so all {@code @Inject} fields and methods
1372    *       will have been populated before the hook runs.
1373    *    <li class='note'>Hooks can be used for custom initialization logic, logging, wrapping beans with proxies,
1374    *       or any other post-creation processing.
1375    *    <li class='note'>If a hook throws an exception, bean creation will fail and the exception will propagate to the caller.
1376    *    <li class='note'>The hook receives the fully initialized bean instance as its parameter.
1377    * </ul>
1378    *
1379    * <h5 class='section'>Example:</h5>
1380    * <p class='bjava'>
1381    *    <jc>// Register multiple post-creation hooks</jc>
1382    *    MyService <jv>service</jv> = BeanInstantiator
1383    *       .<jsm>of</jsm>(MyService.<jk>class</jk>, <jv>myBeanStore</jv>)
1384    *       .postCreateHook(<jv>s</jv> -&gt; <jv>s</jv>.initialize())
1385    *       .postCreateHook(<jv>s</jv> -&gt; <jv>s</jv>.loadConfiguration())
1386    *       .postCreateHook(<jv>s</jv> -&gt; <jv>logger</jv>.info(<js>"Created service: "</js> + <jv>s</jv>))
1387      .run();
1388    * </p>
1389    *
1390    * @param hook The post-creation hook to run after bean creation. Cannot be <jk>null</jk>.
1391    * @return This object.
1392    */
1393   public Builder<T> postCreateHook(Consumer<T> hook) {
1394      reqnn("hook", hook);
1395      postCreateHooks.add(hook);
1396      return this;
1397   }
1398
1399   /**
1400    * Suppresses warning-level log output from this builder.
1401    *
1402    * <p>
1403    * Useful when {@link #asOptional()} is preferred and you don't want the underlying log noise that
1404    * accompanies fallback or factory-abstention paths.
1405    *
1406    * <p>
1407    * Errors and exceptions are still thrown normally; only descriptive log entries are suppressed.
1408    *
1409    * @return This object.
1410    */
1411   public Builder<T> silent() {
1412      silent = true;
1413      return this;
1414   }
1415
1416   /**
1417    * Sets a human-readable description used in log entries and error messages.
1418    *
1419    * <p>
1420    * Useful when the bean type alone doesn't provide enough context (e.g. multiple instances of the same type).
1421    *
1422    * @param value The description. Cannot be <jk>null</jk>.
1423    * @return This object.
1424    */
1425   public Builder<T> description(String value) {
1426      reqnn("value", value);
1427      description = value;
1428      return this;
1429   }
1430
1431   /**
1432    * Registers a transformation to apply to the created bean before it is returned.
1433    *
1434    * <p>
1435    * Multiple wrappers may be registered; they are applied in registration order. Each wrapper receives
1436    * the output of the previous wrapper.
1437    *
1438    * <h5 class='section'>Example:</h5>
1439    * <p class='bjava'>
1440    *    BeanInstantiator.<jsm>of</jsm>(MyService.<jk>class</jk>, store)
1441    *       .wrap(LoggingProxy::wrap)
1442    *       .wrap(MetricsProxy::wrap)
1443    *       .run();
1444    * </p>
1445    *
1446    * @param wrapper The wrapper. Cannot be <jk>null</jk>.
1447    * @return This object.
1448    */
1449   public Builder<T> wrap(UnaryOperator<T> wrapper) {
1450      reqnn("value", wrapper);
1451      wrappers.add(wrapper);
1452      return this;
1453   }
1454
1455   /**
1456    * Registers a post-creation validator.
1457    *
1458    * <p>
1459    * The predicate is invoked on the created bean. If it returns <jk>false</jk>, an
1460    * {@link ExecutableException} is thrown with the supplied message.
1461    *
1462    * @param predicate The predicate. Cannot be <jk>null</jk>.
1463    * @param message The error message used when the predicate returns <jk>false</jk>. Cannot be <jk>null</jk>.
1464    * @return This object.
1465    */
1466   public Builder<T> validate(Predicate<T> predicate, String message) {
1467      reqnn("value", predicate);
1468      reqnn("value", message);
1469      validators.add(new Validation<>(predicate, message));
1470      return this;
1471   }
1472
1473   /**
1474    * Registers an alternative {@link BeanInstantiator.Builder} to consult if this builder fails to create the bean.
1475    *
1476    * <p>
1477    * Multiple alternatives may be registered. They are tried in registration order. The first one that
1478    * succeeds returns its bean; if all fail, the last exception is propagated.
1479    *
1480    * @param alternative The alternative builder. Cannot be <jk>null</jk>.
1481    * @return This object.
1482    */
1483   public Builder<T> or(Builder<? extends T> alternative) {
1484      reqnn("value", alternative);
1485      alternatives.add(alternative);
1486      return this;
1487   }
1488
1489   /**
1490    * Registers a callback that is invoked on the builder instance before {@code build()} is called.
1491    *
1492    * <p>
1493    * Multiple initializers may be registered; they are applied in registration order. The callback
1494    * receives the builder instance after it has been retrieved (from the bean store, static factory,
1495    * or constructor) but before any auto-wiring or {@link #injectBuilder() inject-based} processing.
1496    *
1497    * <p>
1498    * The callback is typed as {@code Consumer<Object>} because the builder type is generally not
1499    * statically known at the call site (an unchecked cast will typically be required inside the
1500    * lambda).
1501    *
1502    * @param initializer The initializer. Cannot be <jk>null</jk>.
1503    * @return This object.
1504    */
1505   public Builder<T> builderInitializer(Consumer<Object> initializer) {
1506      reqnn("value", initializer);
1507      builderInitializers.add(initializer);
1508      return this;
1509   }
1510
1511   /**
1512    * Enables {@code @Inject} / {@code @Autowired} field and method injection on the builder instance.
1513    *
1514    * <p>
1515    * When set, after the builder is retrieved (from the bean store, static factory, or constructor),
1516    * its fields and methods annotated with {@code @Inject} or {@code @Autowired} are auto-resolved
1517    * from the bean store. This is similar to the post-construction injection performed on the
1518    * resulting bean, but applied to the builder.
1519    *
1520    * <p>
1521    * Default is <jk>false</jk> (opt-in).
1522    *
1523    * @return This object.
1524    */
1525   public Builder<T> injectBuilder() {
1526      injectBuilder = true;
1527      return this;
1528   }
1529
1530   /**
1531    * Enables auto-wiring of bean store entries into matching setter methods on the builder.
1532    *
1533    * <p>
1534    * When set, after the builder is retrieved, every public single-argument setter method is
1535    * inspected. If the bean store has a bean of a type assignable to the setter's parameter type,
1536    * the setter is invoked with that bean.
1537    *
1538    * <p>
1539    * If a parameter is of type {@code Supplier<X>} and the bean store has a bean of type {@code X},
1540    * the bean is wrapped in a supplier and passed to the setter (when {@code unwrapSuppliers}
1541    * defaulting to true).
1542    *
1543    * <p>
1544    * Default is <jk>false</jk> (opt-in).
1545    *
1546    * @return This object.
1547    */
1548   public Builder<T> autoWireBuilder() {
1549      autoWireBuilder = true;
1550      return this;
1551   }
1552
1553   /**
1554    * Disables builder-based bean creation entirely.
1555    *
1556    * <p>
1557    * When set, builder detection is skipped and the bean is instantiated directly via static factory
1558    * methods or constructors. This is useful when a Builder class is co-located with the bean type
1559    * (e.g. {@code Foo.Builder}) but the caller wants to use a builder-taking constructor instead.
1560    *
1561    * <h5 class='section'>Example:</h5>
1562    * <p class='bjava'>
1563    *    BeanInstantiator.<jsm>of</jsm>(BasicSwaggerProvider.<jk>class</jk>, beanStore)
1564    *       .add(SwaggerProvider.Builder.<jk>class</jk>, <jv>this</jv>)
1565    *       .noBuilder()
1566    *       .run();
1567    * </p>
1568    *
1569    * @return This object.
1570    */
1571   public Builder<T> noBuilder() {
1572      noBuilder = true;
1573      return this;
1574   }
1575
1576   /**
1577    * Sets the lifecycle scope of the bean being created.
1578    *
1579    * <p>
1580    * {@link Scope#SINGLETON} is equivalent to calling {@link #cached()};
1581    * {@link Scope#PROTOTYPE} resets the cached flag.
1582    *
1583    * @param value The scope. Cannot be <jk>null</jk>.
1584    * @return This object.
1585    */
1586   public Builder<T> scope(Scope value) {
1587      reqnn("value", value);
1588      cached = (value == Scope.SINGLETON);
1589      return this;
1590   }
1591
1592   /**
1593    * Returns a copy of this builder with the same configuration.
1594    *
1595    * <p>
1596    * The returned builder shares no mutable state with this one and may be configured independently.
1597    *
1598    * @return A new builder pre-populated with this builder's configuration.
1599    */
1600   public Builder<T> copy() {
1601      var c = new Builder<T>(beanType.inner(), parentStore, name, enclosingInstance);
1602      c.beanSubType = beanSubType;
1603      c.explicitBuilderType = explicitBuilderType;
1604      c.explicitBuilder = explicitBuilder;
1605      c.explicitImplementation = explicitImplementation;
1606      c.postCreateHooks = new ArrayList<>(postCreateHooks);
1607      c.factoryMethodNames = factoryMethodNames;
1608      c.builderMethodNames = builderMethodNames;
1609      c.buildMethodNames = buildMethodNames;
1610      c.builderClassNames = builderClassNames;
1611      c.fallbackSupplier = fallbackSupplier;
1612      c.cached = cached;
1613      c.factoryAbstainOnNull = factoryAbstainOnNull;
1614      c.preferZeroArgConstructor = preferZeroArgConstructor;
1615      c.silent = silent;
1616      c.description = description;
1617      c.wrappers = new ArrayList<>(wrappers);
1618      c.validators = new ArrayList<>(validators);
1619      c.alternatives = new ArrayList<>(alternatives);
1620      c.builderInitializers = new ArrayList<>(builderInitializers);
1621      c.injectBuilder = injectBuilder;
1622      c.autoWireBuilder = autoWireBuilder;
1623      c.autoWireUnwrapSuppliers = autoWireUnwrapSuppliers;
1624      c.noBuilder = noBuilder;
1625      debug.ifPresent(x -> c.debug.set(l()));
1626      return c;
1627   }
1628
1629   /**
1630    * Holds a single validator predicate plus its error message.
1631    */
1632   private static record Validation<T>(Predicate<T> predicate, String message) {}
1633
1634   /**
1635    * Resets the builder, builderType, and beanImpl suppliers.
1636    *
1637    * <p>
1638    * This causes them to be re-evaluated on the next call to {@link #run()}.
1639    *
1640    * <h5 class='section'>Notes:</h5><ul>
1641    *    <li class='note'>If an explicit builder instance was set via {@link #builder(Object)},
1642    *       it is preserved and not cleared by this method.
1643    *    <li class='note'>If an explicit implementation was set via {@link #impl(Object)},
1644    *       it is preserved and not cleared by this method.
1645    *    <li class='note'>Only the cached results from suppliers are reset, not the configuration itself.
1646    * </ul>
1647    *
1648    * @return This object.
1649    */
1650   public Builder<T> reset() {
1651      if (explicitBuilder == null) {
1652         builderMemoizer.reset();
1653      }
1654      builderType.reset();
1655      if (explicitImplementation == null) {
1656         beanImpl.reset();
1657      }
1658      return this;
1659   }
1660
1661   /**
1662    * Finds and creates the bean implementation.
1663    *
1664    * <p>
1665    * This method contains all the logic for creating beans via builders, factory methods, constructors, or fallbacks.
1666    *
1667    * @return The created bean.
1668    * @throws ExecutableException if bean could not be created.
1669    */
1670   private T findBeanImpl() {
1671      var store2 = this.store;
1672      var builder2 = explicitBuilder != null ? explicitBuilder : this.builderMemoizer.get();  // Use explicit builder if set, otherwise get from supplier
1673      T bean = null;
1674      var methodComparator = comparing(MethodInfo::getParameterCount).reversed();
1675      var constructorComparator = comparing(ConstructorInfo::getParameterCount).reversed();
1676
1677      boolean builderAttempted = false;
1678      if (builder2 != null) {
1679         log("Builder detected: %s", cn(builder2));
1680
1681         // Initialization hooks: builder initializers, then optional inject, then optional auto-wire.
1682         for (var init : builderInitializers)
1683            init.accept(builder2);
1684         if (injectBuilder) {
1685            log("Injecting builder via @Inject/@Autowired");
1686            inject(builder2);
1687         }
1688         if (autoWireBuilder) {
1689            log("Auto-wiring builder setters from bean store");
1690            autoWireBuilder(builder2, store2);
1691         }
1692
1693         // Look for Builder.build()/create()/get() - REQUIRED
1694         // Uses buildMethodNames configuration (defaults to "build", "create", "get")
1695         // Check declared methods first to get the most specific override, then fall back to public methods
1696         var buildMethod = info(builder2.getClass()).getPublicMethods().stream()
1697            .filter(x -> x.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
1698            .filter(x -> buildMethodNames.contains(x.getNameSimple()))
1699            .filter(x -> o(x).map(x2 -> x2.getReturnType()).filter(x2 -> x2.is(beanSubType.inner()) || x2.isParentOf(beanSubType)).isPresent()) // Accept methods that return beanSubType or a parent type of beanSubType
1700            .filter(x -> x.getAnnotations().stream().anyMatch(JsrSupport::isInjectAnnotation) ? x.canResolveAllParameters(store2) : x.getParameterCount() == 0)
1701            .sorted(methodComparator)
1702            .findFirst();
1703
1704         if (buildMethod.isPresent()) {
1705            var method = buildMethod.get();
1706            log("Found build method: %s", method.getNameFull());
1707            log("Method declaring class: %s", method.getDeclaringClass().getName());
1708            log("Builder class: %s", cn(builder2));
1709            var returnType = method.getReturnType();
1710            log("Method return type: %s", returnType.getName());
1711            log("Expected beanSubType: %s", beanSubType.getName());
1712
1713            // Check if method has @Inject annotation
1714            boolean hasInject = method.getAnnotations().stream().anyMatch(JsrSupport::isInjectAnnotation);
1715            if (hasInject) {
1716               log("Method has @Inject annotation, resolving parameters from bean store");
1717            } else {
1718               log("Method has no parameters, calling without injection");
1719            }
1720
1721            builderAttempted = true;
1722            // Call the builder method
1723            Object builtBean = method.inject(store2, builder2);
1724
1725            // Validate the produced bean (loose-builder mode). We accept the result if either:
1726            //   1. The build method's declared return type is exactly beanSubType, OR
1727            //   2. The runtime instance produced by the build method is assignment-compatible with beanSubType.
1728            // Case 2 supports the legacy pattern where a parent class's Builder.build() declares the parent
1729            // type but constructs a configured subclass at runtime because the builder's internal
1730            // type-binding was preset.
1731            // If neither matches, we fall through to factory-method / constructor resolution on beanSubType
1732            // rather than throwing — that lets standard "subclass adds its own constructor" patterns succeed.
1733            if (returnType.is(beanSubType.inner()) || (builtBean != null && beanSubType.inner().isInstance(builtBean))) {
1734               bean = (T)beanType.cast(builtBean);
1735            } else {
1736               log("Builder method %s returned %s but expected %s; falling through to factory methods/constructors", method.getNameFull(), builtBean == null ? "null" : cn(builtBean), beanSubType.getName());
1737            }
1738         }
1739
1740         if (bean != null) {
1741            log("Bean created successfully via builder");
1742            bean = inject(bean);
1743            runPostCreateHooks(bean);
1744            return bean;
1745         }
1746
1747         // Builder was found but no appropriate build method was found
1748         // Try fallback: look for ANY method on builder that returns the exact bean type
1749         log("Attempting Builder.anything()");
1750         var anyMethod = info(builder2.getClass()).getPublicMethods().stream()
1751            .filter(x -> x.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
1752            .filter(x -> !buildMethodNames.contains(x.getNameSimple())) // Skip standard build methods we already checked
1753            .filter(x -> x.getReturnType().is(beanSubType.inner())) // Must return exact beanSubType, not parent
1754            .filter(x -> x.getAnnotations().stream().anyMatch(JsrSupport::isInjectAnnotation) ? x.canResolveAllParameters(store2) : x.getParameterCount() == 0)
1755            .sorted(methodComparator)
1756            .findFirst();
1757
1758         if (anyMethod.isPresent()) {
1759            var method = anyMethod.get();
1760            log("Found builder method returning bean type: %s", method.getNameFull());
1761            var returnType = method.getReturnType();
1762
1763            // Call the builder method
1764            Object builtBean = method.inject(store2, builder2);
1765
1766            // Builder method must return the exact beanSubType
1767            if (!returnType.is(beanSubType.inner())) {
1768               log("Builder method %s returns %s, but must return %s", method.getNameFull(), returnType.getName(), beanSubType.getName());
1769               throw exex("Builder method %s returns %s, but must return %s. Builder build methods must always return the exact bean subtype being created.", method.getNameFull(), returnType.getName(), beanSubType.getName());
1770            }
1771            bean = (T)beanType.cast(builtBean);
1772
1773            if (bean != null) {
1774               log("Bean created successfully via builder fallback method");
1775               bean = inject(bean);
1776               runPostCreateHooks(bean);
1777               return bean;
1778            }
1779         }
1780
1781         // Check if builder type has ANY method that returns the right type (even if not named build/create/get)
1782         // If so, log that builder was detected but no appropriate build method was found
1783         // Otherwise, log that builder type is invalid
1784         var builderTypeInfo = builderType.get();
1785         boolean hasAnyMethodWithRightReturnType = builderTypeInfo != null && builderTypeInfo.getPublicMethods().stream()
1786            .filter(x -> x.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
1787            .map(x -> x.getReturnType())
1788            .anyMatch(x -> x.isAssignableFrom(beanSubType));
1789         boolean hasBuildMethodWithRightReturnType = builderTypeInfo != null && builderTypeInfo.getPublicMethods().stream()
1790            .filter(x -> x.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
1791            .filter(x -> buildMethodNames.contains(x.getNameSimple()))
1792            .map(x -> x.getReturnType())
1793            .anyMatch(x -> x.isAssignableFrom(beanSubType));
1794         boolean isExplicitBuilder = explicitBuilderType != null || explicitBuilder != null;
1795         boolean isValidBuilder = builderTypeInfo != null && isValidBuilderType(builderTypeInfo);
1796
1797         if (hasAnyMethodWithRightReturnType && !hasBuildMethodWithRightReturnType) {
1798            // Builder has a method returning the right type but not with the expected name
1799            log("Builder detected but no appropriate build method found. Builder type: %s. Expected method names: %s", cn(builder2), buildMethodNames);
1800            // Fall through to factory methods/constructors
1801         } else if (builderAttempted) {
1802            // We tried the builder's build method but it produced a runtime instance that wasn't the expected
1803            // beanSubType (loose-builder fallthrough above).  Don't throw; fall through to factory-method /
1804            // constructor resolution on beanSubType so subclasses with their own constructor/factory still resolve.
1805            log("Builder %s build method produced wrong runtime type; falling through to factory methods/constructors", cn(builder2));
1806         } else if (hasBuildMethodWithRightReturnType || isExplicitBuilder || isValidBuilder) {
1807            // Builder has a build method with right return type (even if can't be called) or was explicitly set
1808            // Throw exception unless fallback exists
1809            log("Builder detected but no appropriate build method found. Builder type: %s. Expected method names: %s", cn(builder2), buildMethodNames);
1810
1811            if (fallbackSupplier != null) {
1812               log(CONST_usingFallbackSupplier);
1813               T fallbackBean = fallbackSupplier.get();
1814               runPostCreateHooks(fallbackBean);
1815               return fallbackBean;
1816            }
1817
1818            log("Failed to create bean using builder");
1819            throw exex("Could not instantiate class %1$s using builder type %2$s. Builder must have a build(), create(), or get() method that returns %1$s. The method may have @Inject annotation to allow injected parameters.", beanSubType.getName(), builderTypeInfo != null ? builderTypeInfo.getName() : cn(builder2));
1820         } else {
1821            // Builder type is invalid and was auto-detected - fall through to factory methods/constructors
1822            log("Builder detected but builder type is invalid. Builder type: %s. Falling back to factory methods/constructors.", cn(builder2));
1823         }
1824      }
1825
1826      // Look for Bean.factoryMethod().
1827      log("Attempting Bean.factoryMethod()");
1828      // If builder was detected but has no build method, pass it as extra bean for factory methods
1829      Object[] factoryMethodExtraBeans = builder2 != null ? new Object[]{builder2} : new Object[0];
1830      var factoryMethod = beanSubType.getPublicMethods().stream()
1831         .filter(x -> x.isAll(STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
1832         .filter(x -> factoryMethodNames.contains(x.getNameSimple()))
1833         .filter(x -> x.hasReturnType(beanSubType))
1834         .filter(x -> x.canResolveAllParameters(store2, factoryMethodExtraBeans))
1835         .sorted(methodComparator)
1836         .findFirst();
1837      if (factoryMethod.isPresent()) {
1838         log("Found factory method: %s", factoryMethod.get().getNameFull());
1839         bean = (T) beanType.cast(factoryMethod.get().inject(store2, null, factoryMethodExtraBeans));
1840         // When factoryAbstainOnNull is set, treat a null result from the factory method as a
1841         // deliberate "abstain" signal and return null without falling through to constructor
1842         // lookup.  Used by callers like RestOpArg subclasses whose create(ParameterInfo)
1843         // returns null when the parameter isn't annotated with the marker the subclass
1844         // handles (e.g. AttributeArg.create() returns null when @Attr isn't present).
1845         if (bean == null && factoryAbstainOnNull)
1846            return null;
1847      }
1848
1849      // Look for Bean().
1850      // Skip constructor invocation when beanSubType is abstract or an interface — Class.newInstance() would
1851      // throw InstantiationException, masking the more useful "class is abstract"/"class is an interface"
1852      // fallthrough below (which honors the registered fallback supplier when present).
1853      if (bean == null && ! beanSubType.isInterface() && ! beanSubType.isAbstract()) {
1854         log("Attempting Bean() constructor");
1855         // If preferZeroArgConstructor is set and a no-arg constructor exists, short-circuit to it.
1856         // This skips the longest-resolvable-constructor heuristic for callers that just want a fresh
1857         // empty instance — see preferZeroArgConstructor() Javadoc for the parameterized-container rationale.
1858         if (preferZeroArgConstructor) {
1859            var zeroArgCtor = beanSubType.getPublicConstructors().stream()
1860               .filter(x -> x.isAll(NOT_DEPRECATED))
1861               .filter(x -> x.isDeclaringClass(beanSubType))
1862               .filter(x -> x.getParameterCount() == 0)
1863               .findFirst();
1864            if (zeroArgCtor.isPresent()) {
1865               log("Using zero-arg constructor (preferZeroArgConstructor): %s", zeroArgCtor.get().getNameFull());
1866               bean = (T) beanType.cast(zeroArgCtor.get().inject(store2, enclosingInstance));
1867            }
1868         }
1869         // If builder was detected but has no build method, pass it as extra bean for constructors
1870         Object[] constructorExtraBeans = builder2 != null ? new Object[]{builder2} : new Object[0];
1871         if (bean == null) {
1872            bean = beanSubType.getPublicConstructors().stream()
1873               .filter(x -> x.isAll(NOT_DEPRECATED))
1874               .filter(x -> x.isDeclaringClass(beanSubType))
1875               .filter(x -> x.canResolveAllParameters(store2, enclosingInstance, constructorExtraBeans))
1876               .sorted(constructorComparator)
1877               .findFirst()
1878               .map(x -> {
1879                  log("Found constructor: %s", x.getNameFull());
1880                  return (T)beanType.cast(x.inject(store2, enclosingInstance, constructorExtraBeans));
1881               })
1882               .orElse(null);
1883         }
1884         // Fallback: try protected constructors (legacy BeanCreator behavior).
1885         // Many framework classes use a protected (Builder) constructor with public static create() factory.
1886         if (bean == null) {
1887            bean = beanSubType.getDeclaredConstructors().stream()
1888               .filter(x -> x.isAll(PROTECTED, NOT_DEPRECATED))
1889               .filter(x -> x.isDeclaringClass(beanSubType))
1890               .filter(x -> x.canResolveAllParameters(store2, enclosingInstance, constructorExtraBeans))
1891               .sorted(constructorComparator)
1892               .findFirst()
1893               .map(x -> {
1894                  log("Found protected constructor: %s", x.getNameFull());
1895                  return (T)beanType.cast(x.inject(store2, enclosingInstance, constructorExtraBeans));
1896               })
1897               .orElse(null);
1898         }
1899         // Fallback: try package-private constructors.
1900         // Useful for @Configuration classes and other framework types that intentionally restrict
1901         // instantiation to their declaring package but still want to participate in the bean store.
1902         // Private constructors are deliberately excluded — they signal "do not instantiate".
1903         if (bean == null) {
1904            bean = beanSubType.getDeclaredConstructors().stream()
1905               .filter(x -> x.isAll(NOT_PUBLIC, NOT_PROTECTED, NOT_PRIVATE, NOT_DEPRECATED))
1906               .filter(x -> x.isDeclaringClass(beanSubType))
1907               .filter(x -> x.canResolveAllParameters(store2, enclosingInstance, constructorExtraBeans))
1908               .sorted(constructorComparator)
1909               .findFirst()
1910               .map(x -> {
1911                  log("Found package-private constructor: %s", x.getNameFull());
1912                  return (T)beanType.cast(x.inject(store2, enclosingInstance, constructorExtraBeans));
1913               })
1914               .orElse(null);
1915         }
1916      }
1917
1918      if (bean != null) {
1919         log("Bean created successfully");
1920         bean = inject(bean);
1921         runPostCreateHooks(bean);
1922         return bean;
1923      }
1924
1925      if (beanSubType.isInterface()) {
1926         log("Bean type is an interface");
1927         if (fallbackSupplier != null) {
1928            log(CONST_usingFallbackSupplier);
1929            T fallbackBean = fallbackSupplier.get();
1930            runPostCreateHooks(fallbackBean);
1931            return fallbackBean;
1932         }
1933         throw exex("Could not instantiate class %s: %s.", beanSubType.getName(), "Class is an interface");
1934      }
1935
1936      if (beanSubType.isAbstract()) {
1937         log("Bean type is abstract");
1938         if (fallbackSupplier != null) {
1939            log(CONST_usingFallbackSupplier);
1940            T fallbackBean = fallbackSupplier.get();
1941            runPostCreateHooks(fallbackBean);
1942            return fallbackBean;
1943         }
1944         throw exex("Could not instantiate class %s: %s.", beanSubType.getName(), "Class is abstract");
1945      }
1946
1947      if (fallbackSupplier != null) {
1948         log(CONST_usingFallbackSupplier);
1949         T fallbackBean = fallbackSupplier.get();
1950         runPostCreateHooks(fallbackBean);
1951         return fallbackBean;
1952      }
1953
1954      log("Failed to create bean: no methods/constructors found with matching parameters");
1955      throw exex("Could not instantiate class %s:  No methods/constructors found with matching parameters", beanSubType.getName());
1956   }
1957
1958   /**
1959    * Finds all bean subtypes, including beanSubType, beanType, and all classes in between.
1960    *
1961    * <p>
1962    * This method traverses the hierarchy from {@code beanSubType} up to {@code beanType}, collecting all classes
1963    * in the inheritance chain. This allows parent bean types to be resolved as injectable types.
1964    *
1965    * <h5 class='section'>Example:</h5>
1966    * <p class='bjava'>
1967    *    <jc>// Class hierarchy</jc>
1968    *    <jk>class</jk> A { }
1969    *    <jk>class</jk> B <jk>extends</jk> A { }
1970    *    <jk>class</jk> C <jk>extends</jk> B { }
1971    *
1972    *    <jc>// When creating C with beanType=A, beanSubType=C</jc>
1973    *    <jc>// This method returns: [C, B, A]</jc>
1974    * </p>
1975    *
1976    * @return A list of bean subtypes from beanSubType to beanType, never <jk>null</jk>.
1977    */
1978   private List<ClassInfo> findBeanSubTypes() {
1979      var result = new ArrayList<ClassInfo>();
1980
1981      // If beanSubType is the same as beanType, just return a single-element list
1982      if (beanSubType.is(beanType.inner())) {
1983         result.add(beanSubType);
1984         return result;
1985      }
1986
1987      // Traverse from beanSubType up to beanType
1988      var parents = beanSubType.getParents();
1989      for (var parent : parents) {
1990         result.add(parent);
1991         // Stop when we reach beanType
1992         if (parent.is(beanType.inner())) {
1993            break;
1994         }
1995      }
1996
1997      // This should never happen if beanSubType validation is correct, but check anyway
1998      if (result.isEmpty() || !result.get(result.size() - 1).is(beanType.inner())) {
1999         throw iaex("beanType %s was not found in the parent hierarchy of beanSubType %s. This indicates a validation error.", beanType.getName(), beanSubType.getName());
2000      }
2001
2002      return result;
2003   }
2004
2005   private Object findBuilder() {
2006
2007      // noBuilder() short-circuits all builder detection.
2008      if (noBuilder)
2009         return null;
2010
2011      var bs = store;
2012      var builderType2 = this.builderType.get();
2013
2014      // If no builder type was determined, return null (builder not needed)
2015      if (builderType2 == null)
2016         return null;
2017
2018      Optional<Object> r;
2019
2020      // Step 0: Look for a pre-registered builder of the builder type in the bean store.
2021      // This handles scenarios where the caller has already configured a builder externally
2022      // (e.g. legacy framework code that registers a configured builder bean).
2023      r = bs.getBean(builderType2.inner()).map(Object.class::cast);
2024      if (r.isPresent()) {
2025         log("Using pre-registered builder from bean store: %s", builderType2.getName());
2026         return r.get();
2027      }
2028
2029      // Step 1: Look for a static create/builder method that returns the builder type
2030      r = beanSubType.getPublicMethods().stream()
2031         .filter(x -> x.isAll(STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
2032         .filter(x -> builderMethodNames.contains(x.getNameSimple()))
2033         .filter(x -> x.hasReturnType(builderType2))
2034         .filter(x -> x.canResolveAllParameters(bs, enclosingInstance))
2035         .sorted(comparing(MethodInfo::getParameterCount).reversed())
2036         .findFirst()
2037         .map(x -> inject(x.inject(bs, enclosingInstance)));
2038      if (r.isPresent())
2039         return r.get();
2040
2041      // Step 2: Look for a public constructor on the builder type
2042      r = builderType2.getPublicConstructors().stream()
2043         .filter(x -> x.isAll(NOT_DEPRECATED))
2044         .filter(x -> x.canResolveAllParameters(bs, enclosingInstance))
2045         .sorted(comparing(ConstructorInfo::getParameterCount).reversed())
2046         .findFirst()
2047         .map(x -> inject(x.inject(bs, enclosingInstance)));
2048
2049      if (r.isPresent())
2050         return r.get();
2051
2052      // Step 3: Look for a protected constructor on the builder type
2053      r = builderType2.getDeclaredConstructors().stream()
2054         .filter(x -> x.isAll(ElementFlag.PROTECTED, NOT_DEPRECATED))
2055         .filter(x -> x.canResolveAllParameters(bs, enclosingInstance))
2056         .sorted(comparing(ConstructorInfo::getParameterCount).reversed())
2057         .findFirst()
2058         .map(x -> inject(x.inject(bs, enclosingInstance)));
2059      if (r.isPresent())
2060         return r.get();
2061
2062      // Step 4: Look for a package-private constructor on the builder type.
2063      // Mirrors the package-private fallback in findBeanImpl() for symmetry: any builder co-located in
2064      // the bean's declaring package can be discovered without forcing the constructor to be public/protected.
2065      r = builderType2.getDeclaredConstructors().stream()
2066         .filter(x -> x.isAll(NOT_PUBLIC, NOT_PROTECTED, NOT_PRIVATE, NOT_DEPRECATED))
2067         .filter(x -> x.canResolveAllParameters(bs, enclosingInstance))
2068         .sorted(comparing(ConstructorInfo::getParameterCount).reversed())
2069         .findFirst()
2070         .map(x -> inject(x.inject(bs, enclosingInstance)));
2071
2072      return r.orElse(null);
2073   }
2074
2075   /**
2076    * Finds the builder type based on priority:
2077    * 1. Explicitly set builderType
2078    * 2. @Builder annotation on beanSubType (includes inherited annotations)
2079    * 3. Autodetect from static methods or inner classes
2080    */
2081   private ClassInfo findBuilderType() {
2082      log("Finding builder type...");
2083
2084      // noBuilder() short-circuits all builder detection.
2085      if (noBuilder) {
2086         log("noBuilder() set; skipping builder type detection");
2087         return null;
2088      }
2089
2090      // Priority 1: Explicitly set builderType
2091      if (explicitBuilderType != null) {
2092         log("Using explicitly set builder type: %s", explicitBuilderType.getName());
2093         return explicitBuilderType;
2094      }
2095
2096      // Priority 2: @Builder annotation on beanSubType
2097      // Check declared annotations first to ensure child's annotation overrides parent's
2098      var r = beanSubType.getDeclaredAnnotations().stream()
2099         .filter(a -> a.isType(org.apache.juneau.commons.Builder.class))
2100         .map(a -> (AnnotationInfo<org.apache.juneau.commons.Builder>)a)
2101         .map(x -> ClassInfo.class.cast(info(x.inner().value())))
2102         .findFirst();
2103      if (r.isPresent()) {
2104         log("Found builder via @Builder annotation on beanSubType: %s", r.get().getName());
2105         return r.get();
2106      }
2107      // Fall back to inherited annotations if no declared annotation found
2108      r = beanSubType.getAnnotations(org.apache.juneau.commons.Builder.class)
2109         .map(x -> ClassInfo.class.cast(info(x.inner().value())))
2110         .findFirst();
2111      if (r.isPresent()) {
2112         log("Found builder via @Builder annotation (inherited): %s", r.get().getName());
2113         return r.get();
2114      }
2115
2116      // Priority 3: Autodetect
2117      // 3a: Look for static create/builder method that returns a potential builder type.
2118      var staticBuilderTypes = beanSubType.getPublicMethods().stream()
2119         .filter(x -> x.isAll(STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
2120         .filter(x -> builderMethodNames.contains(x.getNameSimple()))
2121         .filter(x -> ! x.hasReturnType(beanSubType)) // Must not return the bean type itself
2122         .filter(x -> isValidBuilderType(x.getReturnType()))
2123         .map(MethodInfo::getReturnType)
2124         .toList();
2125      if (! staticBuilderTypes.isEmpty()) {
2126         // Prefer a factory whose builder builds the EXACT requested type (or a
2127         // subtype) over one that only promises a supertype — e.g. an inherited generic builder(Class)
2128         // whose build() erases to a base class. A supertype-only factory must not displace a stricter path.
2129         var strict = staticBuilderTypes.stream().filter(this::isStrictBuilderType).findFirst();
2130         if (strict.isPresent()) {
2131            log("Found builder via static method (strict): %s", strict.get().getName());
2132            return strict.get();
2133         }
2134         var weak = gateWeakBuilder(staticBuilderTypes.get(0));
2135         if (weak != null) {
2136            log("Found builder via static method: %s", weak.getName());
2137            return weak;
2138         }
2139         // Weak/supertype-only static factory rejected in favor of a direct constructor; fall through to
2140         // 3b/3c (a stricter inner Builder may still exist) and ultimately the constructor path.
2141      }
2142
2143      // 3b: Look for inner Builder class
2144      log("Looking for inner builder class (names: %s)...", builderClassNames);
2145      r = beanSubType.getDeclaredMemberClasses().stream()
2146         .filter(x -> builderClassNames.contains(x.getNameSimple()))
2147         .findFirst();
2148      if (r.isPresent()) {
2149         var builderClass = r.get();
2150         if (isValidBuilderType(builderClass)) {
2151            var gated = isStrictBuilderType(builderClass) ? builderClass : gateWeakBuilder(builderClass);
2152            if (gated != null) {
2153               log("Found builder via inner class: %s", gated.getName());
2154               return gated;
2155            }
2156            // Weak inner builder rejected in favor of a direct constructor; fall through.
2157         } else {
2158            // Still return it so we can provide a better error message when trying to use it
2159            log("Found builder class via inner class but it is not valid: %s", builderClass.getName());
2160            return builderClass;
2161         }
2162      }
2163
2164      // 3c: Look for builder in parent classes and implemented interfaces (skip beanSubType itself,
2165      // which was already searched in 3b). getAllParents returns parents (child-to-parent) followed by
2166      // interfaces, so we walk the whole chain in a uniform order.
2167      var allParents = beanSubType.getAllParents();
2168      for (int i = 1; i < allParents.size(); i++) {
2169         var parentClass = allParents.get(i);
2170         r = parentClass.getDeclaredMemberClasses().stream()
2171            .filter(x -> builderClassNames.contains(x.getNameSimple()))
2172            .findFirst();
2173         if (r.isPresent()) {
2174            var builderClass = r.get();
2175            if (isValidBuilderType(builderClass)) {
2176               var gated = isStrictBuilderType(builderClass) ? builderClass : gateWeakBuilder(builderClass);
2177               if (gated != null) {
2178                  log("Found builder via parent inner class: %s", gated.getName());
2179                  return gated;
2180               }
2181               // Weak parent inner builder rejected in favor of a direct constructor; keep scanning.
2182               continue;
2183            }
2184            // Still return it so we can provide a better error message when trying to use it
2185            log("Found builder class via parent inner class but it is not valid: %s", builderClass.getName());
2186            return builderClass;
2187         }
2188      }
2189
2190      // No builder type found
2191      log("No builder type found");
2192      return null;
2193   }
2194
2195   /**
2196    * Tests whether a builder candidate builds the <b>exact</b> requested bean type (or a subtype of it),
2197    * as opposed to only promising a supertype.
2198    *
2199    * <p>
2200    * Used by {@link #findBuilderType()} to prefer a precise builder over an inherited
2201    * generic base builder whose {@code build()} erases to a parent class (e.g. a self-typed REST
2202    * {@code DefaultBuilder} whose {@code build()} returns {@code RestMixin}).
2203    *
2204    * @param builderCandidate The builder type to test.
2205    * @return <jk>true</jk> if the candidate has a 0-arg (or {@code @Inject}) build/create/get method whose
2206    *    return type is {@code beanSubType} or a subtype of it.
2207    */
2208   private boolean isStrictBuilderType(ClassInfo builderCandidate) {
2209      return builderCandidate.getPublicMethods().stream()
2210         .filter(x -> x.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
2211         .filter(x -> buildMethodNames.contains(x.getNameSimple()))
2212         .filter(x -> { var rt = x.getReturnType(); return rt.is(beanSubType.inner()) || beanSubType.isParentOf(rt); })
2213         .anyMatch(x -> x.getAnnotations().stream().anyMatch(JsrSupport::isInjectAnnotation) || x.getParameterCount() == 0);
2214   }
2215
2216   /**
2217    * A builder candidate that only builds a <i>supertype</i> of the requested bean
2218    * type must not be used when the requested type is concretely instantiable via a direct constructor.
2219    *
2220    * <p>
2221    * Returns the candidate unchanged when it is safe to use, or {@code null} to signal "prefer the
2222    * constructor path". This is only invoked for non-strict (supertype-only) candidates.
2223    *
2224    * @param builderCandidate The (weak) builder candidate.
2225    * @return The candidate, or {@code null} to prefer the direct-constructor path.
2226    */
2227   private ClassInfo gateWeakBuilder(ClassInfo builderCandidate) {
2228      if (hasUsableDirectConstructor()) {
2229         log("Builder candidate %s only builds a supertype of %s and a usable direct constructor exists; preferring the constructor.", builderCandidate.getName(), beanSubType.getName());
2230         return null;
2231      }
2232      return builderCandidate;
2233   }
2234
2235   /**
2236    * Tests whether the requested bean type is concretely instantiable via a non-private constructor whose
2237    * parameters can all be resolved from the bean store (a no-arg constructor always qualifies).
2238    *
2239    * @return <jk>true</jk> if a usable direct constructor exists on {@code beanSubType}.
2240    */
2241   private boolean hasUsableDirectConstructor() {
2242      if (beanSubType.isAbstract() || beanSubType.isInterface())
2243         return false;
2244      return beanSubType.getDeclaredConstructors().stream()
2245         .filter(x -> x.isAll(NOT_DEPRECATED, NOT_PRIVATE))
2246         .filter(x -> x.isDeclaringClass(beanSubType))
2247         .anyMatch(x -> x.canResolveAllParameters(store, enclosingInstance));
2248   }
2249
2250   /**
2251    * Finds all builder types, including the primary builder type and any builder types found in the builder's parent hierarchy.
2252    *
2253    * <p>
2254    * This method traverses the parent hierarchy of the primary builder type, looking for builder classes whose build methods
2255    * return types that are the same as or parents of {@code beanType}. This allows parent builders to be resolved as injectable types.
2256    *
2257    * <h5 class='section'>Example:</h5>
2258    * <p class='bjava'>
2259    *    <jc>// Class A with builder</jc>
2260    *    <jk>class</jk> A {
2261    *       <jk>static class</jk> Builder {
2262    *          A create() { <jk>return new</jk> A(); }
2263    *       }
2264    *    }
2265    *
2266    *    <jc>// Class B extends A with builder</jc>
2267    *    <jk>class</jk> B <jk>extends</jk> A {
2268    *       <jk>static class</jk> Builder <jk>extends</jk> A.Builder {
2269    *          B create() { <jk>return new</jk> B(); }
2270    *       }
2271    *    }
2272    *
2273    *    <jc>// When creating B, this method returns: [B.Builder, A.Builder]</jc>
2274    *    <jc>// Traverses B.Builder → A.Builder hierarchy</jc>
2275    *    <jc>// B.Builder.create() returns B (matches beanType)</jc>
2276    *    <jc>// A.Builder.create() returns A (A is parent of B)</jc>
2277    * </p>
2278    *
2279    * @return A list of builder types, never <jk>null</jk>.
2280    */
2281   private List<ClassInfo> findBuilderTypes() {
2282      var result = new ArrayList<ClassInfo>();
2283
2284      // Start with the primary builder type
2285      var primaryBuilderType = findBuilderType();
2286      if (primaryBuilderType == null) {
2287         return result;
2288      }
2289
2290      // Add the primary builder type first
2291      result.add(primaryBuilderType);
2292
2293      // Traverse the parent hierarchy of the builder type itself (skip the first element which is the primary builder type itself)
2294      var builderParents = primaryBuilderType.getParents();
2295      for (int i = 0; i < builderParents.size(); i++) {
2296         var builderParent = builderParents.get(i);
2297         builderParent.getPublicMethods().stream()
2298            .filter(m -> m.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
2299            .filter(m -> buildMethodNames.contains(m.getNameSimple()))
2300            .filter(m -> m.getReturnType().isParentOf(beanType))
2301            .findFirst()
2302            .ifPresent(x -> result.add(builderParent));
2303      }
2304
2305      return result;
2306   }
2307
2308   /**
2309    * Helper method to inject beans into an object instance.
2310    *
2311    * <p>
2312    * If the instantiator's <b>declared target type</b> ({@code beanType.inner()}) carries a class-level
2313    * {@link ConfigProperties @ConfigProperties} annotation, also runs {@link ConfigPropertiesBinder} against
2314    * {@code bean} under the annotation's prefix. The bind is performed <i>between</i> {@code @Value}/{@code @Inject}
2315    * field-and-method injection and {@code @PostConstruct} dispatch (via
2316    * {@link ClassInfo#inject(Object, BeanStore, Runnable)}), so a {@code @PostConstruct} method observes the
2317    * fully-bound config fields. The instantiator's internal {@link #store} is handed to the binder so
2318    * caller-registered {@link org.apache.juneau.commons.settings.PropertySource PropertySource} beans participate
2319    * ahead of the global {@link org.apache.juneau.commons.settings.Settings Settings} chain.
2320    *
2321    * <p>
2322    * The bound instance is then auto-registered into the parent {@link BeanStore} — under the declared target type,
2323    * as a {@linkplain WritableBeanStore#addDefaultSupplier(Class, Supplier) tier-4 default supplier} so a caller's
2324    * explicit registration of the same type is never shadowed — when that store is a writable, non-shared store.
2325    * The annotation is <b>not inherited</b>: a subclass or interface implementor of an annotated type does not
2326    * trigger binding or registration.
2327    *
2328    * @param <T2> The bean type.
2329    * @param bean The object to inject beans into.
2330    * @return The same bean instance (for method chaining).
2331    */
2332   private <T2> T2 inject(T2 bean) {
2333      // Probe the DECLARED target type directly (not a stream over the inherited annotation set).
2334      // @ConfigProperties is not @Inherited, so getAnnotation() on the declared type intentionally returns null for
2335      // a subclass/implementor of an annotated type, and the isInstance() guard skips builders and other
2336      // non-target objects that pass through this shared helper.
2337      var cp = beanType.inner().getAnnotation(ConfigProperties.class);
2338      if (cp == null || ! beanType.inner().isInstance(bean))
2339         return ClassInfo.of(bean).inject(bean, store);
2340      // Bind between field/method injection and @PostConstruct. The binder defaults to Settings.get() and
2341      // sources the passed store's PropertySource beans ahead of it, so no explicit .settings(...) is needed.
2342      var bean2 = ClassInfo.of(bean).inject(bean, store, () ->
2343         ConfigPropertiesBinder.of(bean, cp.prefix())
2344            .beanStore(store)
2345            .relaxed(cp.relaxed())
2346            .run());
2347      registerConfigBean(bean2);
2348      return bean2;
2349   }
2350
2351   /**
2352    * Registers a freshly-bound {@code @ConfigProperties} instance under the instantiator's declared target type.
2353    *
2354    * <p>
2355    * Registration is a no-op when the parent store is <jk>null</jk> or read-only (not a
2356    * {@link WritableBeanStore}), or when it's the shared read-only {@link BasicBeanStore#INSTANCE} — binding
2357    * still occurs regardless. When it does register, it uses a
2358    * {@linkplain WritableBeanStore#addDefaultSupplier(Class, Supplier) default supplier} so a caller's explicit
2359    * same-type registration takes precedence, registered under the declared target type.
2360    *
2361    * @param bean The bound instance (guaranteed by the caller to be an instance of {@code beanType.inner()}).
2362    */
2363   private void registerConfigBean(Object bean) {
2364      if (parentStore instanceof WritableBeanStore ws && ws != BasicBeanStore.INSTANCE) {
2365         var declaredType = beanType.inner();
2366         ws.addDefaultSupplier(declaredType, () -> declaredType.cast(bean));
2367      }
2368   }
2369
2370   /**
2371    * Auto-wires bean store entries into matching public single-argument setter methods of the builder.
2372    *
2373    * <p>
2374    * Setters are public, non-static, non-deprecated methods taking exactly one argument. The bean store
2375    * is queried for a bean assignable to the parameter's type. If the parameter is {@code Supplier<X>}
2376    * and {@link #autoWireUnwrapSuppliers} is true, the bean of type {@code X} is wrapped before being
2377    * passed in.
2378    *
2379    * <p>
2380    * Setters whose argument cannot be resolved are silently skipped.
2381    */
2382   @SuppressWarnings({
2383      "java:S3011" // Reflection access override is required for invoking discovered setters uniformly.
2384   })
2385   private void autoWireBuilder(Object builder2, BeanStore bs) {
2386      var ci = info(builder2.getClass());
2387      ci.getPublicMethods().stream()
2388         .filter(m -> m.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
2389         .filter(m -> m.getParameterCount() == 1)
2390         .forEach(m -> {
2391            try {
2392               var p0 = m.getParameterTypes().get(0);
2393               Class<?> rawType = p0.inner();
2394               Object value = null;
2395               if (autoWireUnwrapSuppliers && Supplier.class.isAssignableFrom(rawType)) {
2396                  // Best-effort: try to resolve the wrapped type from the parameter's parameterized signature.
2397                  var generic = m.inner().getGenericParameterTypes()[0];
2398                  if (generic instanceof java.lang.reflect.ParameterizedType pt) {
2399                     var args = pt.getActualTypeArguments();
2400                     if (args.length == 1 && args[0] instanceof Class<?> wrapped) {
2401                        var wrappedBean = bs.getBean(wrapped);
2402                        if (wrappedBean.isPresent()) {
2403                           Object wb = wrappedBean.get();
2404                           value = (Supplier<Object>) () -> wb;
2405                        }
2406                     }
2407                  }
2408               } else {
2409                  var bean = bs.getBean(rawType);
2410                  if (bean.isPresent())
2411                     value = bean.get();
2412               }
2413               if (value != null) {
2414                  m.inner().setAccessible(true);
2415                  m.inner().invoke(builder2, value);
2416                  log("Auto-wired setter %s with bean of type %s", m.getNameSimple(), rawType.getName());
2417               }
2418            } catch (@SuppressWarnings("unused") Exception e) {
2419               // Swallow auto-wire errors silently; auto-wire is a best-effort.
2420            }
2421         });
2422   }
2423
2424   /**
2425    * Checks if a type can be used as a builder.
2426    * A valid builder type must have:
2427    * - A build/create/get method that returns the bean type or a parent of the bean type
2428    *   - The method may have @Inject annotation to allow injected parameters
2429    *   - If no @Inject annotation, the method must have no parameters
2430    */
2431   private boolean isValidBuilderType(ClassInfo builderCandidate) {
2432      log("Validating builder candidate: %s", builderCandidate.getName());
2433
2434      // Check if it has a build/create/get method that returns beanSubType, beanType, or a parent of beanSubType
2435      var hasBuildMethod = builderCandidate.getPublicMethods().stream()
2436         .filter(x -> x.isAll(NOT_STATIC, NOT_DEPRECATED, NOT_SYNTHETIC, NOT_BRIDGE))
2437         .filter(x -> buildMethodNames.contains(x.getNameSimple()))
2438         .filter(x -> {
2439            var returnType = x.getReturnType();
2440            return returnType.is(beanSubType.inner()) || returnType.is(beanType.inner()) || returnType.isParentOf(beanSubType);
2441         })
2442         .anyMatch(x -> x.getAnnotations().stream().anyMatch(JsrSupport::isInjectAnnotation) || x.getParameterCount() == 0);
2443
2444      if (hasBuildMethod) {
2445         log("Builder is valid: has build/create/get method returning bean type");
2446         return true;
2447      }
2448
2449      log("Builder is NOT valid: no build/create/get method returning bean type");
2450      return false;
2451   }
2452
2453   private void log(String message, Object... args) {
2454      if ((silent || ! logger.isLoggable(java.util.logging.Level.FINE)) && ! debug.isPresent())
2455         return;
2456      var prefix = description != null ? (description + " (" + beanType.getName() + ")") : beanType.getName();
2457      logger.fine(prefix + ": " + message, args);
2458      debug.ifPresent(x -> x.add(args.length == 0 ? message : f(message, args)));
2459   }
2460
2461   T runPostCreateHooks(T bean) {
2462      postCreateHooks.forEach(x -> x.accept(bean));
2463      return bean;
2464   }
2465   } // class Builder
2466}