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