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.marshall;
018
019import static org.apache.juneau.commons.function.Suppliers.*;
020import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
021import static org.apache.juneau.commons.utils.CollectionUtils.*;
022import static org.apache.juneau.commons.utils.Shorts.*;
023import static org.apache.juneau.marshall.ClassMeta.Category.*;
024
025import java.io.*;
026import java.lang.annotation.*;
027import java.lang.reflect.*;
028import java.lang.reflect.Proxy;
029import java.net.*;
030import java.time.*;
031import java.time.temporal.*;
032import java.util.*;
033import java.util.function.*;
034import java.util.stream.*;
035
036import org.apache.juneau.commons.bean.*;
037import org.apache.juneau.commons.collections.*;
038import org.apache.juneau.commons.conversion.*;
039import org.apache.juneau.commons.function.*;
040import org.apache.juneau.commons.http.*;
041import org.apache.juneau.commons.inject.*;
042import org.apache.juneau.commons.reflect.*;
043import org.apache.juneau.commons.utils.*;
044import org.apache.juneau.marshall.json.*;
045import org.apache.juneau.marshall.swap.spi.*;
046
047/**
048 * A wrapper class around the {@link Class} object that provides cached information about that class.
049 *
050 * <p>
051 * Instances of this class can be created through the {@link MarshallingContext#getClassMeta(Class)} method.
052 *
053 * <p>
054 * The {@link MarshallingContext} class will cache and reuse instances of this class except for the following class types:
055 * <ul>
056 *    <li>Arrays
057 *    <li>Maps with non-Object key/values.
058 *    <li>Collections with non-Object key/values.
059 * </ul>
060 *
061 * <p>
062 * This class is tied to the {@link MarshallingContext} class because it's that class that makes the determination of what is
063 * a bean.
064 *
065 *
066 * @param <T> The class type of the wrapped class.
067 */
068@BeanType(properties = "innerClass,elementType,keyType,valueType,notABeanReason,initException,beanMeta")
069@SuppressWarnings({
070   "java:S1200", // Class has 23 dependencies, acceptable for this core reflection metadata class
071   "java:S1452", // Wildcard required - ClassMeta<?>, ObjectSwap<T,?>, etc. for element/component types
072   "java:S3776", // The ClassMeta constructor's type-category chain, findNameProperty()/findParentProperty() annotation scans and getExample() each branch over many type/annotation cases
073   "java:S6539", // Monster Class: ClassMeta is a focused reflection-metadata cache; splitting would increase coupling
074   "unchecked" // Casts to T, ObjectSwap<T,?>, BuilderSwap<T,?> and Collection/Map<Object,Object> in example creation, newInstance and swap discovery are guaranteed by inner()
075})
076public final class ClassMeta<T> extends BeanInfo<T> {
077
078   private static class Categories {
079      int bits;
080
081      /**
082       * Checks whether this instance represents the same set of categories as another.
083       *
084       * @param cat The other instance to compare against.
085       * @return <jk>true</jk> if both instances have identical category bits set.
086       */
087      public boolean same(Categories cat) {
088         return cat.bits == bits;
089      }
090
091      boolean is(Category c) {
092         return (bits & c.mask) != 0;
093      }
094
095      boolean isUnknown() {
096         return bits == 0;
097      }
098
099      Categories set(Category c) {
100         bits |= c.mask;
101         return this;
102      }
103   }
104
105   enum Category {
106      MAP(0),
107      COLLECTION(1),
108      NUMBER(2),
109      DECIMAL(3),
110      DATE(4),
111      ARRAY(5),
112      ENUM(6),
113      CHARSEQ(8),
114      STR(9),
115      URI(10),
116      BEANMAP(11),
117      READER(12),
118      INPUTSTREAM(13),
119      ARGS(14),
120      CALENDAR(15),
121      TEMPORAL(16),
122      LIST(17),
123      SET(18),
124      DELEGATE(19),
125      ITERATOR(21),
126      ITERABLE(22),
127      STREAM(23),
128      DURATION(24),
129      PERIOD(25);
130
131      private final int mask;
132
133      Category(int bitPosition) {
134         this.mask = 1 << bitPosition;
135      }
136   }
137
138   /**
139    * Generated classes shouldn't be cacheable to prevent needlessly filling up the cache.
140    */
141   private static boolean isCacheable(Class<?> c) {
142      var n = cn(c);
143      var x = n.charAt(n.length() - 1);  // All generated classes appear to end with digits.
144      return !(x >= '0' && x <= '9' && (n.indexOf("$$") != -1 || n.startsWith("sun") || n.startsWith("com.sun") || n.indexOf("$Proxy") != -1));
145   }
146
147   private final List<ClassMeta<?>> args;                                     // Arg types if this is an array of args.
148   private final MarshallingContext marshallingContext;                                     // The bean context that created this object.
149   private final Supplier<BuilderSwap<T,?>> builderSwap;                      // The builder swap associated with this bean (if it has one).
150   private final Categories cat;                                              // The class category.
151   private final Cache<Class<?>,ObjectSwap<?,?>> childSwapMap;                // Maps normal subclasses to ObjectSwaps.
152   private final Supplier<List<ObjectSwap<?,?>>> childSwaps;                  // Any ObjectSwaps where the normal type is a subclass of this class.
153   private final Cache<Class<?>,ObjectSwap<?,?>> childUnswapMap;              // Maps swap subclasses to ObjectSwaps.
154   private final Supplier<String> beanDictionaryName;                             // The dictionary name of this class if it has one.
155   private final Supplier<ClassMeta<?>> elementType;                          // If ARRAY or COLLECTION, the element class type.
156   private final NullableSupplier<String> example;                            // Example JSON.
157   private final NullableSupplier<FieldInfo> exampleField;                    // The @Example-annotated field (if it has one).
158   private final NullableSupplier<MethodInfo> exampleMethod;                  // The example() or @Example-annotated method (if it has one).
159   private final NullableSupplier<MethodInfo> fromStringMethod;               // Static fromString(String) or equivalent method
160   private final NullableSupplier<ClassInfoTyped<? extends T>> implClass;     // The implementation class to use if this is an interface.
161   private final Supplier<KeyValueTypes> keyValueTypes;                        // Key and value types for MAP types.
162   private final Supplier<Type[]> parameters;                                  // Type parameters for converter dispatch.
163   private final NullableSupplier<MarshalledFilter> marshalledFilter;
164   private final Supplier<Property<T,Object>> nameProperty;                   // The method to set the name on an object (if it has one).
165   private final NullableSupplier<ConstructorInfo> noArgConstructor;          // The no-arg constructor for this class (if it has one).
166   private final Supplier<Property<T,Object>> parentProperty;                 // The method to set the parent on an object (if it has one).
167   private final Cache<String,Optional<?>> properties;
168   private final NullableSupplier<ConstructorInfo> stringConstructor;         // The X(String) constructor (if it has one).
169   private final Supplier<List<ObjectSwap<T,?>>> swaps;                       // The object POJO swaps associated with this bean (if it has any).
170   private final NullableSupplier<BeanMeta.BeanMetaValue<T>> beanMeta;
171   private final boolean nonStaticMemberClass;                                // Class.isMemberClass() is a native call, so it is evaluated once here.
172
173   private record KeyValueTypes(ClassMeta<?> keyType, ClassMeta<?> valueType) {
174      Optional<ClassMeta<?>> optKeyType() { return o(keyType()); }
175      Optional<ClassMeta<?>> optValueType() { return o(valueType()); }
176   }
177
178   /**
179    * Construct a new {@code ClassMeta} based on the specified {@link Class}.
180    *
181    * @param innerClass The class being wrapped.
182    * @param marshallingContext The bean context that created this object.
183    */
184   ClassMeta(Class<T> innerClass, MarshallingContext marshallingContext) {
185      super(innerClass);
186      this.marshallingContext = marshallingContext;
187      this.nonStaticMemberClass = isMemberClass() && isNotStatic();
188      this.cat = new Categories();
189
190      var ap = marshallingContext.getAnnotationProvider();
191
192      if (isAssignableTo(Delegate.class)) {
193         cat.set(DELEGATE);
194      }
195      if (isEnum()) {
196         cat.set(ENUM);
197      } else if (isAssignableTo(CharSequence.class)) {
198         cat.set(CHARSEQ);
199         if (is(String.class)) {
200            cat.set(STR);
201         }
202      } else if (isAssignableTo(Number.class) || isAny(byte.class, short.class, int.class, long.class, float.class, double.class)) {
203         cat.set(NUMBER);
204         if (isAssignableToAny(Float.class, Double.class) || isAny(float.class, double.class)) {
205            cat.set(DECIMAL);
206         }
207      } else if (isAssignableTo(Collection.class)) {
208         cat.set(COLLECTION);
209         if (isAssignableTo(Set.class)) {
210            cat.set(SET);
211         } else if (isAssignableTo(List.class)) {
212            cat.set(LIST);
213         }
214      } else if (isAssignableTo(Iterable.class)) {
215         cat.set(ITERABLE);
216      } else if (isAssignableTo(Iterator.class) || isAssignableTo(Enumeration.class)) {
217         cat.set(ITERATOR);
218      } else if (isAssignableTo(BaseStream.class)) {
219         cat.set(STREAM);
220      } else if (isAssignableTo(Map.class)) {
221         cat.set(MAP);
222         if (isAssignableTo(BeanMap.class)) {
223            cat.set(BEANMAP);
224         }
225      } else if (isAssignableToAny(Date.class, Calendar.class)) {
226         if (isAssignableTo(Date.class)) {
227            cat.set(DATE);
228         } else if (isAssignableTo(Calendar.class)) {
229            cat.set(CALENDAR);
230         }
231      } else if (is(Duration.class)) {
232         cat.set(DURATION);
233      } else if (is(Period.class)) {
234         cat.set(PERIOD);
235      } else if (isAssignableTo(Temporal.class)) {
236         cat.set(TEMPORAL);
237      } else if (isAssignableTo(javax.xml.datatype.XMLGregorianCalendar.class)) {
238         cat.set(CALENDAR);
239      } else if (inner().isArray()) {
240         cat.set(ARRAY);
241      } else if (isAssignableToAny(URL.class, URI.class) || ap.has(Uri.class, this)) {
242         cat.set(URI);
243      } else if (isAssignableTo(Reader.class)) {
244         cat.set(READER);
245      } else if (isAssignableTo(InputStream.class)) {
246         cat.set(INPUTSTREAM);
247      }
248
249      beanMeta = memoize(this::findBeanMeta);
250      builderSwap = memoize(this::findBuilderSwap);
251      childSwapMap = Cache.<Class<?>,ObjectSwap<?,?>>create().supplier(this::findSwap).build();
252      childSwaps = memoize(this::findChildSwaps);
253      childUnswapMap = Cache.<Class<?>,ObjectSwap<?,?>>create().supplier(this::findUnswap).build();
254      beanDictionaryName = memoize(this::findBeanDictionaryName);
255      elementType = memoize(this::findElementType);
256      example = memoize(this::findExample);
257      exampleField = memoize(this::findExampleField);
258      exampleMethod = memoize(this::findExampleMethod);
259      fromStringMethod = memoize(this::findFromStringMethod);
260      implClass = memoize(this::findImplClass);
261      keyValueTypes = memoize(this::findKeyValueTypes);
262      parameters = memoize(this::findParameters);
263      marshalledFilter = memoize(this::findMarshalledFilter);
264      nameProperty = memoize(this::findNameProperty);
265      noArgConstructor = memoize(this::findNoArgConstructor);
266      parentProperty = memoize(this::findParentProperty);
267      properties = Cache.<String,Optional<?>>create().build();
268      stringConstructor = memoize(this::findStringConstructor);
269      swaps = memoize(this::findSwaps);
270
271      this.args = null;
272
273      // Publish after fields are assigned so concurrent getClassMeta callers never observe
274      // this instance with swaps == null.  Recursive bean-property lookups still resolve:
275      // they run from the lazy suppliers after construction, once this entry is cached.
276      if (nn(marshallingContext) && nn(marshallingContext.getCmCache()) && isCacheable(innerClass))
277         marshallingContext.getCmCache().put(innerClass, this);
278   }
279
280   protected ObjectSwap<?,?> findSwap(Class<?> c) {
281      return childSwaps.get().stream().filter(x -> x.getNormalClass().isAssignableFrom(c)).findFirst().orElse(null);
282   }
283
284   protected ObjectSwap<?,?> findUnswap(Class<?> c) {
285      return childSwaps.get().stream().filter(x -> x.getSwapClass().isAssignableFrom(c)).findFirst().orElse(null);
286   }
287
288
289   /**
290    * Constructor for args-arrays.
291    */
292   ClassMeta(List<ClassMeta<?>> args) {
293      super((Class<T>)Object[].class);
294      this.args = args;
295      this.nonStaticMemberClass = false;
296      this.childSwaps = memoize(this::findChildSwaps);
297      this.childSwapMap = null;
298      this.childUnswapMap = null;
299      this.cat = new Categories().set(ARGS);
300      this.marshallingContext = null;
301      this.elementType = memoize(this::findElementType);
302      this.keyValueTypes = memoize(this::findKeyValueTypes);
303      this.beanMeta = memoize(this::findBeanMeta);
304      this.swaps = memoize(this::findSwaps);
305      this.fromStringMethod = memoize(this::findFromStringMethod);
306      this.exampleMethod = memoize(this::findExampleMethod);
307      this.parentProperty = memoize(this::findParentProperty);
308      this.nameProperty = memoize(this::findNameProperty);
309      this.exampleField = memoize(this::findExampleField);
310      this.noArgConstructor = memoize(this::findNoArgConstructor);
311      this.properties = Cache.<String,Optional<?>>create().build();
312      this.stringConstructor = memoize(this::findStringConstructor);
313      this.marshalledFilter = memoize(this::findMarshalledFilter);
314      this.builderSwap = memoize(this::findBuilderSwap);
315      this.example = memoize(this::findExample);
316      this.implClass = memoize(this::findImplClass);
317      this.beanDictionaryName = memoize(this::findBeanDictionaryName);
318      this.parameters = memoize(this::findParameters);
319   }
320
321   /**
322    * Copy constructor.
323    *
324    * <p>
325    * Used for creating Map and Collection class metas that shouldn't be cached.
326    */
327   ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> valueType, ClassMeta<?> elementType) {
328      super(mainType.inner());
329      this.nonStaticMemberClass = mainType.nonStaticMemberClass;
330      this.childSwaps = mainType.childSwaps;
331      this.childSwapMap = mainType.childSwapMap;
332      this.childUnswapMap = mainType.childUnswapMap;
333      this.cat = mainType.cat;
334      this.fromStringMethod = mainType.fromStringMethod;
335      this.marshallingContext = mainType.marshallingContext;
336      this.elementType = elementType != null ? memoize(()->elementType) : mainType.elementType;
337      this.keyValueTypes = (keyType != null || valueType != null) ? memoize(()->new KeyValueTypes(keyType, valueType)) : mainType.keyValueTypes;
338      this.beanMeta = mainType.beanMeta;
339      this.swaps = mainType.swaps;
340      this.exampleMethod = mainType.exampleMethod;
341      this.args = null;
342      this.parentProperty = mainType.parentProperty;
343      this.nameProperty = mainType.nameProperty;
344      this.exampleField = mainType.exampleField;
345      this.noArgConstructor = mainType.noArgConstructor;
346      this.properties = mainType.properties;
347      this.stringConstructor = mainType.stringConstructor;
348      this.marshalledFilter = mainType.marshalledFilter;
349      this.builderSwap = mainType.builderSwap;
350      this.example = mainType.example;
351      this.implClass = mainType.implClass;
352      this.beanDictionaryName = mainType.beanDictionaryName;
353      this.parameters = memoize(this::findParameters);
354   }
355
356   /**
357    * Returns <jk>true</jk> if this class can be instantiated as a bean.
358    * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match the class type of
359    * the defining class.
360    *
361    * @param outer
362    *    The outer class object for non-static member classes.  Can be <jk>null</jk> for non-member or static classes.
363    * @return
364    *    <jk>true</jk> if a new instance of this bean can be created within the context of the specified outer object.
365    */
366   public boolean canCreateNewBean(Object outer) {
367      var bm = getBeanMeta();
368      if (bm == null || ! bm.hasConstructor())
369         return false;
370      if (nonStaticMemberClass)
371         return nn(outer) && bm.getConstructor().hasParameterTypes(outer.getClass());
372      return true;
373   }
374
375   /**
376    * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler.
377    *
378    * @return <jk>true</jk> if a new instance of this class can be constructed.
379    */
380   @Override
381   public boolean canCreateNewInstance() {
382      if (nonStaticMemberClass)
383         return false;
384      var bm = getBeanMeta();
385      return noArgConstructor.isPresent() || (bm != null && bm.getBeanProxyInvocationHandler() != null) || (isArray() && elementType.get().canCreateNewInstance());
386   }
387
388   /**
389    * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler.
390    * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match the class type of
391    * the defining class.
392    *
393    * @param outer
394    *    The outer class object for non-static member classes.  Can be <jk>null</jk> for non-member or static classes.
395    * @return
396    *    <jk>true</jk> if a new instance of this class can be created within the context of the specified outer object.
397    */
398   @Override
399   public boolean canCreateNewInstance(Object outer) {
400      if (nonStaticMemberClass)
401         return nn(outer) && noArgConstructor.map(x -> x.hasParameterTypes(outer.getClass())).orElse(false);
402      return canCreateNewInstance();
403   }
404
405   /**
406    * Returns <jk>true</jk> if this class can call the {@link #newInstanceFromString(Object, String)} method.
407    *
408    * @param outer
409    *    The outer class object for non-static member classes.
410    *    Can be <jk>null</jk> for non-member or static classes.
411    * @return <jk>true</jk> if this class has a no-arg constructor or invocation handler.
412    */
413   public boolean canCreateNewInstanceFromString(Object outer) {
414      if (fromStringMethod.isPresent())
415         return true;
416      if (stringConstructor.isPresent()) {
417         if (nonStaticMemberClass)
418            return nn(outer) && stringConstructor.map(x -> x.hasParameterTypes(outer.getClass(), String.class)).orElse(false);
419         return true;
420      }
421      return false;
422   }
423
424   @Override /* Overridden from Object */
425   public boolean equals(Object o) {
426      return (o instanceof ClassMeta<?>) && super.equals(o);
427   }
428
429   @Override /* Overridden from Object */
430   public int hashCode() {
431      return super.hashCode();
432   }
433
434   /**
435    * Performs an action on all matching annotations of the specified type defined on this class or parent classes/interfaces in parent-to-child order.
436    *
437    * @param <A> The annotation type to look for.
438    * @param type The annotation to search for.
439    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk> (if null, all entries are accepted).
440    * @param action An action to perform on the entry.
441    * @return This object.
442    */
443   public <A extends Annotation> ClassMeta<T> forEachAnnotation(Class<A> type, Predicate<A> filter, Consumer<A> action) {
444      if (marshallingContext != null) {
445         marshallingContext.getAnnotationProvider().find(type, this).stream().map(AnnotationInfo::inner).filter(x -> filter == null || filter.test(x)).forEach(action::accept);
446      }
447      return this;
448   }
449
450   /**
451    * Returns the argument metadata at the specified index if this is an args metadata object.
452    *
453    * @param index The argument index.
454    * @return The argument metadata.  Never <jk>null</jk>.
455    * @throws BeanRuntimeException If this metadata object is not a list of arguments, or the index is out of range.
456    */
457   public ClassMeta<?> getArg(int index) {
458      if (nn(args) && index >= 0 && index < args.size())
459         return args.get(index);
460      throw brex("Invalid argument index specified:  %s.  Only %s arguments are defined.", index, args == null ? 0 : args.size());
461   }
462
463   /**
464    * Returns the argument types of this meta.
465    *
466    * @return The argument types of this meta, or <jk>null</jk> if this isn't an array of argument types.
467    */
468   public List<ClassMeta<?>> getArgs() { return args; }
469
470   /**
471    * Returns the {@link MarshallingContext} that created this object.
472    *
473    * <p>
474    * Covariantly overrides {@link BeanInfo#getMarshallingContext()} to narrow the return type from
475    * {@link Object} (commons SPI) back to {@link MarshallingContext} (marshalling-side concrete type).
476    *
477    * @return The bean context, or <jk>null</jk> if this is an arguments-array metadata object (see {@link #isArgs()}).
478    */
479   @Override
480   public MarshallingContext getMarshallingContext() { return marshallingContext; }
481
482   /**
483    * Returns the {@link BeanConfigContext} snapshot carried by the {@link MarshallingContext} that built this
484    * {@link ClassMeta}.
485    *
486    * <p>
487    * Satisfies the {@link BeanInfo#getBeanConfigContext()} commons SPI contract.
488    *
489    * @return The bean-modeling configuration of the marshalling context.  Never <jk>null</jk>.
490    */
491   @Override
492   public BeanConfigContext getBeanConfigContext() { return marshallingContext.getBeanConfigContext(); }
493
494   /**
495    * Returns the {@link BeanMeta} associated with this class.
496    *
497    * @return
498    *    The {@link BeanMeta} associated with this class, or <jk>null</jk> if there is no bean meta associated with
499    *    this class.
500    */
501   public BeanMeta<T> getBeanMeta() {
502      return beanMeta.get().beanMeta();
503   }
504
505   /**
506    * Same as {@link #getBeanMeta()} but derives property names using the supplied {@link PropertyNamer} instead of
507    * the one configured on the owning context.
508    *
509    * <p>
510    * Unlike {@link #getBeanMeta()}, the returned {@link BeanMeta} is built fresh on each call and is <b>not</b>
511    * cached — the shared, {@code Class}-keyed default {@link BeanMeta} (which uses the context's default namer) is
512    * left untouched.  This keeps the cache safe when callers request an alternate property-naming strategy (e.g.
513    * {@code MarshallingSession#toBeanMap(Object, PropertyNamer)}).
514    *
515    * @param propertyNamer The property namer to use for deriving property names.  Must not be <jk>null</jk>.
516    * @return
517    *    A newly-built {@link BeanMeta} whose property names reflect the supplied namer, or <jk>null</jk> if there is
518    *    no bean meta associated with this class.
519    */
520   public BeanMeta<T> getBeanMeta(PropertyNamer propertyNamer) {
521      if (! cat.isUnknown())
522         return null;
523      return BeanMeta.create(this, implClass.get(), propertyNamer).beanMeta();
524   }
525
526   /**
527    * Returns the bean registry for this class.
528    *
529    * <p>
530    * This bean registry contains names specified in the {@link Marshalled#dictionary() @Marshalled(dictionary)} annotation
531    * defined on the class, regardless of whether the class is an actual bean.
532    * This allows interfaces to define subclasses with type names.
533    *
534    * <p>
535    * This is a shortcut for calling getBeanMeta().getBeanRegistry().
536    *
537    * @return The bean registry for this class, or <jk>null</jk> if no bean registry is associated with it.
538    */
539   public BeanRegistry getBeanRegistry() {
540      return beanMeta.get().optBeanMeta().map(x -> (BeanRegistry) x.getBeanRegistry()).orElse(null);
541   }
542
543   /**
544    * Returns the builder swap associated with this class.
545    *
546    * @param session The current bean session (retained for API compatibility).
547    * @return The builder swap associated with this class, or <jk>null</jk> if it doesn't exist.
548    */
549   @SuppressWarnings({
550      "java:S1172" // 'session' retained for public-API/binary compatibility; the builder swap is session-independent.
551   })
552   public BuilderSwap<T,?> getBuilderSwap(MarshallingSession session) {
553      return builderSwap.get();
554   }
555
556   /**
557    * Returns the bean dictionary name associated with this class.
558    *
559    * <p>
560    * The lexical name is defined by {@link Marshalled#typeName() @Marshalled(typeName)}.
561    *
562    * @return
563    *    The type name associated with this bean class, or <jk>null</jk> if there is no type name defined or this
564    *    isn't a bean.
565    */
566   public String getBeanDictionaryName() {
567      return beanDictionaryName.get();
568   }
569
570   /**
571    * For array and {@code Collection} types, returns the class type of the components of the array or
572    * {@code Collection}.
573    *
574    * @return The element class type, or <jk>null</jk> if this class is not an array or Collection.
575    */
576   @Override
577   public ClassMeta<?> getElementType() { return elementType.get(); }
578
579   /**
580    * Returns the example of this class.
581    *
582    * @param session
583    *    The bean session.
584    *    <br>Required because the example method may take it in as a parameter.
585    * @param jpSession The JSON parser for parsing examples into POJOs.
586    * @return An example instance of this class, or <jk>null</jk> if no example is defined or one could not be created.
587    */
588   public T getExample(MarshallingSession session, JsonParserSession jpSession) {
589      try {
590         if (example.isPresent())
591            return jpSession.read(example.get(), this);
592         if (exampleMethod.isPresent())
593            return (T)exampleMethod.get().invokeLenient(null, session);
594         if (exampleField.isPresent())
595            return (T)exampleField.get().get(null);
596
597         if (isCollection()) {
598            var etExample = getElementType().getExample(session, jpSession);
599            if (nn(etExample)) {
600               if (canCreateNewInstance()) {
601                  var c = (Collection<Object>)newInstance();
602                  c.add(etExample);
603                  return (T)c;
604               }
605               return (T)Collections.singleton(etExample);
606            }
607         } else if (super.isArray()) {
608            var etExample = getElementType().getExample(session, jpSession);
609            if (nn(etExample)) {
610               var o = Array.newInstance(getElementType().inner(), 1);
611               Array.set(o, 0, etExample);
612               return (T)o;
613            }
614         } else if (isMap()) {
615            var vtExample = getValueType().getExample(session, jpSession);
616            var ktExample = getKeyType().getExample(session, jpSession);
617            if (nn(ktExample) && nn(vtExample)) {
618               if (canCreateNewInstance()) {
619                  var m = (Map<Object,Object>)newInstance();
620                  m.put(ktExample, vtExample);
621                  return (T)m;
622               }
623               return (T)Collections.singletonMap(ktExample, vtExample);
624            }
625         }
626
627         return null;
628      } catch (Exception e) {
629         throw new ClassMetaRuntimeException(e);
630      }
631   }
632
633   /**
634    * Returns <jk>true</jk> if this class can be instantiated from the specified input type.
635    *
636    * @param c The input class type.
637    * @return <jk>true</jk> if a conversion exists.
638    */
639   public boolean canConvertFrom(Class<?> c) {
640      return BasicConverter.INSTANCE.canConvert(c, inner());
641   }
642
643   /**
644    * Returns the no-arg constructor for this class based on the {@link Marshalled#implClass()} value.
645    *
646    * @param conVis The constructor visibility.
647    * @return The no-arg constructor for this class, or <jk>null</jk> if it does not exist.
648    */
649   public ConstructorInfo getImplClassConstructor(Visibility conVis) {
650      return implClass.map(x -> x.getNoArgConstructor(conVis).orElse(null)).orElse(null);
651   }
652
653
654   /**
655    * For {@code Map} types, returns the class type of the keys of the {@code Map}.
656    *
657    * @return The key class type, or <jk>null</jk> if this class is not a Map.
658    */
659   @Override
660   public ClassMeta<?> getKeyType() {
661      return keyValueTypes.get().keyType();
662   }
663
664   /**
665    * Returns the method or field annotated with {@link NameProperty @NameProperty}.
666    *
667    * @return
668    *    The method or field  annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not
669    *    exist.
670    */
671   public Property<T,Object> getNameProperty() { return nameProperty.get(); }
672
673   /**
674    * Returns the reason why this class is not a bean, or <jk>null</jk> if it is a bean.
675    *
676    * @return The reason why this class is not a bean, or <jk>null</jk> if it is a bean.
677    */
678   public synchronized String getNotABeanReason() {
679      return beanMeta.get().notABeanReason();
680   }
681
682   /**
683    * If this is an {@link Optional}, returns an empty optional.
684    *
685    * <p>
686    * Note that if this is a nested optional, will recursively create empty optionals.
687    *
688    * @return An empty optional, or <jk>null</jk> if this isn't an optional.
689    */
690   @SuppressWarnings({
691      "java:S2789" // null intentionally signals "not an Optional type"; callers check for null
692   })
693   @Override
694   public Optional<?> getOptionalDefault() {
695      if (isPrimitiveOptional())
696         return oe();
697      if (isOptional())
698         return o(getElementType().getOptionalDefault());
699      return null;
700   }
701
702   /**
703    * Returns <jk>true</jk> if this is one of the primitive {@link Optional} cousins
704    * ({@link OptionalInt}, {@link OptionalLong}, or {@link OptionalDouble}).
705    *
706    * <p>
707    * These types carry no generic element type but are treated as first-class optional types by the
708    * marshallers (mirroring the {@link Optional} contract — empty ≡ null/absent, present unwraps to the
709    * primitive number).  See {@link #getElementType()} which synthesizes the matching boxed element type.
710    *
711    * @return <jk>true</jk> if this is {@link OptionalInt}/{@link OptionalLong}/{@link OptionalDouble}.
712    */
713   public boolean isPrimitiveOptional() {
714      return is(OptionalInt.class) || is(OptionalLong.class) || is(OptionalDouble.class);
715   }
716
717   /**
718    * Returns <jk>true</jk> if this class is {@link Optional} or one of its primitive cousins
719    * ({@link OptionalInt}/{@link OptionalLong}/{@link OptionalDouble}).
720    *
721    * <p>
722    * Overrides {@link ClassInfo#isOptional()} so the marshalling layer
723    * treats the three primitive optional types as first-class optionals (synthetic boxed element type,
724    * empty-as-null contract) without registering swaps for them.
725    *
726    * @return <jk>true</jk> if this is an optional-like type.
727    */
728   @Override
729   public boolean isOptional() {
730      return super.isOptional() || isPrimitiveOptional();
731   }
732
733   /**
734    * Returns the method or field annotated with {@link ParentProperty @ParentProperty}.
735    *
736    * @return
737    *    The method or field annotated with {@link ParentProperty @ParentProperty} or <jk>null</jk> if method does not
738    *    exist.
739    */
740   public Property<T,Object> getParentProperty() { return parentProperty.get(); }
741
742   /**
743    * Returns a lazily-computed, cached property value for this {@link ClassMeta} instance.
744    *
745    * <p>
746    * This method provides a memoization mechanism for expensive computations. The property value is computed
747    * on the first call using the provided function, then cached for subsequent calls with the same property name.
748    *
749    * <p>
750    * The function is only invoked once per property name per {@link ClassMeta} instance. Subsequent calls
751    * with the same name will return the cached value without re-invoking the function.
752    *
753    * <h5 class='section'>Thread Safety:</h5>
754    * <p>
755    * This method is thread-safe. If multiple threads call this method simultaneously with the same property name,
756    * the function may be invoked multiple times, but only one result will be cached and returned.
757    *
758    * <h5 class='section'>Usage:</h5>
759    * <p class='bjava'>
760    *    <jc>// Compute and cache an expensive property</jc>
761    *    Optional&lt;String&gt; <jv>computedValue</jv> = classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -&gt; {
762    *       <jc>// Expensive computation that only runs once</jc>
763    *       <jk>return</jk> performExpensiveComputation(cm);
764    *    });
765    *
766    *    <jc>// Subsequent calls return cached value</jc>
767    *    Optional&lt;String&gt; <jv>cached</jv> = classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -&gt; {
768    *       <jc>// This function is NOT called again</jc>
769    *       <jk>return</jk> performExpensiveComputation(cm);
770    *    });
771    * </p>
772    *
773    * @param <T2> The type of the property value.
774    * @param name The unique name identifying this property. Used as the cache key.
775    * @param function The function that computes the property value. Receives this {@link ClassMeta} instance as input.
776    *    Only invoked if the property hasn't been computed yet. Can return <jk>null</jk>.
777    * @return An {@link Optional} containing the property value if the function returned a non-null value,
778    *    otherwise an empty {@link Optional}. Never <jk>null</jk>.
779    */
780   public <T2> Optional<T2> getProperty(String name, Function<ClassMeta<?>,T2> function) {
781      return (Optional<T2>)properties.get(name, () -> o(function.apply(this)));
782   }
783
784   /**
785    * Returns the interface proxy invocation handler for this class.
786    *
787    * @return The interface proxy invocation handler, or <jk>null</jk> if it does not exist.
788    */
789   public InvocationHandler getProxyInvocationHandler() {
790      return beanMeta.get().optBeanMeta().map(x -> x.getBeanProxyInvocationHandler()).orElse(null);
791   }
792
793
794   /**
795    * Returns the serialized (swapped) form of this class if there is an {@link ObjectSwap} associated with it.
796    *
797    * @param session
798    *    The bean session.
799    *    <br>Required because the swap used may depend on the media type being serialized or parsed.
800    * @return The serialized class type, or this object if no swap is associated with the class.
801    */
802   public ClassMeta<?> getSerializedClassMeta(MarshallingSession session) {
803      var ps = getSwap(session);
804      return (ps == null ? this : ps.getSwapClassMeta(session));
805   }
806
807
808   /**
809    * Returns the {@link ObjectSwap} associated with this class that's the best match for the specified session.
810    *
811    * @param session
812    *    The current bean session.
813    *    <br>If multiple swaps are associated with a class, only the first one with a matching media type will
814    *    be returned.
815    * @return
816    *    The {@link ObjectSwap} associated with this class, or <jk>null</jk> if there are no POJO swaps associated with
817    *    this class.
818    */
819   public ObjectSwap<T,?> getSwap(MarshallingSession session) {
820      var swapsList = swaps.get();
821      if (! swapsList.isEmpty()) {
822         var matchQuant = 0;
823         ObjectSwap<T,?> matchSwap = null;
824
825         for (var swap : swapsList) {
826            var q = swap.match(session);
827            if (q > matchQuant) {
828               matchQuant = q;
829               matchSwap = swap;
830            }
831         }
832
833         if (matchSwap != null)
834            return matchSwap;
835      }
836      return null;
837   }
838
839   /**
840    * Returns <jk>true</jk> if this class can be converted to the specified output type.
841    *
842    * @param c The output class type.
843    * @return <jk>true</jk> if a conversion exists.
844    */
845   public boolean canConvertTo(Class<?> c) {
846      return BasicConverter.INSTANCE.canConvert(inner(), c);
847   }
848
849   /**
850    * For {@code Map} types, returns the class type of the values of the {@code Map}.
851    *
852    * @return The value class type, or <jk>null</jk> if this class is not a Map.
853    */
854   @Override
855   public ClassMeta<?> getValueType() {
856      return keyValueTypes.get().valueType();
857   }
858
859   /**
860    * Returns the type parameters for this class as a {@link Type} array suitable for passing to
861    * {@link Converter#to(Object, Type, Type...)}.
862    *
863    * <p>
864    * The returned array depends on the kind of type:
865    * <ul>
866    *    <li>{@link Map} types: {@code [keyType, valueType]}
867    *    <li>{@link Collection} and {@link Optional} types: {@code [elementType, elementType.getParameters()...]}
868    *       — the element raw class followed by the element's own parameters, recursively flattened by one level.
869    *    <li>All other types: empty array.
870    * </ul>
871    *
872    * <h5 class='section'>Examples:</h5>
873    * <ul>
874    *    <li><c>ClassMeta&lt;Optional&lt;Integer&gt;&gt;</c> → <c>[Integer]</c>
875    *    <li><c>ClassMeta&lt;List&lt;Integer&gt;&gt;</c> → <c>[Integer]</c>
876    *    <li><c>ClassMeta&lt;Map&lt;String,Integer&gt;&gt;</c> → <c>[String, Integer]</c>
877    *    <li><c>ClassMeta&lt;List&lt;Map&lt;String,Integer&gt;&gt;&gt;</c> → <c>[Map, String, Integer]</c>
878    * </ul>
879    *
880    * @return The type parameters as a {@link Type} array, never <jk>null</jk>.
881    */
882   public Type[] getParameters() { return parameters.get(); }
883
884   private Type[] findParameters() {
885      if (isMap()) {
886         var kt = getKeyType();
887         var vt = getValueType();
888         if (kt == null || vt == null)
889            return new Type[0];
890         return new Type[]{kt.inner(), vt.inner()};
891      }
892      if (isCollection() || isOptional()) {
893         var et = getElementType();
894         var etParams = et.getParameters();
895         var result = new Type[1 + etParams.length];
896         result[0] = et.inner();
897         System.arraycopy(etParams, 0, result, 1, etParams.length);
898         return result;
899      }
900      return new Type[0];
901   }
902
903   /**
904    * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream.
905    *
906    * <p>
907    * Only returns <jk>true</jk> for types that can be explicitly constructed from an {@link InputStream} (e.g. via a
908    * public constructor or static factory). Excludes assignable supertypes and the generic {@code toString()} fallback.
909    *
910    * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream.
911    */
912   public boolean hasInputStreamMutater() {
913      // Exclude String: BasicConverter maps InputStream→String via stream.toString() which doesn't read content.
914      // Exclude supertypes of InputStream (e.g. Object, Closeable): assignability, not an explicit transform.
915      if (inner() == String.class || inner().isAssignableFrom(InputStream.class))
916         return false;
917      return BasicConverter.INSTANCE.canConvert(InputStream.class, inner());
918   }
919
920   /**
921    * Returns <jk>true</jk> if this class can be instantiated from the specified type via an explicit conversion.
922    *
923    * <p>
924    * Same-type conversions (identity) always return <jk>true</jk>. Strict supertype relationships (where this
925    * class is already a supertype of the source) return <jk>false</jk> — those are assignability, not explicit
926    * transforms.
927    *
928    * @param c The class type to convert from.  Must not be <jk>null</jk>.
929    * @return <jk>true</jk> if an explicit conversion from {@code c} to this type exists.
930    */
931   public boolean hasMutaterFrom(Class<?> c) {
932      // Same type is always "convertible" (identity).
933      if (inner() == c)
934         return true;
935      // Exclude strict supertype: if this type is already a strict supertype of c, it's assignability only.
936      if (inner().isAssignableFrom(c))
937         return false;
938      // Exclude array→array: MarshallingSession handles those directly; BasicConverter infers
939      // array conversions from element types, which can produce false positives here.
940      if (inner().isArray() && c.isArray())
941         return false;
942      // Exclude Collections and Maps — MarshallingSession handles collection/map conversion directly.
943      // BasicConverter's generic conversions produce false positives that Mutaters never did.
944      if (Collection.class.isAssignableFrom(inner()) || Map.class.isAssignableFrom(inner()))
945         return false;
946      return BasicConverter.INSTANCE.canConvert(c, inner());
947   }
948
949   /**
950    * Returns <jk>true</jk> if this class can be instantiated from the specified type via an explicit conversion.
951    *
952    * @param c The class type to convert from.  Must not be <jk>null</jk>.
953    * @return <jk>true</jk> if an explicit conversion from {@code c} to this type exists.
954    */
955   public boolean hasMutaterFrom(ClassMeta<?> c) {
956      return hasMutaterFrom(c.inner());
957   }
958
959   /**
960    * Returns <jk>true</jk> if this class can be transformed to the specified type via an explicit conversion.
961    *
962    * <p>
963    * Same-type conversions (identity) always return <jk>true</jk>. Cases where the target is already a strict
964    * supertype of this class return <jk>false</jk> — those are assignability, not explicit transforms.
965    *
966    * @param c The class type to convert to.  Must not be <jk>null</jk>.
967    * @return <jk>true</jk> if an explicit conversion from this type to {@code c} exists.
968    */
969   public boolean hasMutaterTo(Class<?> c) {
970      // Same type is always "convertible" (identity).
971      if (inner() == c)
972         return true;
973      // Exclude strict supertype: if the target is already a strict supertype of this type, it's assignability only.
974      if (c.isAssignableFrom(inner()))
975         return false;
976      // Exclude array→array: MarshallingSession handles those directly; BasicConverter infers
977      // array conversions from element types, which can produce false positives here.
978      if (inner().isArray() && c.isArray())
979         return false;
980      // Exclude Collection/Map targets — MarshallingSession handles collection/map conversion directly.
981      // BasicConverter's generic conversions produce false positives that Mutaters never did.
982      if (Collection.class.isAssignableFrom(c) || Map.class.isAssignableFrom(c))
983         return false;
984      return BasicConverter.INSTANCE.canConvert(inner(), c);
985   }
986
987   /**
988    * Returns <jk>true</jk> if this class can be transformed to the specified type via an explicit conversion.
989    *
990    * @param c The class type to convert to.  Must not be <jk>null</jk>.
991    * @return <jk>true</jk> if an explicit conversion from this type to {@code c} exists.
992    */
993   public boolean hasMutaterTo(ClassMeta<?> c) {
994      return hasMutaterTo(c.inner());
995   }
996
997   /**
998    * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader.
999    *
1000    * <p>
1001    * Only returns <jk>true</jk> for types that can be explicitly constructed from a {@link Reader} (e.g. via a
1002    * public constructor or static factory). Excludes assignable supertypes and the generic {@code toString()} fallback.
1003    *
1004    * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader.
1005    */
1006   public boolean hasReaderMutater() {
1007      // Exclude String: BasicConverter maps Reader→String via reader.toString() which doesn't read content.
1008      // Exclude supertypes of Reader (e.g. Object, Closeable): assignability, not an explicit transform.
1009      if (inner() == String.class || inner().isAssignableFrom(Reader.class))
1010         return false;
1011      return BasicConverter.INSTANCE.canConvert(Reader.class, inner());
1012   }
1013
1014   /**
1015    * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a String.
1016    *
1017    * <p>
1018    * Delegates to {@link #hasMutaterFrom(Class)} with {@link String} as the source type, which applies the
1019    * same exclusions: supertypes of {@link String} (e.g. {@link Object}), arrays, and {@link Collection}/{@link Map}
1020    * targets are excluded because those are handled by parsers or {@code MarshallingSession}, not explicit string transforms.
1021    *
1022    * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a String.
1023    */
1024   public boolean hasStringMutater() {
1025      return hasMutaterFrom(String.class);
1026   }
1027
1028   /**
1029    * Returns <jk>true</jk> if this metadata represents an array of argument types.
1030    *
1031    * @return <jk>true</jk> if this metadata represents an array of argument types.
1032    */
1033   public boolean isArgs() { return cat.is(ARGS); }
1034
1035   /**
1036    * Returns <jk>true</jk> if this class is a bean.
1037    *
1038    * @return <jk>true</jk> if this class is a bean.
1039    */
1040   @Override
1041   public boolean isBean() { return nn(getBeanMeta()); }
1042
1043   /**
1044    * Returns <jk>true</jk> if this class is a subclass of {@link BeanMap}.
1045    *
1046    * @return <jk>true</jk> if this class is a subclass of {@link BeanMap}.
1047    */
1048   public boolean isBeanMap() { return cat.is(BEANMAP); }
1049
1050   /**
1051    * Returns <jk>true</jk> if this class is a {@link Boolean}.
1052    *
1053    * @return <jk>true</jk> if this class is a {@link Boolean}.
1054    */
1055   @Override
1056   public boolean isBoolean() { return isAny(boolean.class, Boolean.class); }
1057
1058   /**
1059    * Returns <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>.
1060    *
1061    * @return <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>.
1062    */
1063   public boolean isByteArray() { return is(byte[].class); }
1064
1065   /**
1066    * Returns <jk>true</jk> if this class is a {@link Calendar}.
1067    *
1068    * @return <jk>true</jk> if this class is a {@link Calendar}.
1069    */
1070   public boolean isCalendar() { return cat.is(CALENDAR); }
1071
1072   /**
1073    * Returns <jk>true</jk> if this class is a {@link Character}.
1074    *
1075    * @return <jk>true</jk> if this class is a {@link Character}.
1076    */
1077   public boolean isChar() { return isAny(char.class, Character.class); }
1078
1079   /**
1080    * Returns <jk>true</jk> if this class is a subclass of {@link CharSequence}.
1081    *
1082    * @return <jk>true</jk> if this class is a subclass of {@link CharSequence}.
1083    */
1084   @Override
1085   public boolean isCharSequence() { return cat.is(CHARSEQ); }
1086
1087   /**
1088    * Returns <jk>true</jk> if this class is a subclass of {@link Collection}.
1089    *
1090    * @return <jk>true</jk> if this class is a subclass of {@link Collection}.
1091    */
1092   @Override
1093   public boolean isCollection() { return cat.is(COLLECTION); }
1094
1095   /**
1096    * Returns <jk>true</jk> if this class is a subclass of {@link Collection} or is an array or {@link Optional}.
1097    *
1098    * @return <jk>true</jk> if this class is a subclass of {@link Collection} or is an array or {@link Optional}.
1099    */
1100   public boolean isCollectionOrArrayOrOptional() { return cat.is(ARRAY) || is(Optional.class) || cat.is(COLLECTION); }
1101
1102   /**
1103    * Returns <jk>true</jk> if this class is a {@link Date}.
1104    *
1105    * @return <jk>true</jk> if this class is a {@link Date}.
1106    */
1107   public boolean isDate() { return cat.is(DATE); }
1108
1109   /**
1110    * Returns <jk>true</jk> if this class is a {@link Date} or {@link Calendar}.
1111    *
1112    * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar}.
1113    */
1114   @Override
1115   public boolean isDateOrCalendar() { return cat.is(DATE) || cat.is(CALENDAR); }
1116
1117   /**
1118    * Returns <jk>true</jk> if this class is a {@link Date} or {@link Calendar} or {@link Temporal}.
1119    *
1120    * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar} or {@link Temporal}.
1121    */
1122   public boolean isDateOrCalendarOrTemporal() { return cat.is(DATE) || cat.is(CALENDAR) || cat.is(TEMPORAL) || cat.is(PERIOD); }
1123
1124   /**
1125    * Returns <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}.
1126    *
1127    * @return <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}.
1128    */
1129   @Override
1130   public boolean isDecimal() { return cat.is(DECIMAL); }
1131
1132   /**
1133    * Returns <jk>true</jk> if this class implements {@link Delegate}, meaning it's a representation of some other
1134    * object.
1135    *
1136    * @return <jk>true</jk> if this class implements {@link Delegate}.
1137    */
1138   public boolean isDelegate() { return cat.is(DELEGATE); }
1139
1140   /**
1141    * Returns <jk>true</jk> if this class is either {@link Double} or <jk>double</jk>.
1142    *
1143    * @return <jk>true</jk> if this class is either {@link Double} or <jk>double</jk>.
1144    */
1145   @Override
1146   public boolean isDouble() { return isAny(Double.class, double.class); }
1147
1148   /**
1149    * Returns <jk>true</jk> if this class is either {@link Float} or <jk>float</jk>.
1150    *
1151    * @return <jk>true</jk> if this class is either {@link Float} or <jk>float</jk>.
1152    */
1153   public boolean isFloat() { return isAny(Float.class, float.class); }
1154
1155   /**
1156    * Returns <jk>true</jk> if this class is an {@link InputStream}.
1157    *
1158    * @return <jk>true</jk> if this class is an {@link InputStream}.
1159    */
1160   public boolean isInputStream() { return cat.is(INPUTSTREAM); }
1161
1162   /**
1163    * Returns <jk>true</jk> if this class is either {@link Integer} or <jk>int</jk>.
1164    *
1165    * @return <jk>true</jk> if this class is either {@link Integer} or <jk>int</jk>.
1166    */
1167   public boolean isInteger() { return isAny(Integer.class, int.class); }
1168
1169   /**
1170    * Returns <jk>true</jk> if this class implements {@link Iterable} but is not a {@link Collection}.
1171    *
1172    * @return <jk>true</jk> if this class implements {@link Iterable} but is not a {@link Collection}.
1173    * @since 9.2.1
1174    */
1175   public boolean isIterable() { return cat.is(ITERABLE); }
1176
1177   /**
1178    * Returns <jk>true</jk> if this class implements {@link Iterator} or {@link Enumeration}.
1179    *
1180    * @return <jk>true</jk> if this class implements {@link Iterator} or {@link Enumeration}.
1181    * @since 9.2.1
1182    */
1183   public boolean isIterator() { return cat.is(ITERATOR); }
1184
1185   /**
1186    * Returns <jk>true</jk> if this class is an {@link Iterator}, {@link Iterable} (non-Collection), or {@link BaseStream}.
1187    *
1188    * <p>
1189    * These types represent lazily-evaluated sequences that can be serialized as arrays.
1190    *
1191    * @return <jk>true</jk> if this class is a streamable type.
1192    * @since 9.2.1
1193    */
1194   public boolean isStreamable() { return cat.is(ITERATOR) || cat.is(ITERABLE) || cat.is(STREAM); }
1195
1196   /**
1197    * Returns <jk>true</jk> if this class extends from {@link List}.
1198    *
1199    * @return <jk>true</jk> if this class extends from {@link List}.
1200    */
1201   public boolean isList() { return cat.is(LIST); }
1202
1203   /**
1204    * Returns <jk>true</jk> if this class is either {@link Long} or <jk>long</jk>.
1205    *
1206    * @return <jk>true</jk> if this class is either {@link Long} or <jk>long</jk>.
1207    */
1208   @Override
1209   public boolean isLong() { return isAny(Long.class, long.class); }
1210
1211   /**
1212    * Returns <jk>true</jk> if this class is a subclass of {@link Map}.
1213    *
1214    * @return <jk>true</jk> if this class is a subclass of {@link Map}.
1215    */
1216   @Override
1217   public boolean isMap() { return cat.is(MAP); }
1218
1219   /**
1220    * Returns <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean.
1221    *
1222    * @return <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean.
1223    */
1224   public boolean isMapOrBean() { return cat.is(MAP) || nn(getBeanMeta()); }
1225
1226   /**
1227    * Returns <jk>true</jk> if this class is {@link Method}.
1228    *
1229    * @return <jk>true</jk> if this class is {@link Method}.
1230    */
1231   public boolean isMethod() { return is(Method.class); }
1232
1233   /**
1234    * Returns <jk>true</jk> if instance of this object can be <jk>null</jk>.
1235    *
1236    * <p>
1237    * Objects can be <jk>null</jk>, but primitives cannot, except for chars which can be represented by
1238    * <code>(<jk>char</jk>)0</code>.
1239    *
1240    * @return <jk>true</jk> if instance of this class can be null.
1241    */
1242   public boolean isNullable() {
1243      if (isPrimitive())
1244         return is(char.class);
1245      return true;
1246   }
1247
1248   /**
1249    * Returns <jk>true</jk> if this class is a subclass of {@link Number}.
1250    *
1251    * @return <jk>true</jk> if this class is a subclass of {@link Number}.
1252    */
1253   @Override
1254   public boolean isNumber() { return cat.is(NUMBER); }
1255
1256   /**
1257    * Returns <jk>true</jk> if this class is a {@link Reader}.
1258    *
1259    * @return <jk>true</jk> if this class is a {@link Reader}.
1260    */
1261   public boolean isReader() { return cat.is(READER); }
1262
1263   /**
1264    * Returns <jk>true</jk> if this class extends from {@link Set}.
1265    *
1266    * @return <jk>true</jk> if this class extends from {@link Set}.
1267    */
1268   public boolean isSet() { return cat.is(SET); }
1269
1270   /**
1271    * Returns <jk>true</jk> if this class is either {@link Short} or <jk>short</jk>.
1272    *
1273    * @return <jk>true</jk> if this class is either {@link Short} or <jk>short</jk>.
1274    */
1275   public boolean isShort() { return isAny(Short.class, short.class); }
1276
1277   /**
1278    * Returns <jk>true</jk> if this class is a subclass of {@link BaseStream} (includes {@link Stream}).
1279    *
1280    * @return <jk>true</jk> if this class is a subclass of {@link BaseStream}.
1281    * @since 9.2.1
1282    */
1283   public boolean isStream() { return cat.is(STREAM); }
1284
1285   /**
1286    * Returns <jk>true</jk> if this class is a {@link String}.
1287    *
1288    * @return <jk>true</jk> if this class is a {@link String}.
1289    */
1290   public boolean isString() { return is(String.class); }
1291
1292   /**
1293    * Returns <jk>true</jk> if this class is a {@link Temporal}.
1294    *
1295    * @return <jk>true</jk> if this class is a {@link Temporal}.
1296    */
1297   @Override
1298   public boolean isTemporal() { return cat.is(TEMPORAL); }
1299
1300   /**
1301    * Returns <jk>true</jk> if this class is a {@link Duration}.
1302    *
1303    * @return <jk>true</jk> if this class is a {@link Duration}.
1304    */
1305   public boolean isDuration() { return cat.is(DURATION); }
1306
1307   /**
1308    * Returns <jk>true</jk> if this class is a {@link Period}.
1309    *
1310    * @return <jk>true</jk> if this class is a {@link Period}.
1311    */
1312   public boolean isPeriod() { return cat.is(PERIOD); }
1313
1314   /**
1315    * Returns <jk>true</jk> if this class is a {@link URI} or {@link URL}.
1316    *
1317    * @return <jk>true</jk> if this class is a {@link URI} or {@link URL}.
1318    */
1319   @Override
1320   public boolean isUri() { return cat.is(URI); }
1321
1322   /**
1323    * Converts the specified object into an instance of this class.
1324    *
1325    * @param o The object to convert.
1326    * @return The converted object, or <jk>null</jk> if no conversion is available.
1327    */
1328   public T mutateFrom(Object o) {
1329      return BasicConverter.INSTANCE.to(o, inner());
1330   }
1331
1332   /**
1333    * Converts the specified object to the specified output type.
1334    *
1335    * @param <O> The output class.
1336    * @param o The object to convert.
1337    * @param c The target class.
1338    * @return The converted object, or <jk>null</jk> if no conversion is available.
1339    */
1340   public static <O> O mutateTo(Object o, Class<O> c) {
1341      return BasicConverter.INSTANCE.to(o, c);
1342   }
1343
1344   /**
1345    * Converts the specified object to the type represented by this class meta.
1346    *
1347    * @param <O> The output class.
1348    * @param o The object to convert.
1349    * @param c The target class meta.  Must not be <jk>null</jk>.
1350    * @return The converted object, or <jk>null</jk> if no conversion is available.
1351    */
1352   public static <O> O mutateTo(Object o, ClassMeta<O> c) {
1353      return mutateTo(o, c.inner());
1354   }
1355
1356   /**
1357    * Create a new instance of the main class of this declared type.
1358    *
1359    * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
1360    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
1361    */
1362   @Override
1363   public T newInstance() throws ExecutableException {
1364      if (super.isArray())
1365         return (T)Array.newInstance(inner().getComponentType(), 0);
1366      if (noArgConstructor.isPresent())
1367         return noArgConstructor.get().newInstance();
1368      var h = getProxyInvocationHandler();
1369      if (nn(h))
1370         return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), a(inner(), Serializable.class), h);
1371      return null;
1372   }
1373
1374   /**
1375    * Same as {@link #newInstance()} except for instantiating non-static member classes.
1376    *
1377    * @param outer
1378    *    The instance of the owning object of the member class instance.
1379    *    Can be <jk>null</jk> if instantiating a non-member or static class.
1380    * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
1381    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
1382    */
1383   public T newInstance(Object outer) throws ExecutableException {
1384      if (nonStaticMemberClass && noArgConstructor.isPresent())
1385         return noArgConstructor.get().<T>newInstance(outer);
1386      return newInstance();
1387   }
1388
1389   /**
1390    * Create a new instance of the main class of this declared type from a <c>String</c> input.
1391    *
1392    * <p>
1393    * In order to use this method, the class must have one of the following methods:
1394    * <ul>
1395    *    <li><code><jk>public static</jk> T valueOf(String in);</code>
1396    *    <li><code><jk>public static</jk> T fromString(String in);</code>
1397    *    <li><code><jk>public</jk> T(String in);</code>
1398    * </ul>
1399    *
1400    * @param outer
1401    *    The outer class object for non-static member classes.  Can be <jk>null</jk> for non-member or static classes.
1402    * @param arg The input argument value.
1403    * @return A new instance of the object, or <jk>null</jk> if there is no string constructor on the object.
1404    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
1405    */
1406   public T newInstanceFromString(Object outer, String arg) throws ExecutableException {
1407
1408      if (isEnum()) {
1409         try {
1410            return (T) EnumFormat.parse(arg, asEnumClass(inner()));
1411         } catch (@SuppressWarnings("unused") IllegalArgumentException e) {
1412            if (marshallingContext.isIgnoreUnknownEnumValues())
1413               return null;
1414            throw new ExecutableException("Could not resolve enum value '%s' on class '%s'", arg, cn(inner()));
1415         }
1416      }
1417
1418      if (fromStringMethod.isPresent())
1419         return (T)fromStringMethod.get().invoke(null, arg);
1420
1421      if (stringConstructor.isPresent()) {
1422         if (nonStaticMemberClass)
1423            return stringConstructor.get().<T>newInstance(outer, arg);
1424         return stringConstructor.get().<T>newInstance(arg);
1425      }
1426      throw exex("No string constructor or valueOf(String) method found for class '%s'", cn(inner()));
1427   }
1428
1429   /**
1430    * Similar to {@link #equals(Object)} except primitive and Object types that are similar are considered the same.
1431    * (e.g. <jk>boolean</jk> == <c>Boolean</c>).
1432    *
1433    * @param cm The class meta to compare to. Can be <jk>null</jk>, in which case this method returns <jk>false</jk>.
1434    * @return <jk>true</jk> if the specified class-meta is equivalent to this one.
1435    */
1436   public boolean same(ClassMeta<?> cm) {
1437      if (cm == null)
1438         return false;
1439      if (equals(cm))
1440         return true;
1441      return (isPrimitive() && cat.same(cm.cat));
1442   }
1443
1444   @Override /* Overridden from Object */
1445   public String toString() {
1446      return toString(false);
1447   }
1448
1449   /**
1450    * Same as {@link #toString()} except use simple class names.
1451    *
1452    * @param simple Print simple class names only (no package).
1453    * @return A new string.
1454    */
1455   public String toString(boolean simple) {
1456      return toString(new StringBuilder(), simple).toString();
1457   }
1458
1459   /**
1460    * Converts the specified object to a string.
1461    *
1462    * @param t The object to convert.  Can be <jk>null</jk> (returns <jk>null</jk>).
1463    * @return The object converted to a string, or <jk>null</jk> if the object was null.
1464    */
1465   public String toString(Object t) {
1466      if (t == null)
1467         return null;
1468      if (isEnum())
1469         return marshallingContext.getEnumFormat().format((Enum<?>)t);
1470      return t.toString();
1471   }
1472
1473   private ObjectSwap<T,?> createSwap(Swap s) {
1474      var c = s.value();
1475      if (ClassUtils.isVoid(c))
1476         c = s.impl();
1477      var ci = info(c);
1478
1479      if (ci.isAssignableTo(ObjectSwap.class)) {
1480         var ps = BeanInstantiator.of(ObjectSwap.class).type(ci).run();
1481         if (s.mediaTypes().length > 0)
1482            ps.forMediaTypes(MediaType.ofAll(s.mediaTypes()));
1483         if (! s.template().isEmpty())
1484            ps.withTemplate(s.template());
1485         return ps;
1486      }
1487
1488      if (ci.isAssignableTo(Surrogate.class)) {
1489         List<SurrogateSwap<?,?>> l = SurrogateSwap.findObjectSwaps(c, marshallingContext);
1490         return first(l).map(x -> (ObjectSwap<T,?>)x).orElse(null);
1491      }
1492
1493      throw new ClassMetaRuntimeException(c, "Invalid swap class '%s' specified.  Must extend from ObjectSwap or Surrogate.", c);
1494   }
1495
1496   private String findBeanDictionaryName() {
1497      if (marshallingContext == null)
1498         return null;
1499
1500      var d = beanMeta.get().optBeanMeta().map(x -> x.getDictionaryName()).orElse(null);
1501      if (nn(d))
1502         return d;
1503
1504      // Note that @Marshalled(typeName) can be defined on non-bean types, so
1505      // we have to check again.
1506      return marshallingContext.getAnnotationProvider().find(Marshalled.class, this)
1507         .stream()
1508         .map(AnnotationInfo::inner)
1509         .filter(x -> ! x.typeName().isEmpty())
1510         .map(Marshalled::typeName)
1511         .findFirst()
1512         .orElse(null);
1513   }
1514
1515   private BeanMeta.BeanMetaValue<T> findBeanMeta() {
1516      if (! cat.isUnknown())
1517         return new BeanMeta.BeanMetaValue<>((BeanMeta<T>)null, "Known non-bean type");
1518      return BeanMeta.create(this, implClass.get());
1519   }
1520
1521   private KeyValueTypes findKeyValueTypes() {
1522      if (cat.is(MAP) && ! cat.is(BEANMAP)) {
1523         // If this is a MAP, see if it's parameterized (e.g. AddressBook extends HashMap<String,Person>)
1524         var typeParams = marshallingContext.findParameters(inner(), inner());
1525         if (nn(typeParams) && typeParams.length == 2) {
1526            return new KeyValueTypes(typeParams[0], typeParams[1]);
1527         }
1528         return new KeyValueTypes(marshallingContext.getClassMeta(Object.class), marshallingContext.getClassMeta(Object.class));
1529      }
1530      return new KeyValueTypes(null, null);
1531   }
1532
1533   private ClassMeta<?> findElementType() {
1534      if (marshallingContext == null)
1535         return null;
1536      // Primitive optional types carry no generic parameter — synthesize the matching boxed element type so
1537      // the first-class optional path (serialize unwrap / parse element-typed wrap) has a target type.
1538      if (is(OptionalInt.class))
1539         return marshallingContext.getClassMeta(Integer.class);
1540      if (is(OptionalLong.class))
1541         return marshallingContext.getClassMeta(Long.class);
1542      if (is(OptionalDouble.class))
1543         return marshallingContext.getClassMeta(Double.class);
1544      if (cat.is(ARRAY)) {
1545         return marshallingContext.getClassMeta(inner().getComponentType());
1546      } else if (cat.is(COLLECTION) || cat.is(ITERABLE) || cat.is(ITERATOR) || cat.is(STREAM) || is(Optional.class)) {
1547         var typeParams = marshallingContext.findParameters(inner(), inner());
1548         if (nn(typeParams) && typeParams.length == 1) {
1549            return typeParams[0];
1550         }
1551         return marshallingContext.getClassMeta(Object.class);
1552      }
1553      return null;
1554   }
1555
1556   private BuilderSwap<T,?> findBuilderSwap() {
1557      var bc = marshallingContext;
1558      if (bc == null)
1559         return null;
1560      return (BuilderSwap<T,?>)BuilderSwap.findSwapFromObjectClass(bc, inner(), bc.getBeanConstructorVisibility(), bc.getBeanMethodVisibility());
1561   }
1562
1563   private List<ObjectSwap<T,?>> findSwaps() {
1564      if (marshallingContext == null)
1565         return l();
1566
1567      var list = new ArrayList<ObjectSwap<T,?>>();
1568      var swapArray = marshallingContext.getSwaps();
1569      if (! swapArray.isEmpty()) {
1570         var innerClass = inner();
1571         swapArray.stream().filter(f -> f.getNormalClass().isAssignableFrom(innerClass)).forEach(f -> list.add((ObjectSwap<T,?>)f));
1572      }
1573
1574      var ap = marshallingContext.getAnnotationProvider();
1575      ap.find(Swap.class, this).stream().map(AnnotationInfo::inner).forEach(x -> list.add(createSwap(x)));
1576
1577      // @Marshalled(as=STRING) installs an AutoStringSwap before any auto-detection.
1578      if (list.isEmpty()) {
1579         var marshalledList = ap.find(Marshalled.class, this);
1580         if (marshalledList.stream().anyMatch(x -> x.inner().as() == MarshalledAs.STRING))
1581            list.add(new AutoStringSwap<>(this));
1582      }
1583
1584      // @MarshalledIgnore on TYPE — skip entirely: serialize as null, parse as null.
1585      // Use String.class (not Object.class) as swapClass so sType.isObject() stays false
1586      // and the serializer correctly emits "null" when swap() returns null.
1587      if (list.isEmpty() && ap.has(MarshalledIgnore.class, this))
1588         list.add(new ObjectSwap<T,String>(inner(), String.class) {
1589            @Override public String swap(MarshallingSession session, T o) { return null; }
1590            @Override public T unswap(MarshallingSession session, String s, ClassMeta<?> hint) { return null; }
1591         });
1592
1593      var ds = DefaultSwaps.find(this);
1594      if (ds == null && !isStreamable()) {
1595         ds = AutoObjectSwap.find(marshallingContext, this);
1596      if (ds == null)
1597         ds = AutoNumberSwap.find(marshallingContext, this);
1598      if (ds == null)
1599         ds = AutoMapSwap.find(marshallingContext, this);
1600      if (ds == null)
1601         ds = AutoListSwap.find(marshallingContext, this);
1602      }
1603
1604      if (nn(ds))
1605         list.add((ObjectSwap<T,?>)ds);
1606
1607      return u(list);
1608   }
1609
1610   private List<ObjectSwap<?,?>> findChildSwaps() {
1611      if (marshallingContext == null)
1612         return l();
1613      var swapArray = marshallingContext.getSwaps();
1614      if (swapArray.isEmpty())
1615         return l();
1616      var list = new ArrayList<ObjectSwap<?,?>>();
1617      var innerClass = inner();
1618      swapArray.stream().filter(f -> f.getNormalClass().isAssignableTo(innerClass)).forEach(list::add);
1619      return u(list);
1620   }
1621
1622   private String findExample() {
1623
1624      var ex = beanMeta.get().optBeanMeta().map(x -> (MarshalledFilter) x.getBeanFilter()).map(x -> x.getExample()).orElse(null);
1625
1626      if (ex == null)
1627         ex = marshalledFilter.map(x -> x.getExample()).orElse(null);
1628
1629      if (ex == null && nn(marshallingContext))
1630         ex = marshallingContext.getAnnotationProvider().find(Example.class, this).stream().map(x -> x.inner().value()).filter(Shorts::ine).findFirst().orElse(null);
1631
1632      if (ex == null) {
1633         if (isAny(boolean.class, Boolean.class)) {
1634            ex = "true";
1635         } else if (isAny(char.class, Character.class)) {
1636            ex = "a";
1637         } else if (cat.is(CHARSEQ)) {
1638            ex = "foo";
1639         } else if (cat.is(ENUM)) {
1640            Optional<Enum<?>> e = first(EnumSet.allOf(asEnumClass(inner())));
1641            ex = e.map(x -> marshallingContext.getEnumFormat().format(x)).orElse(null);
1642         } else if (isAny(float.class, Float.class, double.class, Double.class)) {
1643            ex = "1.0";
1644         } else if (isAny(short.class, Short.class, int.class, Integer.class, long.class, Long.class)) {
1645            ex = "1";
1646         }
1647      }
1648
1649      return ex;
1650   }
1651
1652   private FieldInfo findExampleField() {
1653      var ap = marshallingContext.getAnnotationProvider();
1654
1655      return getDeclaredFields()
1656         .stream()
1657         .filter(x -> x.isStatic() && isAssignableFrom(x.getFieldType()) && ap.has(Example.class, x))
1658         .map(x -> x.accessible())
1659         .findFirst()
1660         .orElse(null);
1661   }
1662
1663   private MethodInfo findExampleMethod() {
1664      // @formatter:off
1665      var ap = marshallingContext.getAnnotationProvider();
1666
1667      // Option 1:  Public example() or @Example method.
1668      var m = getPublicMethod(
1669         x -> x.isStatic() && x.isNotDeprecated() && (x.hasName("example") || ap.has(Example.class, x)) && x.hasParameterTypesLenient(MarshallingSession.class)
1670      );
1671      if (m.isPresent()) return m.get();
1672
1673      // Option 2:  Non-public @Example method.
1674      return getDeclaredMethods()
1675         .stream()
1676         .flatMap(x -> x.getMatchingMethods().stream())
1677         .filter(x -> x.isStatic() && ap.has(Example.class, x))
1678         .map(x -> x.accessible())
1679         .findFirst()
1680         .orElse(null);
1681      // @formatter:on
1682   }
1683
1684   private MethodInfo findFromStringMethod() {
1685      // @formatter:off
1686
1687      // Option 1:  Explicit @StringCtor(name) short-circuits the name-based guessing below.
1688      // marshallingContext is null for context-less ClassMetas (e.g. the args-array ClassMeta), so there's
1689      // no annotation provider to consult in that case.
1690      if (marshallingContext != null) {
1691         var ap = marshallingContext.getAnnotationProvider();
1692         var stringCtor = ap.find(StringCtor.class, this).stream().findFirst();
1693         if (stringCtor.isPresent()) {
1694            var methodName = stringCtor.get().inner().value();
1695            return getPublicMethod(
1696               x -> x.isStatic() && x.hasReturnType(this) && x.hasParameterTypes(String.class) && x.hasName(methodName)
1697            ).orElse(null);
1698         }
1699      }
1700
1701      // Option 2:  Find static fromString(String) or equivalent method by conventional name.
1702      // fromString() must be checked before valueOf() so that Enum classes can create their own
1703      //    specialized fromString() methods to override the behavior of Enum.valueOf(String).
1704      // valueOf() is used by enums.
1705      // parse() is used by the java logging Level class.
1706      // forName() is used by Class and Charset
1707      var names = a("fromString", "fromValue", "valueOf", "parse", "parseString", "forName", "forString");
1708      return getPublicMethod(
1709         x -> x.isStatic() && x.isNotDeprecated() && x.hasReturnType(this) && x.hasParameterTypes(String.class) && contains(x.getName(), names)
1710      ).orElse(null);
1711      // @formatter:on
1712   }
1713
1714   private ClassInfoTyped<? extends T> findImplClass() {
1715
1716      if (is(Object.class))
1717         return null;
1718
1719      var mf = marshalledFilter.get();
1720      if (mf == null)
1721         return null;
1722      var implClassInfo = mf.getImplClass();
1723      if (implClassInfo == null)
1724         return null;
1725      return (ClassInfoTyped<? extends T>)implClassInfo;
1726   }
1727
1728   private MarshalledFilter findMarshalledFilter() {
1729      var ap = marshallingContext.getAnnotationProvider();
1730      var l = ap.find(Marshalled.class, this);
1731      var bt = ap.find(BeanType.class, this);
1732      if (l.isEmpty() && bt.isEmpty())
1733         return null;
1734      return MarshalledFilter.create(this)
1735         .applyAnnotations(reverse(l.stream().map(AnnotationInfo::inner).toList()))
1736         .applyBeanTypeAnnotations(reverse(bt.stream().map(AnnotationInfo::inner).toList()))
1737         .build();
1738   }
1739
1740   private Property<T,Object> findNameProperty() {
1741      var ap = marshallingContext.getAnnotationProvider();
1742
1743      var s = getAllFields()
1744         .stream()
1745         .filter(x -> ap.has(NameProperty.class, x))
1746         .map(x -> x.accessible())
1747         .map(x -> Property.<T,Object>create().field(x).build())
1748         .findFirst();
1749
1750      if (s.isPresent()) return s.get();
1751
1752      var builder = Property.<T,Object>create();
1753
1754      // Look for setter method (1 parameter) with @NameProperty
1755      var setterMethod = getAllMethods()
1756         .stream()
1757         .filter(x -> ap.has(NameProperty.class, x) && x.hasNumParameters(1))
1758         .findFirst();
1759
1760      if (setterMethod.isPresent()) {
1761         builder.setter(setterMethod.get().accessible());
1762
1763         // Try to find a corresponding getter method (even if not annotated)
1764         // If setter is "setName", look for "getName" or "isName"
1765         var setterName = setterMethod.get().getNameSimple();
1766         if (setterName.startsWith("set") && setterName.length() > 3) {
1767            var propertyName = setterName.substring(3);
1768            var getterName1 = "get" + propertyName;
1769            var getterName2 = "is" + propertyName;
1770
1771            var getter = getAllMethods()
1772               .stream()
1773               .filter(x -> !x.isStatic() && x.hasNumParameters(0) &&
1774                  (x.hasName(getterName1) || x.hasName(getterName2)) &&
1775                  !x.getReturnType().is(Void.TYPE))
1776               .findFirst();
1777
1778            if (getter.isPresent()) {
1779               builder.getter(getter.get().accessible());
1780            } else {
1781               // Try to find a field with the property name (lowercase first letter)
1782               var fieldName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
1783               var field = getAllFields()
1784                  .stream()
1785                  .filter(x -> !x.isStatic() && x.hasName(fieldName))
1786                  .findFirst();
1787
1788               if (field.isPresent()) {
1789                  var f = field.get().accessible();
1790                  builder.getter(f::get);
1791               }
1792            }
1793         }
1794      }
1795
1796      // Look for getter method (0 parameters, non-void return) with @NameProperty
1797      var getterMethod = getAllMethods()
1798         .stream()
1799         .filter(x -> ap.has(NameProperty.class, x) && x.hasNumParameters(0) && !x.getReturnType().is(Void.TYPE))
1800         .findFirst();
1801
1802      if (getterMethod.isPresent()) {
1803         builder.getter(getterMethod.get().accessible());
1804      }
1805
1806      // Return null if neither setter nor getter was found
1807      if (setterMethod.isEmpty() && getterMethod.isEmpty())
1808         return null;
1809
1810      return builder.build();
1811   }
1812
1813   private ConstructorInfo findNoArgConstructor() {
1814
1815      if (is(Object.class))
1816         return null;
1817
1818      if (implClass.isPresent())
1819         return implClass.get().getPublicConstructor(x -> x.hasNumParameters(0)).orElse(null);
1820
1821      if (isAbstract())
1822         return null;
1823
1824      var numParams = nonStaticMemberClass ? 1 : 0;
1825      return getPublicConstructors()
1826         .stream()
1827         .filter(x -> x.isPublic() && x.isNotDeprecated() && x.hasNumParameters(numParams))
1828         .findFirst()
1829         .orElse(null);
1830   }
1831
1832   private Property<T,Object> findParentProperty() {
1833      var ap = marshallingContext.getAnnotationProvider();
1834
1835      var s = getAllFields()
1836         .stream()
1837         .filter(x -> ap.has(ParentProperty.class, x))
1838         .map(x -> x.accessible())
1839         .map(x -> Property.<T,Object>create().field(x).build())
1840         .findFirst();
1841
1842      if (s.isPresent()) return s.get();
1843
1844      var builder = Property.<T,Object>create();
1845
1846      // Look for setter method (1 parameter) with @ParentProperty
1847      var setterMethod = getAllMethods()
1848         .stream()
1849         .filter(x -> ap.has(ParentProperty.class, x) && x.hasNumParameters(1))
1850         .findFirst();
1851
1852      if (setterMethod.isPresent()) {
1853         builder.setter(setterMethod.get().accessible());
1854
1855         // Try to find a corresponding getter method (even if not annotated)
1856         // If setter is "setParent", look for "getParent" or "isParent"
1857         var setterName = setterMethod.get().getNameSimple();
1858         if (setterName.startsWith("set") && setterName.length() > 3) {
1859            var propertyName = setterName.substring(3);
1860            var getterName1 = "get" + propertyName;
1861            var getterName2 = "is" + propertyName;
1862
1863            var getter = getAllMethods()
1864               .stream()
1865               .filter(x -> !x.isStatic() && x.hasNumParameters(0) &&
1866                  (x.hasName(getterName1) || x.hasName(getterName2)) &&
1867                  !x.getReturnType().is(Void.TYPE))
1868               .findFirst();
1869
1870            if (getter.isPresent()) {
1871               builder.getter(getter.get().accessible());
1872            } else {
1873               // Try to find a field with the property name (lowercase first letter)
1874               var fieldName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
1875               var field = getAllFields()
1876                  .stream()
1877                  .filter(x -> !x.isStatic() && x.hasName(fieldName))
1878                  .findFirst();
1879
1880               if (field.isPresent()) {
1881                  var f = field.get().accessible();
1882                  builder.getter(f::get);
1883               }
1884            }
1885         }
1886      }
1887
1888      // Look for getter method (0 parameters, non-void return) with @ParentProperty
1889      var getterMethod = getAllMethods()
1890         .stream()
1891         .filter(x -> ap.has(ParentProperty.class, x) && x.hasNumParameters(0) && !x.getReturnType().is(Void.TYPE))
1892         .findFirst();
1893
1894      if (getterMethod.isPresent()) {
1895         builder.getter(getterMethod.get().accessible());
1896      }
1897
1898      // Return null if neither setter nor getter was found
1899      if (setterMethod.isEmpty() && getterMethod.isEmpty())
1900         return null;
1901
1902      return builder.build();
1903   }
1904
1905   private ConstructorInfo findStringConstructor() {
1906
1907      if (is(Object.class) || isAbstract())
1908         return null;
1909
1910      if (implClass.isPresent())
1911         return implClass.get().getPublicConstructor(x -> x.hasParameterTypes(String.class)).orElse(null);
1912
1913      if (isAbstract())
1914         return null;
1915
1916      var numParams = nonStaticMemberClass ? 2 : 1;
1917      return getPublicConstructors()
1918         .stream()
1919         .filter(x -> x.isPublic() && x.isNotDeprecated() && x.hasNumParameters(numParams))
1920         .filter(x -> x.getParameter(numParams == 2 ? 1 : 0).isType(String.class))
1921         .findFirst()
1922         .orElse(null);
1923   }
1924
1925   /**
1926    * Returns the {@link ObjectSwap} where the specified class is the same/subclass of the normal class of one of the
1927    * child POJO swaps associated with this class.
1928    *
1929    * @param normalClass The normal class being resolved.
1930    * @return The resolved {@link ObjectSwap} or <jk>null</jk> if none were found.
1931    */
1932   protected ObjectSwap<?,?> getChildObjectSwapForSwap(Class<?> normalClass) {
1933      return childSwapMap.get(normalClass);
1934   }
1935
1936   /**
1937    * Returns the {@link ObjectSwap} where the specified class is the same/subclass of the swap class of one of the child
1938    * POJO swaps associated with this class.
1939    *
1940    * @param swapClass The swap class being resolved.
1941    * @return The resolved {@link ObjectSwap} or <jk>null</jk> if none were found.
1942    */
1943   protected ObjectSwap<?,?> getChildObjectSwapForUnswap(Class<?> swapClass) {
1944      return childUnswapMap.get(swapClass);
1945   }
1946
1947   /**
1948    * Returns <jk>true</jk> if this class or any child classes has a {@link ObjectSwap} associated with it.
1949    *
1950    * <p>
1951    * Used when transforming bean properties to prevent having to look up transforms if we know for certain that no
1952    * transforms are associated with a bean property.
1953    *
1954    * @return <jk>true</jk> if this class or any child classes has a {@link ObjectSwap} associated with it.
1955    */
1956   protected boolean hasChildSwaps() {
1957      return ! childSwaps.get().isEmpty();
1958   }
1959
1960   /**
1961    * Appends this object as a readable string to the specified string builder.
1962    *
1963    * @param sb The string builder to append this object to.  Must not be <jk>null</jk>.
1964    * @param simple Print simple class names only (no package).
1965    * @return The passed-in string builder.
1966    */
1967   protected StringBuilder toString(StringBuilder sb, boolean simple) {
1968      var n = simple ? cnsq(inner()) : cn(inner());
1969      if (cat.is(ARRAY))
1970         return elementType.get().toString(sb, simple).append('[').append(']');
1971      if (cat.is(BEANMAP))
1972         return sb.append(cn(BeanMap.class)).append('<').append(n).append('>');
1973      if (cat.is(MAP)) {
1974         var kvTypes = keyValueTypes.get();
1975         var kt = kvTypes.optKeyType();
1976         var vt = kvTypes.optValueType();
1977         if (kt.isPresent() && vt.isPresent() && kt.get().isObject() && vt.get().isObject())
1978            return sb.append(n);
1979         return sb.append(n).append('<').append(kt.map(x -> x.toString(simple)).orElse("?")).append(',').append(vt.map(x -> x.toString(simple)).orElse("?")).append('>');
1980      }
1981      if (cat.is(COLLECTION) || is(Optional.class)) {
1982         var et = elementType.get();
1983         String typeString = et != null ? et.toString(simple) : "?";
1984         return sb.append(n).append(et != null && et.isObject() ? "" : "<" + typeString + ">");
1985      }
1986      return sb.append(n);
1987   }
1988}