001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under  the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau;
018
019import static org.apache.juneau.ClassMeta.Category.*;
020import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
021import static org.apache.juneau.commons.utils.CollectionUtils.*;
022import static org.apache.juneau.commons.utils.ThrowableUtils.*;
023import static org.apache.juneau.commons.utils.Utils.*;
024
025import java.io.*;
026import java.lang.annotation.*;
027import java.lang.reflect.*;
028import java.lang.reflect.Proxy;
029import java.net.*;
030import java.time.temporal.*;
031import java.util.*;
032import java.util.List;
033import java.util.concurrent.*;
034import java.util.function.*;
035
036import org.apache.juneau.annotation.*;
037import org.apache.juneau.commons.collections.*;
038import org.apache.juneau.commons.function.*;
039import org.apache.juneau.commons.reflect.*;
040import org.apache.juneau.commons.utils.*;
041import org.apache.juneau.cp.*;
042import org.apache.juneau.json.*;
043import org.apache.juneau.reflect.*;
044import org.apache.juneau.swap.*;
045
046/**
047 * A wrapper class around the {@link Class} object that provides cached information about that class.
048 *
049 * <p>
050 * Instances of this class can be created through the {@link BeanContext#getClassMeta(Class)} method.
051 *
052 * <p>
053 * The {@link BeanContext} class will cache and reuse instances of this class except for the following class types:
054 * <ul>
055 *    <li>Arrays
056 *    <li>Maps with non-Object key/values.
057 *    <li>Collections with non-Object key/values.
058 * </ul>
059 *
060 * <p>
061 * This class is tied to the {@link BeanContext} class because it's that class that makes the determination of what is
062 * a bean.
063 *
064 *
065 * @param <T> The class type of the wrapped class.
066 */
067@Bean(properties = "innerClass,elementType,keyType,valueType,notABeanReason,initException,beanMeta")
068public class ClassMeta<T> extends ClassInfoTyped<T> {
069
070   private static class Categories {
071      int bits;
072
073      public boolean same(Categories cat) {
074         return cat.bits == bits;
075      }
076
077      boolean is(Category c) {
078         return (bits & c.mask) != 0;
079      }
080
081      boolean isUnknown() {
082         return bits == 0;
083      }
084
085      Categories set(Category c) {
086         bits |= c.mask;
087         return this;
088      }
089   }
090
091   enum Category {
092      MAP(0),
093      COLLECTION(1),
094      NUMBER(2),
095      DECIMAL(3),
096      DATE(4),
097      ARRAY(5),
098      ENUM(6),
099      CHARSEQ(8),
100      STR(9),
101      URI(10),
102      BEANMAP(11),
103      READER(12),
104      INPUTSTREAM(13),
105      ARGS(14),
106      CALENDAR(15),
107      TEMPORAL(16),
108      LIST(17),
109      SET(18),
110      DELEGATE(19),
111      BEAN(20);
112
113      private final int mask;
114
115      Category(int bitPosition) {
116         this.mask = 1 << bitPosition;
117      }
118   }
119
120   /**
121    * Checks if the specified category is set in the bitmap.
122    *
123    * @param category The category to check.
124    * @return {@code true} if the category is set, {@code false} otherwise.
125    */
126
127   /**
128    * Generated classes shouldn't be cacheable to prevent needlessly filling up the cache.
129    */
130   private static boolean isCacheable(Class<?> c) {
131      var n = cn(c);
132      var x = n.charAt(n.length() - 1);  // All generated classes appear to end with digits.
133      if (x >= '0' && x <= '9') {
134         if (n.indexOf("$$") != -1 || n.startsWith("sun") || n.startsWith("com.sun") || n.indexOf("$Proxy") != -1)
135            return false;
136      }
137      return true;
138   }
139
140   private final List<ClassMeta<?>> args;                                     // Arg types if this is an array of args.
141   private final BeanContext beanContext;                                     // The bean context that created this object.
142   private final Supplier<BuilderSwap<T,?>> builderSwap;                      // The builder swap associated with this bean (if it has one).
143   private final Categories cat;                                              // The class category.
144   private final Cache<Class<?>,ObjectSwap<?,?>> childSwapMap;                // Maps normal subclasses to ObjectSwaps.
145   private final Supplier<List<ObjectSwap<?,?>>> childSwaps;                  // Any ObjectSwaps where the normal type is a subclass of this class.
146   private final Cache<Class<?>,ObjectSwap<?,?>> childUnswapMap;              // Maps swap subclasses to ObjectSwaps.
147   private final Supplier<String> beanDictionaryName;                             // The dictionary name of this class if it has one.
148   private final Supplier<ClassMeta<?>> elementType;                          // If ARRAY or COLLECTION, the element class type.
149   private final OptionalSupplier<String> example;                            // Example JSON.
150   private final OptionalSupplier<FieldInfo> exampleField;                    // The @Example-annotated field (if it has one).
151   private final OptionalSupplier<MethodInfo> exampleMethod;                  // The example() or @Example-annotated method (if it has one).
152   private final Supplier<BidiMap<Object,String>> enumValues;
153   private final Map<Class<?>,Mutater<?,T>> fromMutaters = new ConcurrentHashMap<>();
154   private final OptionalSupplier<MethodInfo> fromStringMethod;               // Static fromString(String) or equivalent method
155   private final OptionalSupplier<ClassInfoTyped<? extends T>> implClass;     // The implementation class to use if this is an interface.
156   private final Supplier<KeyValueTypes> keyValueTypes;                        // Key and value types for MAP types.
157   private final OptionalSupplier<MarshalledFilter> marshalledFilter;
158   private final Supplier<Property<T,Object>> nameProperty;                   // The method to set the name on an object (if it has one).
159   private final OptionalSupplier<ConstructorInfo> noArgConstructor;          // The no-arg constructor for this class (if it has one).
160   private final Supplier<Property<T,Object>> parentProperty;                 // The method to set the parent on an object (if it has one).
161   private final Cache<String,Optional<?>> properties;
162   private final Mutater<String,T> stringMutater;
163   private final OptionalSupplier<ConstructorInfo> stringConstructor;         // The X(String) constructor (if it has one).
164   private final Supplier<List<ObjectSwap<T,?>>> swaps;                       // The object POJO swaps associated with this bean (if it has any).
165   private final Map<Class<?>,Mutater<T,?>> toMutaters = new ConcurrentHashMap<>();
166   private final OptionalSupplier<BeanMeta.BeanMetaValue<T>> beanMeta;
167
168   private record KeyValueTypes(ClassMeta<?> keyType, ClassMeta<?> valueType) {
169      Optional<ClassMeta<?>> optKeyType() { return opt(keyType()); }
170      Optional<ClassMeta<?>> optValueType() { return opt(valueType()); }
171   }
172
173   /**
174    * Construct a new {@code ClassMeta} based on the specified {@link Class}.
175    *
176    * @param innerClass The class being wrapped.
177    * @param beanContext The bean context that created this object.
178    * @param delayedInit
179    *    Don't call init() in constructor.
180    *    Used for delayed initialization when the possibility of class reference loops exist.
181    */
182   ClassMeta(Class<T> innerClass, BeanContext beanContext) {
183      super(innerClass);
184      this.beanContext = beanContext;
185      this.cat = new Categories();
186
187      // We always immediately add this class meta to the bean context cache so that we can resolve recursive references.
188      if (nn(beanContext) && nn(beanContext.getCmCache()) && isCacheable(innerClass))
189         beanContext.getCmCache().put(innerClass, this);
190
191      var ap = beanContext.getAnnotationProvider();
192
193      if (isChildOf(Delegate.class)) {
194         cat.set(DELEGATE);
195      }
196      if (isEnum()) {
197         cat.set(ENUM);
198      } else if (isChildOf(CharSequence.class)) {
199         cat.set(CHARSEQ);
200         if (is(String.class)) {
201            cat.set(STR);
202         }
203      } else if (isChildOf(Number.class) || isAny(byte.class, short.class, int.class, long.class, float.class, double.class)) {
204         cat.set(NUMBER);
205         if (isChildOfAny(Float.class, Double.class) || isAny(float.class, double.class)) {
206            cat.set(DECIMAL);
207         }
208      } else if (isChildOf(Collection.class)) {
209         cat.set(COLLECTION);
210         if (isChildOf(Set.class)) {
211            cat.set(SET);
212         } else if (isChildOf(List.class)) {
213            cat.set(LIST);
214         }
215      } else if (isChildOf(Map.class)) {
216         cat.set(MAP);
217         if (isChildOf(BeanMap.class)) {
218            cat.set(BEANMAP);
219         }
220      } else if (isChildOfAny(Date.class, Calendar.class)) {
221         if (isChildOf(Date.class)) {
222            cat.set(DATE);
223         } else if (isChildOf(Calendar.class)) {
224            cat.set(CALENDAR);
225         }
226      } else if (isChildOf(Temporal.class)) {
227         cat.set(TEMPORAL);
228      } else if (inner().isArray()) {
229         cat.set(ARRAY);
230      } else if (isChildOfAny(URL.class, URI.class) || ap.has(Uri.class, this)) {
231         cat.set(URI);
232      } else if (isChildOf(Reader.class)) {
233         cat.set(READER);
234      } else if (isChildOf(InputStream.class)) {
235         cat.set(INPUTSTREAM);
236      }
237
238      beanMeta = mem(()->findBeanMeta());
239      builderSwap = mem(()->findBuilderSwap());
240      childSwapMap = Cache.<Class<?>,ObjectSwap<?,?>>create().supplier(x -> findSwap(x)).build();
241      childSwaps = mem(()->findChildSwaps());
242      childUnswapMap = Cache.<Class<?>,ObjectSwap<?,?>>create().supplier(x -> findUnswap(x)).build();
243      beanDictionaryName = mem(()->findBeanDictionaryName());
244      elementType = mem(()->findElementType());
245      enumValues = mem(()->findEnumValues());
246      example = mem(()->findExample());
247      exampleField = mem(()->findExampleField());
248      exampleMethod = mem(()->findExampleMethod());
249      fromStringMethod = mem(()->findFromStringMethod());
250      implClass = mem(()->findImplClass());
251      keyValueTypes = mem(()->findKeyValueTypes());
252      marshalledFilter = mem(()->findMarshalledFilter());
253      nameProperty = mem(()->findNameProperty());
254      noArgConstructor = mem(()->findNoArgConstructor());
255      parentProperty = mem(()->findParentProperty());
256      properties = Cache.<String,Optional<?>>create().build();
257      stringConstructor = mem(()->findStringConstructor());
258      swaps = mem(()->findSwaps());
259
260      this.args = null;
261      this.stringMutater = Mutaters.get(String.class, inner());
262   }
263
264   protected ObjectSwap<?,?> findSwap(Class<?> c) {
265      return childSwaps.get().stream().filter(x -> x.getNormalClass().isParentOf(c)).findFirst().orElse(null);
266   }
267
268   protected ObjectSwap<?,?> findUnswap(Class<?> c) {
269      return childSwaps.get().stream().filter(x -> x.getSwapClass().isParentOf(c)).findFirst().orElse(null);
270   }
271
272
273   /**
274    * Constructor for args-arrays.
275    */
276   @SuppressWarnings("unchecked")
277   ClassMeta(List<ClassMeta<?>> args) {
278      super((Class<T>)Object[].class);
279      this.args = args;
280      this.childSwaps = mem(()->findChildSwaps());
281      this.childSwapMap = null;
282      this.childUnswapMap = null;
283      this.cat = new Categories().set(ARGS);
284      this.beanContext = null;
285      this.elementType = mem(()->findElementType());
286      this.keyValueTypes = mem(()->findKeyValueTypes());
287      this.beanMeta = mem(()->findBeanMeta());
288      this.swaps = mem(()->findSwaps());
289      this.stringMutater = null;
290      this.fromStringMethod = mem(()->findFromStringMethod());
291      this.exampleMethod = mem(()->findExampleMethod());
292      this.parentProperty = mem(()->findParentProperty());
293      this.nameProperty = mem(()->findNameProperty());
294      this.exampleField = mem(()->findExampleField());
295      this.noArgConstructor = mem(()->findNoArgConstructor());
296      this.properties = Cache.<String,Optional<?>>create().build();
297      this.stringConstructor = mem(()->findStringConstructor());
298      this.marshalledFilter = mem(()->findMarshalledFilter());
299      this.builderSwap = mem(()->findBuilderSwap());
300      this.example = mem(()->findExample());
301      this.implClass = mem(()->findImplClass());
302      this.enumValues = mem(()->findEnumValues());
303      this.beanDictionaryName = mem(()->findBeanDictionaryName());
304   }
305
306   /**
307    * Copy constructor.
308    *
309    * <p>
310    * Used for creating Map and Collection class metas that shouldn't be cached.
311    */
312   ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> valueType, ClassMeta<?> elementType) {
313      super(mainType.inner());
314      this.childSwaps = mainType.childSwaps;
315      this.childSwapMap = mainType.childSwapMap;
316      this.childUnswapMap = mainType.childUnswapMap;
317      this.cat = mainType.cat;
318      this.fromStringMethod = mainType.fromStringMethod;
319      this.beanContext = mainType.beanContext;
320      this.elementType = elementType != null ? mem(()->elementType) : mainType.elementType;
321      this.keyValueTypes = (keyType != null || valueType != null) ? mem(()->new KeyValueTypes(keyType, valueType)) : mainType.keyValueTypes;
322      this.beanMeta = mainType.beanMeta;
323      this.swaps = mainType.swaps;
324      this.exampleMethod = mainType.exampleMethod;
325      this.args = null;
326      this.stringMutater = mainType.stringMutater;
327      this.parentProperty = mainType.parentProperty;
328      this.nameProperty = mainType.nameProperty;
329      this.exampleField = mainType.exampleField;
330      this.noArgConstructor = mainType.noArgConstructor;
331      this.properties = mainType.properties;
332      this.stringConstructor = mainType.stringConstructor;
333      this.marshalledFilter = mainType.marshalledFilter;
334      this.builderSwap = mainType.builderSwap;
335      this.example = mainType.example;
336      this.implClass = mainType.implClass;
337      this.enumValues = mainType.enumValues;
338      this.beanDictionaryName = mainType.beanDictionaryName;
339   }
340
341   /**
342    * Returns <jk>true</jk> if this class can be instantiated as a bean.
343    * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match the class type of
344    * the defining class.
345    *
346    * @param outer
347    *    The outer class object for non-static member classes.  Can be <jk>null</jk> for non-member or static classes.
348    * @return
349    *    <jk>true</jk> if a new instance of this bean can be created within the context of the specified outer object.
350    */
351   public boolean canCreateNewBean(Object outer) {
352      var bm = getBeanMeta();
353      if (bm == null || ! bm.hasConstructor())
354         return false;
355      if (isMemberClass() && isNotStatic())
356         return nn(outer) && bm.getConstructor().hasParameterTypes(outer.getClass());
357      return true;
358   }
359
360   /**
361    * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler.
362    *
363    * @return <jk>true</jk> if a new instance of this class can be constructed.
364    */
365   public boolean canCreateNewInstance() {
366      if (isMemberClass() && isNotStatic())
367         return false;
368      var bm = getBeanMeta();
369      if (noArgConstructor.isPresent() || (bm != null && bm.getBeanProxyInvocationHandler() != null) || (isArray() && elementType.get().canCreateNewInstance()))
370         return true;
371      return false;
372   }
373
374   /**
375    * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler.
376    * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match the class type of
377    * the defining class.
378    *
379    * @param outer
380    *    The outer class object for non-static member classes.  Can be <jk>null</jk> for non-member or static classes.
381    * @return
382    *    <jk>true</jk> if a new instance of this class can be created within the context of the specified outer object.
383    */
384   public boolean canCreateNewInstance(Object outer) {
385      if (isMemberClass() && isNotStatic())
386         return nn(outer) && noArgConstructor.map(x -> x.hasParameterTypes(outer.getClass())).orElse(false);
387      return canCreateNewInstance();
388   }
389
390   /**
391    * Returns <jk>true</jk> if this class can call the {@link #newInstanceFromString(Object, String)} method.
392    *
393    * @param outer
394    *    The outer class object for non-static member classes.
395    *    Can be <jk>null</jk> for non-member or static classes.
396    * @return <jk>true</jk> if this class has a no-arg constructor or invocation handler.
397    */
398   public boolean canCreateNewInstanceFromString(Object outer) {
399      if (fromStringMethod.isPresent())
400         return true;
401      if (stringConstructor.isPresent()) {
402         if (isMemberClass() && isNotStatic())
403            return nn(outer) && stringConstructor.map(x -> x.hasParameterTypes(outer.getClass(), String.class)).orElse(false);
404         return true;
405      }
406      return false;
407   }
408
409   @Override /* Overridden from Object */
410   public boolean equals(Object o) {
411      return (o instanceof ClassMeta<?>) && super.equals(o);
412   }
413
414   /**
415    * Performs an action on all matching annotations of the specified type defined on this class or parent classes/interfaces in parent-to-child order.
416    *
417    * @param <A> The annotation type to look for.
418    * @param type The annotation to search for.
419    * @param filter A predicate to apply to the entries to determine if action should be performed.  Can be <jk>null</jk>.
420    * @param action An action to perform on the entry.
421    * @return This object.
422    */
423   public <A extends Annotation> ClassMeta<T> forEachAnnotation(Class<A> type, Predicate<A> filter, Consumer<A> action) {
424      if (beanContext != null) {
425         beanContext.getAnnotationProvider().find(type, this).stream().map(AnnotationInfo::inner).filter(x -> filter == null || filter.test(x)).forEach(x -> action.accept(x));
426      }
427      return this;
428   }
429
430   /**
431    * Returns the argument metadata at the specified index if this is an args metadata object.
432    *
433    * @param index The argument index.
434    * @return The The argument metadata.  Never <jk>null</jk>.
435    * @throws BeanRuntimeException If this metadata object is not a list of arguments, or the index is out of range.
436    */
437   public ClassMeta<?> getArg(int index) {
438      if (nn(args) && index >= 0 && index < args.size())
439         return args.get(index);
440      throw bex("Invalid argument index specified:  {0}.  Only {1} arguments are defined.", index, args == null ? 0 : args.size());
441   }
442
443   /**
444    * Returns the argument types of this meta.
445    *
446    * @return The argument types of this meta, or <jk>null</jk> if this isn't an array of argument types.
447    */
448   public List<ClassMeta<?>> getArgs() { return args; }
449
450   /**
451    * Returns the {@link BeanContext} that created this object.
452    *
453    * @return The bean context.
454    */
455   public BeanContext getBeanContext() { return beanContext; }
456
457   /**
458    * Returns the {@link BeanMeta} associated with this class.
459    *
460    * @return
461    *    The {@link BeanMeta} associated with this class, or <jk>null</jk> if there is no bean meta associated with
462    *    this class.
463    */
464   public BeanMeta<T> getBeanMeta() {
465      return beanMeta.get().beanMeta();
466   }
467
468   /**
469    * Returns the bean registry for this class.
470    *
471    * <p>
472    * This bean registry contains names specified in the {@link Bean#dictionary() @Bean(dictionary)} annotation
473    * defined on the class, regardless of whether the class is an actual bean.
474    * This allows interfaces to define subclasses with type names.
475    *
476    * <p>
477    * This is a shortcut for calling getBeanMeta().getBeanRegistry().
478    *
479    * @return The bean registry for this class, or <jk>null</jk> if no bean registry is associated with it.
480    */
481   public BeanRegistry getBeanRegistry() {
482      return beanMeta.get().optBeanMeta().map(x -> x.getBeanRegistry()).orElse(null);
483   }
484
485   /**
486    * Returns the builder swap associated with this class.
487    *
488    * @param session The current bean session.
489    * @return The builder swap associated with this class, or <jk>null</jk> if it doesn't exist.
490    */
491   public BuilderSwap<T,?> getBuilderSwap(BeanSession session) {
492      return builderSwap.get();
493   }
494
495   /**
496    * Returns the bean dictionary name associated with this class.
497    *
498    * <p>
499    * The lexical name is defined by {@link Bean#typeName() @Bean(typeName)}.
500    *
501    * @return
502    *    The type name associated with this bean class, or <jk>null</jk> if there is no type name defined or this
503    *    isn't a bean.
504    */
505   public String getBeanDictionaryName() {
506      return beanDictionaryName.get();
507   }
508
509   /**
510    * For array and {@code Collection} types, returns the class type of the components of the array or
511    * {@code Collection}.
512    *
513    * @return The element class type, or <jk>null</jk> if this class is not an array or Collection.
514    */
515   public ClassMeta<?> getElementType() { return elementType.get(); }
516
517   /**
518    * Returns the example of this class.
519    *
520    * @param session
521    *    The bean session.
522    *    <br>Required because the example method may take it in as a parameter.
523    * @param jpSession The JSON parser for parsing examples into POJOs.
524    * @return The serialized class type, or this object if no swap is associated with the class.
525    */
526   @SuppressWarnings({ "unchecked" })
527   public T getExample(BeanSession session, JsonParserSession jpSession) {
528      try {
529         if (example.isPresent())
530            return jpSession.parse(example.get(), this);
531         if (exampleMethod.isPresent())
532            return (T)exampleMethod.get().invokeLenient(null, session);
533         if (exampleField.isPresent())
534            return (T)exampleField.get().get(null);
535
536         if (isCollection()) {
537            var etExample = getElementType().getExample(session, jpSession);
538            if (nn(etExample)) {
539               if (canCreateNewInstance()) {
540                  var c = (Collection<Object>)newInstance();
541                  c.add(etExample);
542                  return (T)c;
543               }
544               return (T)Collections.singleton(etExample);
545            }
546         } else if (super.isArray()) {
547            var etExample = getElementType().getExample(session, jpSession);
548            if (nn(etExample)) {
549               var o = Array.newInstance(getElementType().inner(), 1);
550               Array.set(o, 0, etExample);
551               return (T)o;
552            }
553         } else if (isMap()) {
554            var vtExample = getValueType().getExample(session, jpSession);
555            var ktExample = getKeyType().getExample(session, jpSession);
556            if (nn(ktExample) && nn(vtExample)) {
557               if (canCreateNewInstance()) {
558                  var m = (Map<Object,Object>)newInstance();
559                  m.put(ktExample, vtExample);
560                  return (T)m;
561               }
562               return (T)Collections.singletonMap(ktExample, vtExample);
563            }
564         }
565
566         return null;
567      } catch (Exception e) {
568         throw new ClassMetaRuntimeException(e);
569      }
570   }
571
572   /**
573    * Returns the transform for this class for creating instances from other object types.
574    *
575    * @param <I> The transform-from class.
576    * @param c The transform-from class.
577    * @return The transform, or <jk>null</jk> if no such transform exists.
578    */
579   @SuppressWarnings({ "rawtypes", "unchecked" })
580   public <I> Mutater<I,T> getFromMutater(Class<I> c) {
581      Mutater t = fromMutaters.get(c);
582      if (t == Mutaters.NULL)
583         return null;
584      if (t == null) {
585         t = Mutaters.get(c, inner());
586         if (t == null)
587            t = Mutaters.NULL;
588         fromMutaters.put(c, t);
589      }
590      return t == Mutaters.NULL ? null : t;
591   }
592
593   /**
594    * Returns the no-arg constructor for this class based on the {@link Marshalled#implClass()} value.
595    *
596    * @param conVis The constructor visibility.
597    * @return The no-arg constructor for this class, or <jk>null</jk> if it does not exist.
598    */
599   public ConstructorInfo getImplClassConstructor(Visibility conVis) {
600      return implClass.map(x -> x.getNoArgConstructor(conVis).orElse(null)).orElse(null);
601   }
602
603   /**
604    * Returns the transform for this class for creating instances from an InputStream.
605    *
606    * @return The transform, or <jk>null</jk> if no such transform exists.
607    */
608   public Mutater<InputStream,T> getInputStreamMutater() { return getFromMutater(InputStream.class); }
609
610   /**
611    * For {@code Map} types, returns the class type of the keys of the {@code Map}.
612    *
613    * @return The key class type, or <jk>null</jk> if this class is not a Map.
614    */
615   public ClassMeta<?> getKeyType() {
616      return keyValueTypes.get().keyType();
617   }
618
619   /**
620    * Returns the method or field annotated with {@link NameProperty @NameProperty}.
621    *
622    * @return
623    *    The method or field  annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not
624    *    exist.
625    */
626   public Property<T,Object> getNameProperty() { return nameProperty.get(); }
627
628   /**
629    * Returns the reason why this class is not a bean, or <jk>null</jk> if it is a bean.
630    *
631    * @return The reason why this class is not a bean, or <jk>null</jk> if it is a bean.
632    */
633   public synchronized String getNotABeanReason() {
634      return beanMeta.get().notABeanReason();
635   }
636
637   /**
638    * If this is an {@link Optional}, returns an empty optional.
639    *
640    * <p>
641    * Note that if this is a nested optional, will recursively create empty optionals.
642    *
643    * @return An empty optional, or <jk>null</jk> if this isn't an optional.
644    */
645   public Optional<?> getOptionalDefault() {
646      if (isOptional())
647         return opt(getElementType().getOptionalDefault());
648      return null;
649   }
650
651   /**
652    * Returns the method or field annotated with {@link ParentProperty @ParentProperty}.
653    *
654    * @return
655    *    The method or field annotated with {@link ParentProperty @ParentProperty} or <jk>null</jk> if method does not
656    *    exist.
657    */
658   public Property<T,Object> getParentProperty() { return parentProperty.get(); }
659
660   /**
661    * Returns a lazily-computed, cached property value for this {@link ClassMeta} instance.
662    *
663    * <p>
664    * This method provides a memoization mechanism for expensive computations. The property value is computed
665    * on the first call using the provided function, then cached for subsequent calls with the same property name.
666    *
667    * <p>
668    * The function is only invoked once per property name per {@link ClassMeta} instance. Subsequent calls
669    * with the same name will return the cached value without re-invoking the function.
670    *
671    * <h5 class='section'>Thread Safety:</h5>
672    * <p>
673    * This method is thread-safe. If multiple threads call this method simultaneously with the same property name,
674    * the function may be invoked multiple times, but only one result will be cached and returned.
675    *
676    * <h5 class='section'>Usage:</h5>
677    * <p class='bjava'>
678    *    <jc>// Compute and cache an expensive property</jc>
679    *    Optional&lt;String&gt; <jv>computedValue</jv> = classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -&gt; {
680    *       <jc>// Expensive computation that only runs once</jc>
681    *       <jk>return</jk> performExpensiveComputation(cm);
682    *    });
683    *
684    *    <jc>// Subsequent calls return cached value</jc>
685    *    Optional&lt;String&gt; <jv>cached</jv> = classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -&gt; {
686    *       <jc>// This function is NOT called again</jc>
687    *       <jk>return</jk> performExpensiveComputation(cm);
688    *    });
689    * </p>
690    *
691    * @param <T2> The type of the property value.
692    * @param name The unique name identifying this property. Used as the cache key.
693    * @param function The function that computes the property value. Receives this {@link ClassMeta} instance as input.
694    *    Only invoked if the property hasn't been computed yet. Can return <jk>null</jk>.
695    * @return An {@link Optional} containing the property value if the function returned a non-null value,
696    *    otherwise an empty {@link Optional}. Never <jk>null</jk>.
697    */
698   @SuppressWarnings("unchecked")
699   public <T2> Optional<T2> getProperty(String name, Function<ClassMeta<?>,T2> function) {
700      return (Optional<T2>)properties.get(name, () -> opt(function.apply(this)));
701   }
702
703   /**
704    * Returns the interface proxy invocation handler for this class.
705    *
706    * @return The interface proxy invocation handler, or <jk>null</jk> if it does not exist.
707    */
708   public InvocationHandler getProxyInvocationHandler() {
709      return beanMeta.get().optBeanMeta().map(x -> x.getBeanProxyInvocationHandler()).orElse(null);
710   }
711
712   /**
713    * Returns the transform for this class for creating instances from a Reader.
714    *
715    * @return The transform, or <jk>null</jk> if no such transform exists.
716    */
717   public Mutater<Reader,T> getReaderMutater() { return getFromMutater(Reader.class); }
718
719   /**
720    * Returns the serialized (swapped) form of this class if there is an {@link ObjectSwap} associated with it.
721    *
722    * @param session
723    *    The bean session.
724    *    <br>Required because the swap used may depend on the media type being serialized or parsed.
725    * @return The serialized class type, or this object if no swap is associated with the class.
726    */
727   public ClassMeta<?> getSerializedClassMeta(BeanSession session) {
728      var ps = getSwap(session);
729      return (ps == null ? this : ps.getSwapClassMeta(session));
730   }
731
732   /**
733    * Returns the transform for this class for creating instances from a String.
734    *
735    * @return The transform, or <jk>null</jk> if no such transform exists.
736    */
737   public Mutater<String,T> getStringMutater() { return stringMutater; }
738
739   /**
740    * Returns the {@link ObjectSwap} associated with this class that's the best match for the specified session.
741    *
742    * @param session
743    *    The current bean session.
744    *    <br>If multiple swaps are associated with a class, only the first one with a matching media type will
745    *    be returned.
746    * @return
747    *    The {@link ObjectSwap} associated with this class, or <jk>null</jk> if there are no POJO swaps associated with
748    *    this class.
749    */
750   public ObjectSwap<T,?> getSwap(BeanSession session) {
751      var swapsList = swaps.get();
752      if (! swapsList.isEmpty()) {
753         var matchQuant = 0;
754         ObjectSwap<T,?> matchSwap = null;
755
756         for (var swap : swapsList) {
757            var q = swap.match(session);
758            if (q > matchQuant) {
759               matchQuant = q;
760               matchSwap = swap;
761            }
762         }
763
764         if (matchSwap != null)
765            return matchSwap;
766      }
767      return null;
768   }
769
770   /**
771    * Returns the transform for this class for creating instances from other object types.
772    *
773    * @param <O> The transform-to class.
774    * @param c The transform-from class.
775    * @return The transform, or <jk>null</jk> if no such transform exists.
776    */
777   @SuppressWarnings({ "rawtypes", "unchecked" })
778   public <O> Mutater<T,O> getToMutater(Class<O> c) {
779      Mutater t = toMutaters.get(c);
780      if (t == Mutaters.NULL)
781         return null;
782      if (t == null) {
783         t = Mutaters.get(inner(), c);
784         if (t == null)
785            t = Mutaters.NULL;
786         toMutaters.put(c, t);
787      }
788      return t == Mutaters.NULL ? null : t;
789   }
790
791   /**
792    * For {@code Map} types, returns the class type of the values of the {@code Map}.
793    *
794    * @return The value class type, or <jk>null</jk> if this class is not a Map.
795    */
796   public ClassMeta<?> getValueType() {
797      return keyValueTypes.get().valueType();
798   }
799
800   /**
801    * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream.
802    *
803    * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from an InputStream.
804    */
805   public boolean hasInputStreamMutater() {
806      return hasMutaterFrom(InputStream.class);
807   }
808
809   /**
810    * Returns <jk>true</jk> if this class can be instantiated from the specified type.
811    *
812    * @param c The class type to convert from.
813    * @return <jk>true</jk> if this class can be instantiated from the specified type.
814    */
815   public boolean hasMutaterFrom(Class<?> c) {
816      return nn(getFromMutater(c));
817   }
818
819   /**
820    * Returns <jk>true</jk> if this class can be instantiated from the specified type.
821    *
822    * @param c The class type to convert from.
823    * @return <jk>true</jk> if this class can be instantiated from the specified type.
824    */
825   public boolean hasMutaterFrom(ClassMeta<?> c) {
826      return nn(getFromMutater(c.inner()));
827   }
828
829   /**
830    * Returns <jk>true</jk> if this class can be transformed to the specified type.
831    *
832    * @param c The class type to convert from.
833    * @return <jk>true</jk> if this class can be transformed to the specified type.
834    */
835   public boolean hasMutaterTo(Class<?> c) {
836      return nn(getToMutater(c));
837   }
838
839   /**
840    * Returns <jk>true</jk> if this class can be transformed to the specified type.
841    *
842    * @param c The class type to convert from.
843    * @return <jk>true</jk> if this class can be transformed to the specified type.
844    */
845   public boolean hasMutaterTo(ClassMeta<?> c) {
846      return nn(getToMutater(c.inner()));
847   }
848
849   /**
850    * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader.
851    *
852    * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a Reader.
853    */
854   public boolean hasReaderMutater() {
855      return hasMutaterFrom(Reader.class);
856   }
857
858   /**
859    * Returns <jk>true</jk> if this class has a transform associated with it that allows it to be created from a String.
860    *
861    * @return <jk>true</jk> if this class has a transform associated with it that allows it to be created from a String.
862    */
863   public boolean hasStringMutater() {
864      return nn(stringMutater);
865   }
866
867   /**
868    * Returns <jk>true</jk> if this metadata represents an array of argument types.
869    *
870    * @return <jk>true</jk> if this metadata represents an array of argument types.
871    */
872   public boolean isArgs() { return cat.is(ARGS); }
873
874   /**
875    * Returns <jk>true</jk> if this class is a bean.
876    *
877    * @return <jk>true</jk> if this class is a bean.
878    */
879   public boolean isBean() { return nn(getBeanMeta()); }
880
881   /**
882    * Returns <jk>true</jk> if this class is a subclass of {@link BeanMap}.
883    *
884    * @return <jk>true</jk> if this class is a subclass of {@link BeanMap}.
885    */
886   public boolean isBeanMap() { return cat.is(BEANMAP); }
887
888   /**
889    * Returns <jk>true</jk> if this class is a {@link Boolean}.
890    *
891    * @return <jk>true</jk> if this class is a {@link Boolean}.
892    */
893   public boolean isBoolean() { return isAny(boolean.class, Boolean.class); }
894
895   /**
896    * Returns <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>.
897    *
898    * @return <jk>true</jk> if this class is <code><jk>byte</jk>[]</code>.
899    */
900   public boolean isByteArray() { return is(byte[].class); }
901
902   /**
903    * Returns <jk>true</jk> if this class is a {@link Calendar}.
904    *
905    * @return <jk>true</jk> if this class is a {@link Calendar}.
906    */
907   public boolean isCalendar() { return cat.is(CALENDAR); }
908
909   /**
910    * Returns <jk>true</jk> if this class is a {@link Character}.
911    *
912    * @return <jk>true</jk> if this class is a {@link Character}.
913    */
914   public boolean isChar() { return isAny(char.class, Character.class); }
915
916   /**
917    * Returns <jk>true</jk> if this class is a subclass of {@link CharSequence}.
918    *
919    * @return <jk>true</jk> if this class is a subclass of {@link CharSequence}.
920    */
921   public boolean isCharSequence() { return cat.is(CHARSEQ); }
922
923   /**
924    * Returns <jk>true</jk> if this class is a subclass of {@link Collection}.
925    *
926    * @return <jk>true</jk> if this class is a subclass of {@link Collection}.
927    */
928   public boolean isCollection() { return cat != null && cat.is(COLLECTION); }
929
930   /**
931    * Returns <jk>true</jk> if this class is a subclass of {@link Collection} or is an array or {@link Optional}.
932    *
933    * @return <jk>true</jk> if this class is a subclass of {@link Collection} or is an array or {@link Optional}.
934    */
935   public boolean isCollectionOrArrayOrOptional() { return cat.is(ARRAY) || is(Optional.class) || cat.is(COLLECTION); }
936
937   /**
938    * Returns <jk>true</jk> if this class is a {@link Date}.
939    *
940    * @return <jk>true</jk> if this class is a {@link Date}.
941    */
942   public boolean isDate() { return cat.is(DATE); }
943
944   /**
945    * Returns <jk>true</jk> if this class is a {@link Date} or {@link Calendar}.
946    *
947    * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar}.
948    */
949   public boolean isDateOrCalendar() { return cat.is(DATE) || cat.is(CALENDAR); }
950
951   /**
952    * Returns <jk>true</jk> if this class is a {@link Date} or {@link Calendar} or {@link Temporal}.
953    *
954    * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar} or {@link Temporal}.
955    */
956   public boolean isDateOrCalendarOrTemporal() { return cat.is(DATE) || cat.is(CALENDAR) || cat.is(TEMPORAL); }
957
958   /**
959    * Returns <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}.
960    *
961    * @return <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}.
962    */
963   public boolean isDecimal() { return cat.is(DECIMAL); }
964
965   /**
966    * Returns <jk>true</jk> if this class implements {@link Delegate}, meaning it's a representation of some other
967    * object.
968    *
969    * @return <jk>true</jk> if this class implements {@link Delegate}.
970    */
971   public boolean isDelegate() { return cat.is(DELEGATE); }
972
973   /**
974    * Returns <jk>true</jk> if this class is either {@link Double} or <jk>double</jk>.
975    *
976    * @return <jk>true</jk> if this class is either {@link Double} or <jk>double</jk>.
977    */
978   public boolean isDouble() { return isAny(Double.class, double.class); }
979
980   /**
981    * Returns <jk>true</jk> if this class is either {@link Float} or <jk>float</jk>.
982    *
983    * @return <jk>true</jk> if this class is either {@link Float} or <jk>float</jk>.
984    */
985   public boolean isFloat() { return isAny(Float.class, float.class); }
986
987   /**
988    * Returns <jk>true</jk> if this class is an {@link InputStream}.
989    *
990    * @return <jk>true</jk> if this class is an {@link InputStream}.
991    */
992   public boolean isInputStream() { return cat.is(INPUTSTREAM); }
993
994   /**
995    * Returns <jk>true</jk> if this class is either {@link Integer} or <jk>int</jk>.
996    *
997    * @return <jk>true</jk> if this class is either {@link Integer} or <jk>int</jk>.
998    */
999   public boolean isInteger() { return isAny(Integer.class, int.class); }
1000
1001   /**
1002    * Returns <jk>true</jk> if this class extends from {@link List}.
1003    *
1004    * @return <jk>true</jk> if this class extends from {@link List}.
1005    */
1006   public boolean isList() { return cat.is(LIST); }
1007
1008   /**
1009    * Returns <jk>true</jk> if this class is either {@link Long} or <jk>long</jk>.
1010    *
1011    * @return <jk>true</jk> if this class is either {@link Long} or <jk>long</jk>.
1012    */
1013   public boolean isLong() { return isAny(Long.class, long.class); }
1014
1015   /**
1016    * Returns <jk>true</jk> if this class is a subclass of {@link Map}.
1017    *
1018    * @return <jk>true</jk> if this class is a subclass of {@link Map}.
1019    */
1020   public boolean isMap() {
1021      // TODO - Figure out how cat can be null.
1022      return cat != null && cat.is(MAP);
1023   }
1024
1025   /**
1026    * Returns <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean.
1027    *
1028    * @return <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean.
1029    */
1030   public boolean isMapOrBean() { return cat.is(MAP) || nn(getBeanMeta()); }
1031
1032   /**
1033    * Returns <jk>true</jk> if this class is {@link Method}.
1034    *
1035    * @return <jk>true</jk> if this class is {@link Method}.
1036    */
1037   public boolean isMethod() { return is(Method.class); }
1038
1039   /**
1040    * Returns <jk>true</jk> if instance of this object can be <jk>null</jk>.
1041    *
1042    * <p>
1043    * Objects can be <jk>null</jk>, but primitives cannot, except for chars which can be represented by
1044    * <code>(<jk>char</jk>)0</code>.
1045    *
1046    * @return <jk>true</jk> if instance of this class can be null.
1047    */
1048   public boolean isNullable() {
1049      if (isPrimitive())
1050         return is(char.class);
1051      return true;
1052   }
1053
1054   /**
1055    * Returns <jk>true</jk> if this class is a subclass of {@link Number}.
1056    *
1057    * @return <jk>true</jk> if this class is a subclass of {@link Number}.
1058    */
1059   public boolean isNumber() { return cat.is(NUMBER); }
1060
1061   /**
1062    * Returns <jk>true</jk> if this class is {@link Object}.
1063    *
1064    * @return <jk>true</jk> if this class is {@link Object}.
1065    */
1066   public boolean isObject() { return is(Object.class); }
1067
1068   /**
1069    * Returns <jk>true</jk> if this class is a subclass of {@link Optional}.
1070    *
1071    * @return <jk>true</jk> if this class is a subclass of {@link Optional}.
1072    */
1073   public boolean isOptional() { return is(Optional.class); }
1074
1075   /**
1076    * Returns <jk>true</jk> if this class is a {@link Reader}.
1077    *
1078    * @return <jk>true</jk> if this class is a {@link Reader}.
1079    */
1080   public boolean isReader() { return cat.is(READER); }
1081
1082   /**
1083    * Returns <jk>true</jk> if this class extends from {@link Set}.
1084    *
1085    * @return <jk>true</jk> if this class extends from {@link Set}.
1086    */
1087   public boolean isSet() { return cat.is(SET); }
1088
1089   /**
1090    * Returns <jk>true</jk> if this class is either {@link Short} or <jk>short</jk>.
1091    *
1092    * @return <jk>true</jk> if this class is either {@link Short} or <jk>short</jk>.
1093    */
1094   public boolean isShort() { return isAny(Short.class, short.class); }
1095
1096   /**
1097    * Returns <jk>true</jk> if this class is a {@link String}.
1098    *
1099    * @return <jk>true</jk> if this class is a {@link String}.
1100    */
1101   public boolean isString() { return is(String.class); }
1102
1103   /**
1104    * Returns <jk>true</jk> if this class is a {@link Temporal}.
1105    *
1106    * @return <jk>true</jk> if this class is a {@link Temporal}.
1107    */
1108   public boolean isTemporal() { return cat.is(TEMPORAL); }
1109
1110   /**
1111    * Returns <jk>true</jk> if this class is a {@link URI} or {@link URL}.
1112    *
1113    * @return <jk>true</jk> if this class is a {@link URI} or {@link URL}.
1114    */
1115   public boolean isUri() { return cat != null && cat.is(URI); }
1116
1117   /**
1118    * Transforms the specified object into an instance of this class.
1119    *
1120    * @param o The object to transform.
1121    * @return The transformed object.
1122    */
1123   @SuppressWarnings({ "unchecked", "rawtypes" })
1124   public T mutateFrom(Object o) {
1125      Mutater t = getFromMutater(o.getClass());
1126      return (T)(t == null ? null : t.mutate(o));
1127   }
1128
1129   /**
1130    * Transforms the specified object into an instance of this class.
1131    *
1132    * @param <O> The transform-to class.
1133    * @param o The object to transform.
1134    * @param c The class
1135    * @return The transformed object.
1136    */
1137   @SuppressWarnings({ "unchecked" })
1138   public <O> O mutateTo(Object o, Class<O> c) {
1139      Mutater<Object,O> t = (Mutater<Object,O>)getToMutater(c);
1140      return t == null ? null : t.mutate(o);
1141   }
1142
1143   /**
1144    * Transforms the specified object into an instance of this class.
1145    *
1146    * @param <O> The transform-to class.
1147    * @param o The object to transform.
1148    * @param c The class
1149    * @return The transformed object.
1150    */
1151   public <O> O mutateTo(Object o, ClassMeta<O> c) {
1152      return mutateTo(o, c.inner());
1153   }
1154
1155   /**
1156    * Create a new instance of the main class of this declared type.
1157    *
1158    * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
1159    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
1160    */
1161   @Override
1162   @SuppressWarnings("unchecked")
1163   public T newInstance() throws ExecutableException {
1164      if (super.isArray())
1165         return (T)Array.newInstance(inner().getComponentType(), 0);
1166      if (noArgConstructor.isPresent())
1167         return noArgConstructor.get().newInstance();
1168      var h = getProxyInvocationHandler();
1169      if (nn(h))
1170         return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), a(inner(), java.io.Serializable.class), h);
1171      return null;
1172   }
1173
1174   /**
1175    * Same as {@link #newInstance()} except for instantiating non-static member classes.
1176    *
1177    * @param outer
1178    *    The instance of the owning object of the member class instance.
1179    *    Can be <jk>null</jk> if instantiating a non-member or static class.
1180    * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
1181    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
1182    */
1183   public T newInstance(Object outer) throws ExecutableException {
1184      if (isMemberClass() && isNotStatic() && noArgConstructor.isPresent())
1185         return noArgConstructor.get().<T>newInstance(outer);
1186      return newInstance();
1187   }
1188
1189   /**
1190    * Create a new instance of the main class of this declared type from a <c>String</c> input.
1191    *
1192    * <p>
1193    * In order to use this method, the class must have one of the following methods:
1194    * <ul>
1195    *    <li><code><jk>public static</jk> T valueOf(String in);</code>
1196    *    <li><code><jk>public static</jk> T fromString(String in);</code>
1197    *    <li><code><jk>public</jk> T(String in);</code>
1198    * </ul>
1199    *
1200    * @param outer
1201    *    The outer class object for non-static member classes.  Can be <jk>null</jk> for non-member or static classes.
1202    * @param arg The input argument value.
1203    * @return A new instance of the object, or <jk>null</jk> if there is no string constructor on the object.
1204    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
1205    */
1206   @SuppressWarnings({ "unchecked" })
1207   public T newInstanceFromString(Object outer, String arg) throws ExecutableException {
1208
1209      if (isEnum()) {
1210         var t = (T)enumValues.get().getKey(arg);
1211         if (t == null && ! beanContext.isIgnoreUnknownEnumValues())
1212            throw new ExecutableException("Could not resolve enum value ''{0}'' on class ''{1}''", arg, cn(inner()));
1213         return t;
1214      }
1215
1216      if (fromStringMethod.isPresent())
1217         return (T)fromStringMethod.get().invoke(null, arg);
1218
1219      if (stringConstructor.isPresent()) {
1220         if (isMemberClass() && isNotStatic())
1221            return stringConstructor.get().<T>newInstance(outer, arg);
1222         return stringConstructor.get().<T>newInstance(arg);
1223      }
1224      throw new ExecutableException("No string constructor or valueOf(String) method found for class '" + cn(inner()) + "'");
1225   }
1226
1227   /**
1228    * Similar to {@link #equals(Object)} except primitive and Object types that are similar are considered the same.
1229    * (e.g. <jk>boolean</jk> == <c>Boolean</c>).
1230    *
1231    * @param cm The class meta to compare to.
1232    * @return <jk>true</jk> if the specified class-meta is equivalent to this one.
1233    */
1234   public boolean same(ClassMeta<?> cm) {
1235      if (equals(cm))
1236         return true;
1237      return (isPrimitive() && cat.same(cm.cat));
1238   }
1239
1240   @Override /* Overridden from Object */
1241   public String toString() {
1242      return toString(false);
1243   }
1244
1245   /**
1246    * Same as {@link #toString()} except use simple class names.
1247    *
1248    * @param simple Print simple class names only (no package).
1249    * @return A new string.
1250    */
1251   public String toString(boolean simple) {
1252      return toString(new StringBuilder(), simple).toString();
1253   }
1254
1255   /**
1256    * Converts the specified object to a string.
1257    *
1258    * @param t The object to convert.
1259    * @return The object converted to a string, or <jk>null</jk> if the object was null.
1260    */
1261   public String toString(Object t) {
1262      if (t == null)
1263         return null;
1264      if (isEnum() && beanContext.isUseEnumNames())
1265         return ((Enum<?>)t).name();
1266      return t.toString();
1267   }
1268
1269   @SuppressWarnings("unchecked")
1270   private ObjectSwap<T,?> createSwap(Swap s) {
1271      var c = s.value();
1272      if (ClassUtils.isVoid(c))
1273         c = s.impl();
1274      var ci = info(c);
1275
1276      if (ci.isChildOf(ObjectSwap.class)) {
1277         var ps = BeanCreator.of(ObjectSwap.class).type(ci).run();
1278         if (s.mediaTypes().length > 0)
1279            ps.forMediaTypes(MediaType.ofAll(s.mediaTypes()));
1280         if (! s.template().isEmpty())
1281            ps.withTemplate(s.template());
1282         return ps;
1283      }
1284
1285      if (ci.isChildOf(Surrogate.class)) {
1286         List<SurrogateSwap<?,?>> l = SurrogateSwap.findObjectSwaps(c, beanContext);
1287         if (! l.isEmpty())
1288            return (ObjectSwap<T,?>)l.iterator().next();
1289      }
1290
1291      throw new ClassMetaRuntimeException(c, "Invalid swap class ''{0}'' specified.  Must extend from ObjectSwap or Surrogate.", c);
1292   }
1293
1294   private String findBeanDictionaryName() {
1295      if (beanContext == null)
1296         return null;
1297
1298      var d = beanMeta.get().optBeanMeta().map(x -> x.getDictionaryName()).orElse(null);
1299      if (nn(d))
1300         return d;
1301
1302      // Note that @Bean(typeName) can be defined on non-bean types, so
1303      // we have to check again.
1304      return beanContext.getAnnotationProvider().find(Bean.class, this)
1305         .stream()
1306         .map(AnnotationInfo::inner)
1307         .filter(x -> ! x.typeName().isEmpty())
1308         .map(x -> x.typeName())
1309         .findFirst()
1310         .orElse(null);
1311   }
1312
1313   private BeanMeta.BeanMetaValue<T> findBeanMeta() {
1314      if (! cat.isUnknown())
1315         return new BeanMeta.BeanMetaValue<>(null, "Known non-bean type");
1316      return BeanMeta.create(this, implClass.get());
1317   }
1318
1319   private KeyValueTypes findKeyValueTypes() {
1320      if (cat.is(MAP) && ! cat.is(BEANMAP)) {
1321         // If this is a MAP, see if it's parameterized (e.g. AddressBook extends HashMap<String,Person>)
1322         var parameters = beanContext.findParameters(inner(), inner());
1323         if (nn(parameters) && parameters.length == 2) {
1324            return new KeyValueTypes(parameters[0], parameters[1]);
1325         }
1326         return new KeyValueTypes(beanContext.getClassMeta(Object.class), beanContext.getClassMeta(Object.class));
1327      }
1328      return new KeyValueTypes(null, null);
1329   }
1330
1331   private ClassMeta<?> findElementType() {
1332      if (beanContext == null)
1333         return null;
1334      if (cat.is(ARRAY)) {
1335         return beanContext.getClassMeta(inner().getComponentType());
1336      } else if (cat.is(COLLECTION) || is(Optional.class)) {
1337         // If this is a COLLECTION, see if it's parameterized (e.g. AddressBook extends LinkedList<Person>)
1338         var parameters = beanContext.findParameters(inner(), inner());
1339         if (nn(parameters) && parameters.length == 1) {
1340            return parameters[0];
1341         }
1342         return beanContext.getClassMeta(Object.class);
1343      }
1344      return null;
1345   }
1346
1347   @SuppressWarnings("unchecked")
1348   private BuilderSwap<T,?> findBuilderSwap() {
1349      var bc = beanContext;
1350      if (bc == null)
1351         return null;
1352      return (BuilderSwap<T,?>)BuilderSwap.findSwapFromObjectClass(bc, inner(), bc.getBeanConstructorVisibility(), bc.getBeanMethodVisibility());
1353   }
1354
1355   @SuppressWarnings("unchecked")
1356   private List<ObjectSwap<T,?>> findSwaps() {
1357      if (beanContext == null)
1358         return l();
1359
1360      var list = new ArrayList<ObjectSwap<T,?>>();
1361      var swapArray = beanContext.getSwaps();
1362      if (! swapArray.isEmpty()) {
1363         var innerClass = inner();
1364         for (var f : swapArray)
1365            if (f.getNormalClass().isParentOf(innerClass))
1366               list.add((ObjectSwap<T,?>)f);
1367      }
1368
1369      var ap = beanContext.getAnnotationProvider();
1370      ap.find(Swap.class, this).stream().map(AnnotationInfo::inner).forEach(x -> list.add(createSwap(x)));
1371      var ds = DefaultSwaps.find(this);
1372      if (ds == null)
1373         ds = AutoObjectSwap.find(beanContext, this);
1374      if (ds == null)
1375         ds = AutoNumberSwap.find(beanContext, this);
1376      if (ds == null)
1377         ds = AutoMapSwap.find(beanContext, this);
1378      if (ds == null)
1379         ds = AutoListSwap.find(beanContext, this);
1380
1381      if (nn(ds))
1382         list.add((ObjectSwap<T,?>)ds);
1383
1384      return u(list);
1385   }
1386
1387   private List<ObjectSwap<?,?>> findChildSwaps() {
1388      if (beanContext == null)
1389         return l();
1390      var swapArray = beanContext.getSwaps();
1391      if (swapArray.isEmpty())
1392         return l();
1393      var list = new ArrayList<ObjectSwap<?,?>>();
1394      var innerClass = inner();
1395      for (var f : swapArray)
1396         if (f.getNormalClass().isChildOf(innerClass))
1397            list.add(f);
1398      return u(list);
1399   }
1400
1401   private BidiMap<Object,String> findEnumValues() {
1402      if (! isEnum())
1403         return null;
1404
1405      var bc = beanContext;
1406      var useEnumNames = nn(bc) && bc.isUseEnumNames();
1407
1408      var m = BidiMap.<Object,String>create().unmodifiable();
1409      var c = inner().asSubclass(Enum.class);
1410      stream(c.getEnumConstants()).forEach(x -> m.add(x, useEnumNames ? x.name() : x.toString()));
1411      return m.build();
1412   }
1413
1414   @SuppressWarnings("unchecked")
1415   private String findExample() {
1416
1417      var example = beanMeta.get().optBeanMeta().map(x -> x.getBeanFilter()).map(x -> x.getExample()).orElse(null);
1418
1419      if (example == null)
1420         example = marshalledFilter.map(x -> x.getExample()).orElse(null);
1421
1422      if (example == null && nn(beanContext))
1423         example = beanContext.getAnnotationProvider().find(Example.class, this).stream().map(x -> x.inner().value()).filter(Utils::ne).findFirst().orElse(null);
1424
1425      if (example == null) {
1426         if (isAny(boolean.class, Boolean.class)) {
1427            example = "true";
1428         } else if (isAny(char.class, Character.class)) {
1429            example = "a";
1430         } else if (cat.is(CHARSEQ)) {
1431            example = "foo";
1432         } else if (cat.is(ENUM)) {
1433            Iterator<? extends Enum<?>> i = EnumSet.allOf(inner().asSubclass(Enum.class)).iterator();
1434            example = i.hasNext() ? (beanContext.isUseEnumNames() ? i.next().name() : i.next().toString()) : null;
1435         } else if (isAny(float.class, Float.class, double.class, Double.class)) {
1436            example = "1.0";
1437         } else if (isAny(short.class, Short.class, int.class, Integer.class, long.class, Long.class)) {
1438            example = "1";
1439         }
1440      }
1441
1442      return example;
1443   }
1444
1445   private FieldInfo findExampleField() {
1446      var ap = beanContext.getAnnotationProvider();
1447
1448      return getDeclaredFields()
1449         .stream()
1450         .filter(x -> x.isStatic() && isParentOf(x.getFieldType()) && ap.has(Example.class, x))
1451         .map(x -> x.accessible())
1452         .findFirst()
1453         .orElse(null);
1454   }
1455
1456   private MethodInfo findExampleMethod() {
1457      // @formatter:off
1458      var ap = beanContext.getAnnotationProvider();
1459
1460      // Option 1:  Public example() or @Example method.
1461      var m = getPublicMethod(
1462         x -> x.isStatic() && x.isNotDeprecated() && (x.hasName("example") || ap.has(Example.class, x)) && x.hasParameterTypesLenient(BeanSession.class)
1463      );
1464      if (m.isPresent()) return m.get();
1465
1466      // Option 2:  Non-public @Example method.
1467      return getDeclaredMethods()
1468         .stream()
1469         .flatMap(x -> x.getMatchingMethods().stream())
1470         .filter(x -> x.isStatic() && ap.has(Example.class, x))
1471         .map(x -> x.accessible())
1472         .findFirst()
1473         .orElse(null);
1474      // @formatter:on
1475   }
1476
1477   private MethodInfo findFromStringMethod() {
1478      // Find static fromString(String) or equivalent method.
1479      // fromString() must be checked before valueOf() so that Enum classes can create their own
1480      //    specialized fromString() methods to override the behavior of Enum.valueOf(String).
1481      // valueOf() is used by enums.
1482      // parse() is used by the java logging Level class.
1483      // forName() is used by Class and Charset
1484      // @formatter:off
1485      var names = a("fromString", "fromValue", "valueOf", "parse", "parseString", "forName", "forString");
1486      return getPublicMethod(
1487         x -> x.isStatic() && x.isNotDeprecated() && x.hasReturnType(this) && x.hasParameterTypes(String.class) && contains(x.getName(), names)
1488      ).orElse(null);
1489      // @formatter:on
1490   }
1491
1492   @SuppressWarnings("unchecked")
1493   private ClassInfoTyped<? extends T> findImplClass() {
1494
1495      if (is(Object.class))
1496         return null;
1497
1498      var v = beanContext.getAnnotationProvider().find(Bean.class, this).stream().map(x -> x.inner()).filter(x -> neq(x.implClass(), void.class)).map(x -> ClassInfo.of(x)).findFirst().orElse(null);
1499
1500      if (v == null)
1501         v = marshalledFilter.map(x -> x.getImplClass()).map(ReflectionUtils::info).orElse(null);
1502
1503      return (ClassInfoTyped<? extends T>)v;
1504   }
1505
1506   private MarshalledFilter findMarshalledFilter() {
1507      var ap = beanContext.getAnnotationProvider();
1508      var l = ap.find(Marshalled.class, this);
1509      if (l.isEmpty())
1510         return null;
1511      return MarshalledFilter.create(inner()).applyAnnotations(reverse(l.stream().map(AnnotationInfo::inner).toList())).build();
1512   }
1513
1514   private Property<T,Object> findNameProperty() {
1515      var ap = beanContext.getAnnotationProvider();
1516
1517      var s = getAllFields()
1518         .stream()
1519         .filter(x -> ap.has(NameProperty.class, x))
1520         .map(x -> x.accessible())
1521         .map(x -> Property.<T,Object>create().field(x).build())
1522         .findFirst();
1523
1524      if (s.isPresent()) return s.get();
1525
1526      var builder = Property.<T,Object>create();
1527
1528      // Look for setter method (1 parameter) with @NameProperty
1529      var setterMethod = getAllMethods()
1530         .stream()
1531         .filter(x -> ap.has(NameProperty.class, x) && x.hasNumParameters(1))
1532         .findFirst();
1533
1534      if (setterMethod.isPresent()) {
1535         builder.setter(setterMethod.get().accessible());
1536
1537         // Try to find a corresponding getter method (even if not annotated)
1538         // If setter is "setName", look for "getName" or "isName"
1539         var setterName = setterMethod.get().getSimpleName();
1540         if (setterName.startsWith("set") && setterName.length() > 3) {
1541            var propertyName = setterName.substring(3);
1542            var getterName1 = "get" + propertyName;
1543            var getterName2 = "is" + propertyName;
1544
1545            var getter = getAllMethods()
1546               .stream()
1547               .filter(x -> !x.isStatic() && x.hasNumParameters(0) &&
1548                  (x.hasName(getterName1) || x.hasName(getterName2)) &&
1549                  !x.getReturnType().is(Void.TYPE))
1550               .findFirst();
1551
1552            if (getter.isPresent()) {
1553               builder.getter(getter.get().accessible());
1554            } else {
1555               // Try to find a field with the property name (lowercase first letter)
1556               var fieldName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
1557               var field = getAllFields()
1558                  .stream()
1559                  .filter(x -> !x.isStatic() && x.hasName(fieldName))
1560                  .findFirst();
1561
1562               if (field.isPresent()) {
1563                  var f = field.get().accessible();
1564                  builder.getter(obj -> f.get(obj));
1565               }
1566            }
1567         }
1568      }
1569
1570      // Look for getter method (0 parameters, non-void return) with @NameProperty
1571      var getterMethod = getAllMethods()
1572         .stream()
1573         .filter(x -> ap.has(NameProperty.class, x) && x.hasNumParameters(0) && !x.getReturnType().is(Void.TYPE))
1574         .findFirst();
1575
1576      if (getterMethod.isPresent()) {
1577         builder.getter(getterMethod.get().accessible());
1578      }
1579
1580      // Return null if neither setter nor getter was found
1581      if (setterMethod.isEmpty() && getterMethod.isEmpty())
1582         return null;
1583
1584      return builder.build();
1585   }
1586
1587   private ConstructorInfo findNoArgConstructor() {
1588
1589      if (is(Object.class))
1590         return null;
1591
1592      if (implClass.isPresent())
1593         return implClass.get().getPublicConstructor(x -> x.hasNumParameters(0)).orElse(null);
1594
1595      if (isAbstract())
1596         return null;
1597
1598      var numParams = isMemberClass() && isNotStatic() ? 1 : 0;
1599      return getPublicConstructors()
1600         .stream()
1601         .filter(x -> x.isPublic() && x.isNotDeprecated() && x.hasNumParameters(numParams))
1602         .findFirst()
1603         .orElse(null);
1604   }
1605
1606   private Property<T,Object> findParentProperty() {
1607      var ap = beanContext.getAnnotationProvider();
1608
1609      var s = getAllFields()
1610         .stream()
1611         .filter(x -> ap.has(ParentProperty.class, x))
1612         .map(x -> x.accessible())
1613         .map(x -> Property.<T,Object>create().field(x).build())
1614         .findFirst();
1615
1616      if (s.isPresent()) return s.get();
1617
1618      var builder = Property.<T,Object>create();
1619
1620      // Look for setter method (1 parameter) with @ParentProperty
1621      var setterMethod = getAllMethods()
1622         .stream()
1623         .filter(x -> ap.has(ParentProperty.class, x) && x.hasNumParameters(1))
1624         .findFirst();
1625
1626      if (setterMethod.isPresent()) {
1627         builder.setter(setterMethod.get().accessible());
1628
1629         // Try to find a corresponding getter method (even if not annotated)
1630         // If setter is "setParent", look for "getParent" or "isParent"
1631         var setterName = setterMethod.get().getSimpleName();
1632         if (setterName.startsWith("set") && setterName.length() > 3) {
1633            var propertyName = setterName.substring(3);
1634            var getterName1 = "get" + propertyName;
1635            var getterName2 = "is" + propertyName;
1636
1637            var getter = getAllMethods()
1638               .stream()
1639               .filter(x -> !x.isStatic() && x.hasNumParameters(0) &&
1640                  (x.hasName(getterName1) || x.hasName(getterName2)) &&
1641                  !x.getReturnType().is(Void.TYPE))
1642               .findFirst();
1643
1644            if (getter.isPresent()) {
1645               builder.getter(getter.get().accessible());
1646            } else {
1647               // Try to find a field with the property name (lowercase first letter)
1648               var fieldName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
1649               var field = getAllFields()
1650                  .stream()
1651                  .filter(x -> !x.isStatic() && x.hasName(fieldName))
1652                  .findFirst();
1653
1654               if (field.isPresent()) {
1655                  var f = field.get().accessible();
1656                  builder.getter(obj -> f.get(obj));
1657               }
1658            }
1659         }
1660      }
1661
1662      // Look for getter method (0 parameters, non-void return) with @ParentProperty
1663      var getterMethod = getAllMethods()
1664         .stream()
1665         .filter(x -> ap.has(ParentProperty.class, x) && x.hasNumParameters(0) && !x.getReturnType().is(Void.TYPE))
1666         .findFirst();
1667
1668      if (getterMethod.isPresent()) {
1669         builder.getter(getterMethod.get().accessible());
1670      }
1671
1672      // Return null if neither setter nor getter was found
1673      if (setterMethod.isEmpty() && getterMethod.isEmpty())
1674         return null;
1675
1676      return builder.build();
1677   }
1678
1679   private ConstructorInfo findStringConstructor() {
1680
1681      if (is(Object.class) || isAbstract())
1682         return null;
1683
1684      if (implClass.isPresent())
1685         return implClass.get().getPublicConstructor(x -> x.hasParameterTypes(String.class)).orElse(null);
1686
1687      if (isAbstract())
1688         return null;
1689
1690      var numParams = isMemberClass() && isNotStatic() ? 2 : 1;
1691      return getPublicConstructors()
1692         .stream()
1693         .filter(x -> x.isPublic() && x.isNotDeprecated() && x.hasNumParameters(numParams))
1694         .filter(x -> x.getParameter(numParams == 2 ? 1 : 0).isType(String.class))
1695         .findFirst()
1696         .orElse(null);
1697   }
1698
1699   /**
1700    * Returns the {@link ObjectSwap} where the specified class is the same/subclass of the normal class of one of the
1701    * child POJO swaps associated with this class.
1702    *
1703    * @param normalClass The normal class being resolved.
1704    * @return The resolved {@link ObjectSwap} or <jk>null</jk> if none were found.
1705    */
1706   protected ObjectSwap<?,?> getChildObjectSwapForSwap(Class<?> normalClass) {
1707      return childSwapMap.get(normalClass);
1708   }
1709
1710   /**
1711    * Returns the {@link ObjectSwap} where the specified class is the same/subclass of the swap class of one of the child
1712    * POJO swaps associated with this class.
1713    *
1714    * @param swapClass The swap class being resolved.
1715    * @return The resolved {@link ObjectSwap} or <jk>null</jk> if none were found.
1716    */
1717   protected ObjectSwap<?,?> getChildObjectSwapForUnswap(Class<?> swapClass) {
1718      return childUnswapMap.get(swapClass);
1719   }
1720
1721   /**
1722    * Returns <jk>true</jk> if this class or any child classes has a {@link ObjectSwap} associated with it.
1723    *
1724    * <p>
1725    * Used when transforming bean properties to prevent having to look up transforms if we know for certain that no
1726    * transforms are associated with a bean property.
1727    *
1728    * @return <jk>true</jk> if this class or any child classes has a {@link ObjectSwap} associated with it.
1729    */
1730   protected boolean hasChildSwaps() {
1731      return ! childSwaps.get().isEmpty();
1732   }
1733
1734   /**
1735    * Appends this object as a readable string to the specified string builder.
1736    *
1737    * @param sb The string builder to append this object to.
1738    * @param simple Print simple class names only (no package).
1739    * @return The passed-in string builder.
1740    */
1741   protected StringBuilder toString(StringBuilder sb, boolean simple) {
1742      var n = simple ? cnsq(inner()) : cn(inner());
1743      if (cat.is(ARRAY))
1744         return elementType.get().toString(sb, simple).append('[').append(']');
1745      if (cat.is(BEANMAP))
1746         return sb.append(cn(BeanMap.class)).append('<').append(n).append('>');
1747      if (cat.is(MAP)) {
1748         var kvTypes = keyValueTypes.get();
1749         var kt = kvTypes.optKeyType();
1750         var vt = kvTypes.optValueType();
1751         if (kt.isPresent() && vt.isPresent() && kt.get().isObject() && vt.get().isObject())
1752            return sb.append(n);
1753         return sb.append(n).append('<').append(kt.map(x -> x.toString(simple)).orElse("?")).append(',').append(vt.map(x -> x.toString(simple)).orElse("?")).append('>');
1754      }
1755      if (cat.is(COLLECTION) || is(Optional.class)) {
1756         var et = elementType.get();
1757         return sb.append(n).append(et != null && et.isObject() ? "" : "<" + (et == null ? "?" : et.toString(simple)) + ">");
1758      }
1759      return sb.append(n);
1760   }
1761}