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<String> <jv>computedValue</jv> = classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -> { 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<String> <jv>cached</jv> = classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -> { 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}