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.utils.ClassUtils.*; 020import static org.apache.juneau.commons.utils.CollectionUtils.*; 021import static org.apache.juneau.commons.utils.Shorts.*; 022 023import java.io.*; 024import java.lang.reflect.*; 025import java.time.*; 026import java.util.*; 027import java.util.logging.*; 028 029import org.apache.juneau.commons.bean.*; 030import org.apache.juneau.commons.collections.*; 031import org.apache.juneau.commons.conversion.*; 032import org.apache.juneau.commons.http.*; 033import org.apache.juneau.commons.lang.*; 034import org.apache.juneau.commons.reflect.*; 035import org.apache.juneau.marshall.collections.*; 036import org.apache.juneau.marshall.json.*; 037import org.apache.juneau.marshall.json5.*; 038import org.apache.juneau.marshall.parser.*; 039import org.apache.juneau.marshall.swap.spi.*; 040 041/** 042 * Session object that lives for the duration of a single use of {@link MarshallingContext}. 043 * 044 * <ul class='spaced-list'> 045 * <li class='info'>Typically session objects are not thread safe nor reusable. However, bean sessions do not maintain any state and 046 * thus can be safely cached and reused. 047 * </ul> 048 * 049 */ 050@SuppressWarnings({ 051 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 052 "java:S1452", // Wildcard required - ClassMeta<?>, ObjectSwap<?,?>, etc. for bean metadata 053 "rawtypes", // Raw types necessary for generic type handling 054 "unchecked" // Type erasure requires unchecked casts 055}) 056public class MarshallingSession extends ContextSession implements ConverterSession, BeanSession { 057 058 /** 059 * Builder class. 060 */ 061 @SuppressWarnings({ 062 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 063 }) 064 public abstract static class Builder<SELF extends Builder<SELF>> extends ContextSession.Builder<SELF> { 065 066 private final MarshallingContext ctx; 067 private Locale locale; 068 private MediaType mediaType; 069 private TimeZone timeZone; 070 private String activeView; 071 072 /** 073 * Constructor 074 * 075 * @param ctx The context creating this session. 076 * <br>Cannot be <jk>null</jk>. 077 */ 078 protected Builder(MarshallingContext ctx) { 079 super(reqnn("ctx", ctx)); 080 this.ctx = ctx; 081 mediaType = ctx.getMediaType(); 082 timeZone = ctx.getTimeZone(); 083 activeView = ctx.getActiveView(); 084 } 085 086 /** 087 * Build the object. 088 * 089 * @return The built object. 090 */ 091 @Override 092 public MarshallingSession build() { 093 return new MarshallingSession(this); 094 } 095 096 /** 097 * The session locale. 098 * 099 * <p> 100 * Specifies the default locale for serializer and parser sessions. 101 * 102 * <p> 103 * If not specified, defaults to {@link MarshallingContext.Builder#locale(Locale)}. 104 * 105 * <h5 class='section'>See Also:</h5><ul> 106 * <li class='ja'>{@link MarshalledConfig#locale()} 107 * <li class='jm'>{@link MarshallingContext.Builder#locale(Locale)} 108 * </ul> 109 * 110 * @param value 111 * The new value for this property. 112 * <br>If <jk>null</jk> defaults to {@link MarshallingContext#getLocale()} 113 * @return This object. 114 */ 115 public SELF locale(Locale value) { 116 locale = value; 117 return self(); 118 } 119 120 /** 121 * The session media type. 122 * 123 * <p> 124 * Specifies the default media type value for serializer and parser sessions. 125 * 126 * <p> 127 * If not specified, defaults to {@link MarshallingContext.Builder#mediaType(MediaType)}. 128 * 129 * <h5 class='section'>See Also:</h5><ul> 130 * <li class='ja'>{@link MarshalledConfig#mediaType()} 131 * <li class='jm'>{@link MarshallingContext.Builder#mediaType(MediaType)} 132 * </ul> 133 * 134 * @param value 135 * The new value for this property. 136 * <br>If <jk>null</jk> defaults to {@link MarshallingContext#getMediaType()}. 137 * @return This object. 138 */ 139 public SELF mediaType(MediaType value) { 140 mediaType = value; 141 return self(); 142 } 143 144 /** 145 * Same as {@link #mediaType(MediaType)} but doesn't overwrite the value if it is already set. 146 * 147 * @param value 148 * The new value for this property. 149 * <br>If <jk>null</jk>, then the media type defined on the context is used. 150 * @return This object. 151 */ 152 public SELF mediaTypeDefault(MediaType value) { 153 if (mediaType == null) 154 mediaType = value; 155 return self(); 156 } 157 158 /** 159 * Overrides the active view for this session. 160 * 161 * <p> 162 * When set, only bean properties whose declared {@link MarshalledProp#view()} set contains this view name 163 * will be included during serialization. On the parse side, out-of-view properties are routed through 164 * the existing unknown/ignored-property mechanism. 165 * 166 * <p> 167 * This is a per-call override. If not set, the value from 168 * {@link MarshallingContext.Builder#activeView(String)} is used. 169 * 170 * <h5 class='section'>See Also:</h5><ul> 171 * <li class='ja'>{@link MarshalledProp#view()} 172 * <li class='jm'>{@link MarshallingContext.Builder#activeView(String)} 173 * </ul> 174 * 175 * @param value The active view name. Use <jk>null</jk> to disable view filtering for this call. 176 * @return This object. 177 * @since 10.0.0 178 */ 179 public SELF activeView(String value) { 180 activeView = value; 181 return self(); 182 } 183 184 @Override /* Overridden from Builder */ 185 public SELF property(String key, Object value) { 186 if (key == null) { 187 super.property(key, value); // delegates null-key validation to base class 188 return self(); 189 } 190 switch (key) { 191 case "locale", "MarshallingSession.locale": 192 return locale(cvt(value, Locale.class)); 193 case "mediaType", "MarshallingSession.mediaType": 194 return mediaType(cvt(value, MediaType.class)); 195 case "timeZone", "MarshallingSession.timeZone": 196 return timeZone(cvt(value, TimeZone.class)); 197 case "activeView", "MarshallingSession.activeView": 198 return activeView(cvt(value, String.class)); 199 default: 200 super.property(key, value); 201 return self(); 202 } 203 } 204 205 /** 206 * The session timezone. 207 * 208 * <p> 209 * Specifies the default timezone for serializer and parser sessions. 210 * 211 * <p> 212 * If not specified, defaults to {@link MarshallingContext.Builder#timeZone(TimeZone)}. 213 * 214 * <h5 class='section'>See Also:</h5><ul> 215 * <li class='ja'>{@link MarshalledConfig#timeZone()} 216 * <li class='jm'>{@link MarshallingContext.Builder#timeZone(TimeZone)} 217 * </ul> 218 * 219 * @param value 220 * The new value for this property. 221 * <br>If <jk>null</jk> defaults to {@link MarshallingContext#getTimeZone()}. 222 * @return This object. 223 */ 224 public SELF timeZone(TimeZone value) { 225 timeZone = value; 226 return self(); 227 } 228 229 /** 230 * Same as {@link #timeZone(TimeZone)} but doesn't overwrite the value if it is already set. 231 * 232 * @param value 233 * The new value for this property. 234 * <br>If <jk>null</jk>, then the time zone defined on the context is used. 235 * @return This object. 236 */ 237 public SELF timeZoneDefault(TimeZone value) { 238 if (timeZone == null) 239 timeZone = value; 240 return self(); 241 } 242 243 } 244 245 /** 246 * Concrete default builder leaf for the non-subclassed {@code create()} path (CRTP terminal). 247 */ 248 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 249 250 DefaultBuilder(MarshallingContext ctx) { 251 super(ctx); 252 } 253 } 254 255 private static final Logger LOG = Logger.getLogger(cn(MarshallingSession.class)); 256 257 /** 258 * Creates a builder of this object. 259 * 260 * @param ctx The context creating this builder. 261 * <br>Cannot be <jk>null</jk>. 262 * @return A new builder. 263 */ 264 public static Builder<?> create(MarshallingContext ctx) { 265 return new DefaultBuilder(reqnn("ctx", ctx)); 266 } 267 268 /** 269 * The name property name. 270 * 271 * <p> 272 * Currently this always returns <js>"_name"</js>. 273 */ 274 public static final String NAME_PROPERTY_NAME = "_name"; 275 276 private final MarshallingContext ctx; 277 private final Locale locale; 278 private final MediaType mediaType; 279 private final TimeZone timeZone; 280 private final String activeView; 281 282 /** 283 * Constructor. 284 * 285 * @param builder The builder for this object. Cannot be <jk>null</jk>. 286 */ 287 protected MarshallingSession(Builder<?> builder) { 288 super(builder); 289 ctx = builder.ctx; 290 locale = o(builder.locale).orElse(ctx.getLocale()); 291 mediaType = o(builder.mediaType).orElse(builder.mediaType); 292 timeZone = o(builder.timeZone).orElse(builder.timeZone); 293 activeView = builder.activeView; 294 } 295 296 @Override /* ConverterSession */ 297 public <T> Optional<T> get(Class<T> c) { 298 if (c == TimeZone.class) return o(c.cast(timeZone)); 299 if (c == Locale.class) return o(c.cast(locale)); 300 if (c == MediaType.class) return o(c.cast(mediaType)); 301 return oe(); 302 } 303 304 /** 305 * Logs a warning message. 306 * 307 * @param msg The warning message. 308 * @param args Optional {@link String#format(String, Object...) String.format}-style arguments. 309 */ 310 @Override 311 public void addWarning(String msg, Object...args) { 312 if (isDebug()) 313 LOG.log(Level.WARNING, () -> args.length == 0 ? msg : f(msg, args)); 314 super.addWarning(msg, args); 315 } 316 317 /** 318 * Same as {@link #convertToType(Object, Class)}, except used for instantiating inner member classes that must 319 * be instantiated within another class instance. 320 * 321 * @param <T> The class type to convert the value to. 322 * @param outer 323 * If class is a member class, this is the instance of the containing class. 324 * Should be <jk>null</jk> if not a member class. 325 * @param value The value to convert. 326 * @param type The class type to convert the value to. 327 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type. 328 * @return The converted value. 329 */ 330 public final <T> T convertToMemberType(Object outer, Object value, Class<T> type) throws InvalidDataConversionException { 331 return convertToMemberType(outer, value, getClassMeta(type)); 332 } 333 334 /** 335 * Converts the specified value to the specified class type. 336 * 337 * <p> 338 * See {@link #convertToType(Object, ClassMeta)} for the list of valid conversions. 339 * 340 * @param <T> The class type to convert the value to. 341 * @param value The value to convert. Can be <jk>null</jk> (returns <jk>null</jk>). 342 * @param type The class type to convert the value to. 343 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type. 344 * @return The converted value. 345 */ 346 public final <T> T convertToType(Object value, Class<T> type) throws InvalidDataConversionException { 347 // Shortcut for most common case. 348 if (nn(value) && value.getClass() == type) 349 return (T)value; 350 return convertToMemberType(null, value, getClassMeta(type)); 351 } 352 353 /** 354 * Casts the specified value into the specified type. 355 * 356 * <p> 357 * If the value isn't an instance of the specified type, then converts the value if possible. 358 * 359 * <p> 360 * The following conversions are valid: 361 * <table class='styled'> 362 * <tr><th>Convert to type</th><th>Valid input value types</th><th>Notes</th></tr> 363 * <tr> 364 * <td> 365 * A class that is the normal type of a registered {@link ObjectSwap}. 366 * </td> 367 * <td> 368 * A value whose class matches the transformed type of that registered {@link ObjectSwap}. 369 * </td> 370 * <td> </td> 371 * </tr> 372 * <tr> 373 * <td> 374 * A class that is the transformed type of a registered {@link ObjectSwap}. 375 * </td> 376 * <td> 377 * A value whose class matches the normal type of that registered {@link ObjectSwap}. 378 * </td> 379 * <td> </td> 380 * </tr> 381 * <tr> 382 * <td> 383 * {@code Number} (e.g. {@code Integer}, {@code Short}, {@code Float},...) 384 * <br><code>Number.<jsf>TYPE</jsf></code> (e.g. <code>Integer.<jsf>TYPE</jsf></code>, 385 * <code>Short.<jsf>TYPE</jsf></code>, <code>Float.<jsf>TYPE</jsf></code>,...) 386 * </td> 387 * <td> 388 * {@code Number}, {@code String}, <jk>null</jk> 389 * </td> 390 * <td> 391 * For primitive {@code TYPES}, <jk>null</jk> returns the JVM default value for that type. 392 * </td> 393 * </tr> 394 * <tr> 395 * <td> 396 * {@code Map} (e.g. {@code Map}, {@code HashMap}, {@code TreeMap}, {@code JsonMap}) 397 * </td> 398 * <td> 399 * {@code Map} 400 * </td> 401 * <td> 402 * If {@code Map} is not constructible, an {@code JsonMap} is created. 403 * </td> 404 * </tr> 405 * <tr> 406 * <td> 407 * {@code Collection} (e.g. {@code List}, {@code LinkedList}, {@code HashSet}, {@code JsonList}) 408 * </td> 409 * <td> 410 * {@code Collection<Object>} 411 * <br>{@code Object[]} 412 * </td> 413 * <td> 414 * If {@code Collection} is not constructible, an {@code JsonList} is created. 415 * </td> 416 * </tr> 417 * <tr> 418 * <td> 419 * {@code X[]} (array of any type X) 420 * </td> 421 * <td> 422 * {@code List<X>} 423 * </td> 424 * <td> </td> 425 * </tr> 426 * <tr> 427 * <td> 428 * {@code X[][]} (multi-dimensional arrays) 429 * </td> 430 * <td> 431 * {@code List<List<X>>} 432 * <br>{@code List<X[]>} 433 * <br>{@code List[]<X>} 434 * </td> 435 * <td> </td> 436 * </tr> 437 * <tr> 438 * <td> 439 * {@code Enum} 440 * </td> 441 * <td> 442 * {@code String} 443 * </td> 444 * <td> </td> 445 * </tr> 446 * <tr> 447 * <td> 448 * Bean 449 * </td> 450 * <td> 451 * {@code Map} 452 * </td> 453 * <td> </td> 454 * </tr> 455 * <tr> 456 * <td> 457 * {@code String} 458 * </td> 459 * <td> 460 * Anything 461 * </td> 462 * <td> 463 * Arrays are converted to JSON arrays 464 * </td> 465 * </tr> 466 * <tr> 467 * <td> 468 * Anything with one of the following methods: 469 * <br><code><jk>public static</jk> T fromString(String)</code> 470 * <br><code><jk>public static</jk> T valueOf(String)</code> 471 * <br><code><jk>public</jk> T(String)</code> 472 * </td> 473 * <td> 474 * <c>String</c> 475 * </td> 476 * <td> 477 * <br> 478 * </td> 479 * </tr> 480 * </table> 481 * 482 * @param <T> The class type to convert the value to. 483 * @param value The value to be converted. Can be <jk>null</jk> (returns <jk>null</jk>). 484 * @param type The target object type. Can be <jk>null</jk> (treated as the {@code Object} type). 485 * @return The converted type. 486 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type. 487 */ 488 public final <T> T convertToType(Object value, ClassMeta<T> type) throws InvalidDataConversionException { 489 return convertToMemberType(null, value, type); 490 } 491 492 /** 493 * Same as {@link #convertToType(Object, Class)}, but allows for complex data types consisting of collections or maps. 494 * 495 * @param <T> The class type to convert the value to. 496 * @param value The value to be converted. 497 * @param type The target object type. 498 * @param args The target object parameter types. 499 * @return The converted type. 500 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type. 501 */ 502 public final <T> T convertToType(Object value, Type type, Type...args) throws InvalidDataConversionException { 503 return (T)convertToMemberType(null, value, getClassMeta(type, args)); 504 } 505 506 /** 507 * Returns the annotation provider for this session. 508 * 509 * @return The annotation provider for this session. 510 */ 511 public AnnotationProvider getAnnotationProvider() { 512 return ctx.getAnnotationProvider(); 513 } 514 515 /** 516 * Returns the {@link MarshallingContext} backing this session. 517 * 518 * @return The marshalling context. 519 */ 520 public final MarshallingContext getMarshallingContext() { 521 return ctx; 522 } 523 524 /** 525 * Minimum bean class visibility. 526 * 527 * @see MarshallingContext.Builder#beanClassVisibility(Visibility) 528 * @return 529 * Classes are not considered beans unless they meet the minimum visibility requirements. 530 */ 531 public final Visibility getBeanClassVisibility() { return ctx.getBeanClassVisibility(); } 532 533 /** 534 * Minimum bean constructor visibility. 535 * 536 * @see MarshallingContext.Builder#beanConstructorVisibility(Visibility) 537 * @return 538 * Only look for constructors with this specified minimum visibility. 539 */ 540 public final Visibility getBeanConstructorVisibility() { return ctx.getBeanConstructorVisibility(); } 541 542 /** 543 * Bean dictionary. 544 * 545 * @see MarshallingContext.Builder#beanDictionary(ClassInfo...) 546 * @return 547 * The list of classes that make up the bean dictionary in this bean context. 548 * <br>Never <jk>null</jk>. 549 * <br>List is unmodifiable. 550 */ 551 public final List<ClassInfo> getBeanDictionary() { return ctx.getBeanDictionary(); } 552 553 /** 554 * Minimum bean field visibility. 555 * 556 * 557 * @see MarshallingContext.Builder#beanFieldVisibility(Visibility) 558 * @return 559 * Only look for bean fields with this specified minimum visibility. 560 */ 561 public final Visibility getBeanFieldVisibility() { return ctx.getBeanFieldVisibility(); } 562 563 /** 564 * Returns the {@link BeanMeta} class for the specified class. 565 * 566 * @param <T> The class type to get the meta-data on. 567 * @param c The class to get the meta-data on. Can be <jk>null</jk> (returns <jk>null</jk>). 568 * @return 569 * The {@link BeanMeta} for the specified class, or <jk>null</jk> if the class 570 * is not a bean per the settings on this context. 571 */ 572 public final <T> BeanMeta<T> getBeanMeta(Class<T> c) { 573 if (c == null) 574 return null; 575 return getClassMeta(c).getBeanMeta(); 576 } 577 578 /** 579 * Minimum bean method visibility. 580 * 581 * @see MarshallingContext.Builder#beanMethodVisibility(Visibility) 582 * @return 583 * Only look for bean methods with this specified minimum visibility. 584 */ 585 public final Visibility getBeanMethodVisibility() { return ctx.getBeanMethodVisibility(); } 586 587 /** 588 * Returns the bean registry defined in this bean context defined by {@link MarshallingContext.Builder#beanDictionary(ClassInfo...)}. 589 * 590 * @return The bean registry defined in this bean context. Never <jk>null</jk>. 591 */ 592 public final BeanRegistry getBeanRegistry() { return ctx.getBeanRegistry(); } 593 594 /** 595 * Bean type property name. 596 * 597 * @see MarshallingContext.Builder#typePropertyName(String) 598 * @return 599 * The name of the bean property used to store the dictionary name of a bean type so that the parser knows the data type to reconstruct. 600 */ 601 public final String getBeanTypePropertyName() { return ctx.getBeanTypePropertyName(); } 602 603 /** 604 * Returns the type property name as defined by {@link MarshallingContext.Builder#typePropertyName(String)}. 605 * 606 * @param cm 607 * The class meta of the type we're trying to resolve the type name for. 608 * Can be <jk>null</jk> (falls back to the session-wide {@link MarshallingContext.Builder#typePropertyName(String) typePropertyName}). 609 * @return The type property name. Never <jk>null</jk>. 610 */ 611 public final String getBeanTypePropertyName(ClassMeta cm) { 612 if (cm == null) 613 return getBeanTypePropertyName(); 614 var beanMeta = cm.getBeanMeta(); 615 if (beanMeta == null) 616 return getBeanTypePropertyName(); 617 var s = beanMeta.getTypePropertyName(); 618 return s == null ? getBeanTypePropertyName() : s; 619 } 620 621 /** 622 * Returns a {@code ClassMeta} wrapper around a {@link Class} object. 623 * 624 * @param <T> The class type being wrapped. 625 * @param c The class being wrapped. 626 * @return The class meta object containing information about the class. 627 */ 628 public final <T> ClassMeta<T> getClassMeta(Class<T> c) { 629 return ctx.getClassMeta(c); 630 } 631 632 /** 633 * Used to resolve <c>ClassMetas</c> of type <c>Collection</c> and <c>Map</c> that have 634 * <c>ClassMeta</c> values that themselves could be collections or maps. 635 * 636 * <p> 637 * <c>Collection</c> meta objects are assumed to be followed by zero or one meta objects indicating the 638 * element type. 639 * 640 * <p> 641 * <c>Map</c> meta objects are assumed to be followed by zero or two meta objects indicating the key and value 642 * types. 643 * 644 * <p> 645 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 646 * 647 * <h5 class='section'>Examples:</h5> 648 * <ul> 649 * <li><code>getClassMeta(String.<jk>class</jk>);</code> - A normal type. 650 * <li><code>getClassMeta(List.<jk>class</jk>);</code> - A list containing objects. 651 * <li><code>getClassMeta(List.<jk>class</jk>, String.<jk>class</jk>);</code> - A list containing strings. 652 * <li><code>getClassMeta(LinkedList.<jk>class</jk>, String.<jk>class</jk>);</code> - A linked-list containing 653 * strings. 654 * <li><code>getClassMeta(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);</code> - 655 * A linked-list containing linked-lists of strings. 656 * <li><code>getClassMeta(Map.<jk>class</jk>);</code> - A map containing object keys/values. 657 * <li><code>getClassMeta(Map.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);</code> - A map 658 * containing string keys/values. 659 * <li><code>getClassMeta(Map.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);</code> - 660 * A map containing string keys and values of lists containing beans. 661 * </ul> 662 * 663 * @param <T> 664 * The class to resolve. 665 * @param type 666 * The class to resolve. 667 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 668 * @param args 669 * The type arguments of the class if it's a collection or map. 670 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 671 * <br>Ignored if the main type is not a map or collection. 672 * @return The class meta. 673 */ 674 public final <T> ClassMeta<T> getClassMeta(Type type, Type...args) { 675 return ctx.getClassMeta(type, args); 676 } 677 678 /** 679 * Returns the {@link ClassMeta} for the object's class, unwrapping recognized proxies first. 680 * 681 * <p> 682 * Delegates to {@link #getClassMetaForObject(Object,ClassMeta)} with a <jk>null</jk> default. 683 * 684 * @param <T> The class of the object being passed in. 685 * @param o The class to find the class type for. Can be <jk>null</jk> (returns <jk>null</jk>). 686 * @return The ClassMeta object, or <jk>null</jk> if {@code o} is <jk>null</jk>. 687 */ 688 public final <T> ClassMeta<T> getClassMetaForObject(T o) { 689 return (ClassMeta<T>)getClassMetaForObject(o, null); 690 } 691 692 /** 693 * Locale. 694 * 695 * <p> 696 * The locale is determined in the following order: 697 * <ol> 698 * <li><c>locale</c> parameter passed in through constructor. 699 * <li>{@link MarshallingContext.Builder#locale(Locale)} setting on bean context. 700 * <li>Locale returned by {@link Locale#getDefault()}. 701 * </ol> 702 * 703 * @see MarshallingContext.Builder#locale(Locale) 704 * @return The session locale. 705 */ 706 public Locale getLocale() { return locale; } 707 708 /** 709 * Active view. 710 * 711 * <p> 712 * Returns the active view name for this session. When non-<jk>null</jk>, only bean properties whose 713 * declared {@link MarshalledProp#view()} set contains this name are included during serialization, and 714 * out-of-view properties encountered during parsing are treated as unknown. 715 * 716 * <p> 717 * This is the per-call override value, which defaults to {@link MarshallingContext#getActiveView()}. 718 * 719 * @see MarshallingContext.Builder#activeView(String) 720 * @see MarshallingSession.Builder#activeView(String) 721 * @return The active view for this session, or <jk>null</jk> if view filtering is not active. 722 * @since 10.0.0 723 */ 724 public final String getActiveView() { return activeView; } 725 726 @Override /* BeanSession */ 727 public boolean isPropertyInActiveView(BeanPropertyMeta pMeta) { 728 if (activeView == null) 729 return true; 730 var views = pMeta.getViews(); 731 if (ie(views)) 732 return ctx.isDefaultViewInclusion(); 733 return views.contains(activeView); 734 } 735 736 /** 737 * Media type. 738 * 739 * <p> 740 * For example, <js>"application/json"</js>. 741 * 742 * @see MarshallingContext.Builder#mediaType(MediaType) 743 * @return The media type for this session, or <jk>null</jk> if not specified. 744 */ 745 public final MediaType getMediaType() { return mediaType; } 746 747 /** 748 * Bean class exclusions. 749 * 750 * @see MarshallingContext.Builder#notBeanClasses(ClassInfo...) 751 * @return 752 * The list of classes that are explicitly not beans. 753 * <br>Never <jk>null</jk>. 754 * <br>List is unmodifiable. 755 */ 756 public final List<ClassInfo> getNotBeanClasses() { return ctx.getNotBeanClasses(); } 757 758 /** 759 * Bean package exclusions. 760 * 761 * @see MarshallingContext.Builder#notBeanPackages(String...) 762 * @return 763 * The set of fully-qualified package names to exclude from being classified as beans. 764 * <br>Never <jk>null</jk>. 765 * <br>Set is unmodifiable. 766 */ 767 public final Set<String> getNotBeanPackagesNames() { return ctx.getNotBeanPackagesNames(); } 768 769 /** 770 * Bean property namer. 771 * 772 * @see MarshallingContext.Builder#propertyNamer(Class) 773 * @return 774 * The interface used to calculate bean property names. 775 */ 776 public final PropertyNamer getPropertyNamer() { return ctx.getPropertyNamer(); } 777 778 /** 779 * Session classloader. 780 * 781 * <p> 782 * Returns the classloader explicitly configured via {@link MarshallingContext.Builder#classLoader(ClassLoader)}, 783 * or <jk>null</jk> if none was set (callers should fall back to the thread-context classloader). 784 * 785 * @return The session classloader, or <jk>null</jk> if not set. 786 */ 787 public final ClassLoader getClassLoader() { return ctx.getClassLoader(); } 788 789 /** 790 * Java object swaps. 791 * 792 * @see MarshallingContext.Builder#swaps(Class...) 793 * @return 794 * The list POJO swaps defined. 795 * <br>Never <jk>null</jk>. 796 * <br>List is unmodifiable. 797 */ 798 public final List<ObjectSwap<?,?>> getSwaps() { return ctx.getSwaps(); } 799 800 /** 801 * Time zone. 802 * 803 * <p> 804 * The timezone is determined in the following order: 805 * <ol> 806 * <li><c>timeZone</c> parameter passed in through constructor. 807 * <li>{@link MarshallingContext.Builder#timeZone(TimeZone)} setting on bean context. 808 * </ol> 809 * 810 * @see MarshallingContext.Builder#timeZone(TimeZone) 811 * @return The session timezone, or <jk>null</jk> if timezone not specified. 812 */ 813 public final TimeZone getTimeZone() { return timeZone; } 814 815 /** 816 * Time zone. 817 * 818 * <p> 819 * The timezone is determined in the following order: 820 * <ol> 821 * <li><c>timeZone</c> parameter passed in through constructor. 822 * <li>{@link MarshallingContext.Builder#timeZone(TimeZone)} setting on bean context. 823 * </ol> 824 * 825 * @see MarshallingContext.Builder#timeZone(TimeZone) 826 * @return The session timezone, or the system timezone if not specified. Never <jk>null</jk>. 827 */ 828 public final ZoneId getTimeZoneId() { return timeZone == null ? ZoneId.systemDefault() : timeZone.toZoneId(); } 829 830 /** 831 * Determines whether the specified class matches the requirements on this context of being a bean. 832 * 833 * @param c The class being tested. 834 * @return <jk>true</jk> if the specified class is considered a bean. 835 */ 836 public final boolean isBean(Class<?> c) { 837 return nn(getBeanMeta(c)); 838 } 839 840 /** 841 * Determines whether the specified object matches the requirements on this context of being a bean. 842 * 843 * @param o The object being tested. 844 * @return <jk>true</jk> if the specified object is considered a bean. 845 */ 846 public final boolean isBean(Object o) { 847 if (o == null) 848 return false; 849 return isBean(o.getClass()); 850 } 851 852 /** 853 * BeanMap.put() returns old property value. 854 * 855 * @see MarshallingContext.Builder#beanMapPutReturnsOldValue() 856 * @return 857 * <jk>true</jk> if the {@link BeanMap#put(String,Object) BeanMap.put()} method will return old property values. 858 * <br>Otherwise, it returns <jk>null</jk>. 859 */ 860 public final boolean isBeanMapPutReturnsOldValue() { return ctx.isBeanMapPutReturnsOldValue(); } 861 862 /** 863 * Beans require no-arg constructors. 864 * 865 * @see MarshallingContext.Builder#beansRequireDefaultConstructor() 866 * @return 867 * <jk>true</jk> if a Java class must implement a default no-arg constructor to be considered a bean. 868 * <br>Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 869 */ 870 public final boolean isBeansRequireDefaultConstructor() { return ctx.isBeansRequireDefaultConstructor(); } 871 872 /** 873 * Beans require Serializable interface. 874 * 875 * @see MarshallingContext.Builder#beansRequireSerializable() 876 * @return 877 * <jk>true</jk> if a Java class must implement the {@link Serializable} interface to be considered a bean. 878 * <br>Otherwise, the bean will be serialized as a string using the {@link Object#toString()} method. 879 */ 880 public final boolean isBeansRequireSerializable() { return ctx.isBeansRequireSerializable(); } 881 882 /** 883 * Beans require setters for getters. 884 * 885 * @see MarshallingContext.Builder#beansRequireSettersForGetters() 886 * @return 887 * <jk>true</jk> if only getters that have equivalent setters will be considered as properties on a bean. 888 * <br>Otherwise, they are ignored. 889 */ 890 public final boolean isBeansRequireSettersForGetters() { return ctx.isBeansRequireSettersForGetters(); } 891 892 /** 893 * Beans require at least one property. 894 * 895 * @see MarshallingContext.Builder#disableBeansRequireSomeProperties() 896 * @return 897 * <jk>true</jk> if a Java class doesn't need to contain at least 1 property to be considered a bean. 898 * <br>Otherwise, the bean is serialized as a string using the {@link Object#toString()} method. 899 */ 900 public final boolean isBeansRequireSomeProperties() { return ctx.isBeansRequireSomeProperties(); } 901 902 /** 903 * Find fluent setters. 904 * 905 * <h5 class='section'>Description:</h5> 906 * <p> 907 * 908 * @see MarshallingContext.Builder#findFluentSetters() 909 * @return 910 * <jk>true</jk> if fluent setters are detected on beans. 911 */ 912 public final boolean isFindFluentSetters() { return ctx.isFindFluentSetters(); } 913 914 /** 915 * Ignore invocation errors on getters. 916 * 917 * @see MarshallingContext.Builder#ignoreInvocationExceptionsOnGetters() 918 * @return 919 * <jk>true</jk> if errors thrown when calling bean getter methods are silently ignored. 920 */ 921 public final boolean isIgnoreInvocationExceptionsOnGetters() { return ctx.isIgnoreInvocationExceptionsOnGetters(); } 922 923 /** 924 * Ignore invocation errors on setters. 925 * 926 * @see MarshallingContext.Builder#ignoreInvocationExceptionsOnSetters() 927 * @return 928 * <jk>true</jk> if errors thrown when calling bean setter methods are silently ignored. 929 */ 930 public final boolean isIgnoreInvocationExceptionsOnSetters() { return ctx.isIgnoreInvocationExceptionsOnSetters(); } 931 932 /** 933 * Silently ignore missing setters. 934 * 935 * @see MarshallingContext.Builder#disableIgnoreMissingSetters() 936 * @return 937 * <jk>true</jk> if trying to set a value on a bean property without a setter should throw a {@link BeanRuntimeException}. 938 */ 939 public final boolean isIgnoreMissingSetters() { return ctx.isIgnoreMissingSetters(); } 940 941 /** 942 * Ignore unknown properties. 943 * 944 * @see MarshallingContext.Builder#ignoreUnknownBeanProperties() 945 * @return 946 * <jk>true</jk> if trying to set a value on a non-existent bean property is silently ignored. 947 * <br>Otherwise, a {@code RuntimeException} is thrown. 948 */ 949 public final boolean isIgnoreUnknownBeanProperties() { return ctx.isIgnoreUnknownBeanProperties(); } 950 951 /** 952 * Schema validation mode. 953 * 954 * @see MarshallingContext.Builder#validateSchema() 955 * @return 956 * <jk>true</jk> if bean property values should be validated against their {@code @Schema}-declared constraints 957 * during parsing and serialization. 958 * @since 10.0.0 959 */ 960 public final boolean isValidateSchema() { return ctx.isValidateSchema(); } 961 962 /** 963 * Ignore unknown properties with null values. 964 * 965 * @see MarshallingContext.Builder#disableIgnoreUnknownNullBeanProperties() 966 * @return 967 * <jk>true</jk> if trying to set a <jk>null</jk> value on a non-existent bean property should not throw a {@link BeanRuntimeException}. 968 */ 969 public final boolean isIgnoreUnknownNullBeanProperties() { return ctx.isIgnoreUnknownNullBeanProperties(); } 970 971 /** 972 * Unsorted properties. 973 * 974 * @see MarshallingContext.Builder#unsortedProperties() 975 * @return 976 * <jk>true</jk> if bean properties are serialized in declaration order instead of alphabetically. 977 */ 978 public final boolean isUnsortedProperties() { return ctx.isUnsortedProperties(); } 979 980 /** 981 * Record component order. 982 * 983 * @see MarshallingContext.Builder#recordComponentOrder() 984 * @return <jk>true</jk> if {@link Record} bean properties use component order. 985 */ 986 public final boolean isRecordComponentOrder() { return ctx.isRecordComponentOrder(); } 987 988 /** 989 * Use interface proxies. 990 * 991 * @see MarshallingContext.Builder#disableInterfaceProxies() 992 * @return 993 * <jk>true</jk> if interfaces will be instantiated as proxy classes through the use of an 994 * {@link InvocationHandler} if there is no other way of instantiating them. 995 */ 996 public final boolean isUseInterfaceProxies() { return ctx.isUseInterfaceProxies(); } 997 998 /** 999 * Use Java Introspector. 1000 * 1001 * @see MarshallingContext.Builder#useJavaBeanIntrospector() 1002 * @return 1003 * <jk>true</jk> if the built-in Java bean introspector should be used for bean introspection. 1004 */ 1005 public final boolean isUseJavaBeanIntrospector() { return ctx.isUseJavaBeanIntrospector(); } 1006 1007 /** 1008 * Creates a new empty bean of the specified type, except used for instantiating inner member classes that must 1009 * be instantiated within another class instance. 1010 * 1011 * <h5 class='section'>Example:</h5> 1012 * <p class='bjava'> 1013 * <jc>// Construct a new instance of the specified bean class</jc> 1014 * Person <jv>person</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.newBean(Person.<jk>class</jk>); 1015 * </p> 1016 * 1017 * @param <T> The class type of the bean being created. 1018 * @param c The class type of the bean being created. 1019 * @return A new bean object, or <jk>null</jk> if the class is not a bean. 1020 * @throws BeanRuntimeException If the specified class is not a valid bean. 1021 */ 1022 public final <T> T newBean(Class<T> c) throws BeanRuntimeException { 1023 return newBean(null, c); 1024 } 1025 1026 /** 1027 * Same as {@link #newBean(Class)}, except used for instantiating inner member classes that must be instantiated 1028 * within another class instance. 1029 * 1030 * @param <T> The class type of the bean being created. 1031 * @param c The class type of the bean being created. 1032 * @param outer 1033 * If class is a member class, this is the instance of the containing class. 1034 * Should be <jk>null</jk> if not a member class. 1035 * @return A new bean object, or <jk>null</jk> if the class is not a bean. 1036 * @throws BeanRuntimeException If the specified class is not a valid bean. 1037 */ 1038 public final <T> T newBean(Object outer, Class<T> c) throws BeanRuntimeException { 1039 var cm = getClassMeta(c); 1040 var m = cm.getBeanMeta(); 1041 if (m == null) 1042 return null; 1043 try { 1044 var o = m.newBean(outer); 1045 if (o == null) 1046 throw brex(c, "Class does not have a no-arg constructor."); 1047 return o; 1048 } catch (BeanRuntimeException e) { 1049 throw e; 1050 } catch (Exception e) { 1051 throw brex(e); 1052 } 1053 } 1054 1055 /** 1056 * Creates a new {@link BeanMap} object (a modifiable {@link Map}) of the given class with uninitialized 1057 * property values. 1058 * 1059 * <p> 1060 * If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a 1061 * bean. 1062 * 1063 * <h5 class='section'>Example:</h5> 1064 * <p class='bjava'> 1065 * <jc>// Construct a new bean map wrapped around a new Person object</jc> 1066 * BeanMap<Person> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.newBeanMap(Person.<jk>class</jk>); 1067 * </p> 1068 * 1069 * @param <T> The class of the object being wrapped. 1070 * @param c The name of the class to create a new instance of. 1071 * @return A new instance of the class, or <jk>null</jk> if the class is not a bean. 1072 */ 1073 public final <T> BeanMap<T> newBeanMap(Class<T> c) { 1074 return newBeanMap(null, c); 1075 } 1076 1077 /** 1078 * Same as {@link #newBeanMap(Class)}, except used for instantiating inner member classes that must be instantiated 1079 * within another class instance. 1080 * 1081 * @param <T> The class of the object being wrapped. 1082 * @param c The name of the class to create a new instance of. 1083 * @param outer 1084 * If class is a member class, this is the instance of the containing class. 1085 * Should be <jk>null</jk> if not a member class. 1086 * @return A new instance of the class, or <jk>null</jk> if the class is not a bean. 1087 */ 1088 @SuppressWarnings({ 1089 "java:S1135", // Deferred design consideration. 1090 "java:S1168" // null when BeanMeta not found. Consider empty BeanMap. 1091 }) 1092 public final <T> BeanMap<T> newBeanMap(Object outer, Class<T> c) { 1093 var m = getBeanMeta(c); 1094 if (m == null) 1095 return null; 1096 T bean = null; 1097 if (ie(m.getConstructorArgs())) 1098 bean = newBean(outer, c); 1099 var bm = new BeanMap<>(bean, m); 1100 bm.setBeanSession(this); 1101 return bm; 1102 } 1103 1104 /** 1105 * Returns a reusable {@link ClassMeta} representation for the class <c>Object</c>. 1106 * 1107 * <p> 1108 * This <c>ClassMeta</c> is often used to represent "any object type" when an object type is not known. 1109 * 1110 * <p> 1111 * This method is identical to calling <code>getClassMeta(Object.<jk>class</jk>)</code> but uses a cached copy to 1112 * avoid a hashmap lookup. 1113 * 1114 * @return The {@link ClassMeta} object associated with the <c>Object</c> class. 1115 */ 1116 public final ClassMeta<Object> object() { 1117 return ctx.object(); 1118 } 1119 1120 /** 1121 * Returns a reusable {@link ClassMeta} representation for the class <c>String</c>. 1122 * 1123 * <p> 1124 * This <c>ClassMeta</c> is often used to represent key types in maps. 1125 * 1126 * <p> 1127 * This method is identical to calling <code>getClassMeta(String.<jk>class</jk>)</code> but uses a cached copy to 1128 * avoid a hashmap lookup. 1129 * 1130 * @return The {@link ClassMeta} object associated with the <c>String</c> class. 1131 */ 1132 public final ClassMeta<String> string() { 1133 return ctx.string(); 1134 } 1135 1136 /** 1137 * Wraps an object inside a {@link BeanMap} object (a modifiable {@link Map}). 1138 * 1139 * <p> 1140 * If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a 1141 * bean. 1142 * 1143 * <p> 1144 * If object is already a {@link BeanMap}, simply returns the same object. 1145 * 1146 * <h5 class='section'>Example:</h5> 1147 * <p class='bjava'> 1148 * <jc>// Construct a bean map around a bean instance</jc> 1149 * BeanMap<Person> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.toBeanMap(<jk>new</jk> Person()); 1150 * </p> 1151 * 1152 * @param <T> The class of the object being wrapped. 1153 * @param o The object to wrap in a map interface. Must not be null. 1154 * @return The wrapped object. 1155 */ 1156 @Override 1157 public final <T> BeanMap<T> toBeanMap(T o) { 1158 reqnn("o", o); 1159 if (o instanceof BeanMap o2) 1160 return o2; 1161 // Unwrap recognized proxies (e.g. Hibernate $HibernateProxy$) so we enumerate the entity's properties, not the proxy's. 1162 var unwrapped = getProxyFor(o); 1163 return this.toBeanMap(o, (Class<T>)(unwrapped != null ? unwrapped : o.getClass())); 1164 } 1165 1166 /** 1167 * Wraps an object inside a {@link BeanMap} object (i.e.: a modifiable {@link Map}) defined as a bean for one of its 1168 * class, a super class, or an implemented interface. 1169 * 1170 * <p> 1171 * If object is not a true bean, throws a {@link BeanRuntimeException} with an explanation of why it's not a bean. 1172 * 1173 * <h5 class='section'>Example:</h5> 1174 * <p class='bjava'> 1175 * <jc>// Construct a bean map for new bean using only properties defined in a superclass</jc> 1176 * BeanMap<MySubBean> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.toBeanMap(<jk>new</jk> MySubBean(), MySuperBean.<jk>class</jk>); 1177 * 1178 * <jc>// Construct a bean map for new bean using only properties defined in an interface</jc> 1179 * BeanMap<MySubBean> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.toBeanMap(<jk>new</jk> MySubBean(), MySuperInterface.<jk>class</jk>); 1180 * </p> 1181 * 1182 * @param <T> The class of the object being wrapped. 1183 * @param o The object to wrap in a bean interface. Must not be null. 1184 * @param c The superclass to narrow the bean properties to. Must not be null. 1185 * @return The bean representation. 1186 * @throws IllegalArgumentException 1187 * If either parameter is <jk>null</jk>, or if the specified object is not an instance of the specified class. 1188 * @throws 1189 * BeanRuntimeException If specified object is not a bean according to the bean rules specified in this context 1190 * class. 1191 */ 1192 public final <T> BeanMap<T> toBeanMap(T o, Class<? super T> c) throws BeanRuntimeException { 1193 reqnn("o", o); 1194 reqnn("c", c); 1195 req(c.isInstance(o), "The specified object is not an instance of the specified class. class='%s', objectClass='%s', object='%s'", cn(c), cn(o), 0); 1196 1197 var cm = getClassMeta(c); 1198 1199 BeanMeta m = cm.getBeanMeta(); 1200 if (m == null) 1201 throw brex(c, "Class is not a bean. Reason='%s'", cm.getNotABeanReason()); 1202 var bm = new BeanMap<>(o, m); 1203 bm.setBeanSession(this); 1204 return bm; 1205 } 1206 1207 /** 1208 * Wraps an object inside a {@link BeanMap} object (a modifiable {@link Map}). 1209 * 1210 * <p> 1211 * Same as {@link #toBeanMap(Object)} but allows you to specify a property namer instance. 1212 * 1213 * <h5 class='section'>Example:</h5> 1214 * <p class='bjava'> 1215 * <jc>// Construct a bean map around a bean instance</jc> 1216 * BeanMap<Person> <jv>beanMap</jv> = MarshallingContext.<jsf>DEFAULT</jsf>.toBeanMap(<jk>new</jk> Person(), PropertyNamerDLC.<jsf>INSTANCE</jsf>); 1217 * </p> 1218 * 1219 * <p> 1220 * The supplied {@code propertyNamer} controls the names of the properties exposed by the returned 1221 * {@link BeanMap}. For example, passing {@link PropertyNamerDLC#INSTANCE} renames a <c>fooBar</c> property to 1222 * <c>foo-bar</c>. 1223 * 1224 * <p> 1225 * When {@code propertyNamer} is <jk>null</jk> or equal to this session's {@link #getPropertyNamer() default 1226 * namer}, the shared, {@code Class}-keyed {@link BeanMeta} cache is used exactly as by {@link #toBeanMap(Object)}. 1227 * Otherwise a fresh, non-cached {@link BeanMeta} is built with the supplied namer so the default cache is never 1228 * polluted with alternate-namer property keys. 1229 * 1230 * @param <T> The class of the object being wrapped. 1231 * @param o The object to wrap in a map interface. Must not be null. 1232 * @param propertyNamer The property namer to use. Can be <jk>null</jk> to use the session's default namer. 1233 * @return The wrapped object. 1234 */ 1235 public final <T> BeanMap<T> toBeanMap(T o, PropertyNamer propertyNamer) { 1236 reqnn("o", o); 1237 if (o instanceof BeanMap o2) 1238 return o2; 1239 1240 // Unwrap recognized proxies (e.g. Hibernate $HibernateProxy$) so we enumerate the entity's properties, not the proxy's. 1241 var unwrapped = getProxyFor(o); 1242 var beanClass = unwrapped != null ? unwrapped : o.getClass(); 1243 1244 // Preserve the cached default-namer path when no override (or the override matches the default) is supplied. 1245 if (propertyNamer == null || propertyNamer.equals(getPropertyNamer())) 1246 return this.toBeanMap(o, (Class<T>)beanClass); 1247 1248 var c = (Class<T>)beanClass; 1249 var cm = getClassMeta(c); 1250 var m = cm.getBeanMeta(propertyNamer); 1251 if (m == null) 1252 throw brex(c, "Class is not a bean. Reason='%s'", cm.getNotABeanReason()); 1253 var bm = new BeanMap<>(o, m); 1254 bm.setBeanSession(this); 1255 return bm; 1256 } 1257 1258 /** 1259 * Same as {@link #convertToType(Object, ClassMeta)}, except used for instantiating inner member classes that must 1260 * be instantiated within another class instance. 1261 * 1262 * @param <T> The class type to convert the value to. 1263 * @param outer 1264 * If class is a member class, this is the instance of the containing class. 1265 * Should be <jk>null</jk> if not a member class. 1266 * @param value The value to convert. Can be <jk>null</jk> (returns <jk>null</jk>). 1267 * @param to The class type to convert the value to. Can be <jk>null</jk> (treated as the {@code Object} type). 1268 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type. 1269 * @return The converted value. 1270 */ 1271 protected final <T> T convertToMemberType(Object outer, Object value, ClassMeta<T> to) throws InvalidDataConversionException { 1272 if (to == null) 1273 to = (ClassMeta<T>)object(); 1274 try { 1275 // If the value is already an instance of the target type and no element/value conversion is needed, return as-is. 1276 // Skip this shortcut for typed maps/collections whose elements may need conversion. 1277 if (value != null && to.inner().isInstance(value) 1278 && !((to.isMap() && !to.getValueType().is(Object.class)) || ((to.isCollection() || to.isOptional()) && !to.getElementType().isObject()))) 1279 return (T) value; 1280 return ctx.getConverter().to(value, outer, this, to.innerType(), to.getParameters()); 1281 } catch (InvalidDataConversionException | MissingRequiredPropertyException e) { 1282 throw e; 1283 } catch (Exception e) { 1284 throw new InvalidDataConversionException(value, to, e); 1285 } 1286 } 1287 1288 /** 1289 * Given an array of {@link Type} objects, returns a {@link ClassMeta} representing those arguments. 1290 * 1291 * <p> 1292 * Constructs a new meta on each call. 1293 * 1294 * @param classes The array of classes to get class metas for. Must not be <jk>null</jk>. 1295 * @return The args {@link ClassMeta} object corresponding to the classes. Never <jk>null</jk>. 1296 */ 1297 protected final ClassMeta<Object[]> getArgsClassMeta(Type[] classes) { 1298 reqnn("classes", classes); 1299 return new ClassMeta(Arrays.stream(classes).map(this::getClassMeta).toList()); 1300 } 1301 1302 /** 1303 * Returns the {@link ClassMeta} for the object's class, unwrapping recognized proxies first. 1304 * 1305 * <p> 1306 * If {@link org.apache.juneau.commons.utils.ClassUtils#getProxyFor(Object)} returns a class, 1307 * that class is used; otherwise {@code o.getClass()}. JDK dynamic proxies (including 1308 * annotation instances) resolve to the first interface. Subclass proxies (CGLIB, 1309 * Javassist, ByteBuddy, Hibernate {@code $HibernateProxy$}) resolve to the immediate 1310 * superclass. Spring CGLIB objects may have {@code getTargetClass()} invoked on the live 1311 * instance. Names matching {@code $$SpringCGLIB$$} only (Spring 6) are not recognized. 1312 * 1313 * @param o The object to find the class type for. Can be <jk>null</jk> (returns {@code def}). 1314 * @param def The default {@link ClassMeta} if the object is null. Can be <jk>null</jk> (returned as-is if 1315 * <c>o</c> is <jk>null</jk>). 1316 * @return The ClassMeta object, or the default value if {@code o} is <jk>null</jk>. 1317 */ 1318 protected final ClassMeta<?> getClassMetaForObject(Object o, ClassMeta<?> def) { 1319 if (o == null) 1320 return def; 1321 var unwrapped = getProxyFor(o); 1322 Class<?> k = unwrapped != null ? unwrapped : o.getClass(); 1323 return getClassMeta(k); 1324 } 1325 1326 /** 1327 * Creates a generic {@link Map} for parser-driven map population, choosing the map type based on the key type. 1328 * 1329 * <p> 1330 * Returns the flavored {@link MarshalledMap} subclass when the key type is String (via {@link #newGenericMap()}), 1331 * otherwise returns a plain {@link LinkedHashMap} (since {@link MarshalledMap} is keyed by String). 1332 * 1333 * <p> 1334 * Per-marshaller parser sessions override {@link #newGenericMap()} (not this method) to return their flavored 1335 * {@code XMap} variant. 1336 * 1337 * @param mapMeta The metadata of the map to create. Must not be <jk>null</jk>. 1338 * @return A new map. 1339 */ 1340 protected Map newGenericMap(ClassMeta mapMeta) { 1341 var k = mapMeta.getKeyType(); 1342 return (k == null || k.isString()) ? newGenericMap() : map(); 1343 } 1344 1345 /** 1346 * Creates a string-keyed generic map for parser-driven map population. 1347 * 1348 * <p> 1349 * This is the main factory hook overridden by per-marshaller parser sessions to return their flavored 1350 * {@code XMap} variant (for example, {@link JsonParserSession} returns 1351 * {@link JsonMap}; {@link Json5ParserSession} returns 1352 * {@link Json5Map}). 1353 * 1354 * @return A new map. 1355 */ 1356 protected MarshalledMap newGenericMap() { 1357 return new MarshalledMap(this); 1358 } 1359 1360 /** 1361 * Creates a generic list for parser-driven list population. 1362 * 1363 * <p> 1364 * Per-marshaller parser sessions override this hook to return their flavored {@code XList} variant 1365 * (for example, {@link JsonParserSession} returns {@link JsonList}; 1366 * {@link Json5ParserSession} returns 1367 * {@link Json5List}). 1368 * 1369 * @return A new list. 1370 */ 1371 protected MarshalledList newGenericList() { 1372 return new MarshalledList(this); 1373 } 1374 1375 @Override /* Overridden from ContextSession */ 1376 protected FluentMap<String,Object> properties() { 1377 return super.properties() 1378 .a("locale", locale) 1379 .a("mediaType", mediaType) 1380 .a("timeZone", timeZone) 1381 .a("activeView", activeView); 1382 } 1383 1384 /** 1385 * Converts the contents of the specified list into an array. 1386 * 1387 * <p> 1388 * Works on both object and primitive arrays. 1389 * 1390 * <p> 1391 * In the case of multi-dimensional arrays, the incoming list must contain elements of type n-1 dimension. 1392 * i.e. if {@code type} is <code><jk>int</jk>[][]</code> then {@code list} must have entries of type 1393 * <code><jk>int</jk>[]</code>. 1394 * 1395 * @param type The type to convert to. Must be an array type. Must not be <jk>null</jk>. 1396 * @param list The contents to populate the array with. Can be <jk>null</jk> (returns <jk>null</jk>). 1397 * @return A new object or primitive array, or <jk>null</jk> if the list was <jk>null</jk>. 1398 */ 1399 protected final Object toArray(ClassMeta<?> type, Collection<?> list) { 1400 if (list == null) 1401 return null; 1402 var componentType = type.isArgs() ? object() : type.getElementType(); 1403 var array = Array.newInstance(componentType.inner(), list.size()); 1404 var i = IntegerHolder.create(); 1405 list.forEach(x -> { 1406 var x2 = x; 1407 if (! type.isInstance(x)) { 1408 if (componentType.isArray() && x instanceof Collection<?> x3) 1409 x2 = toArray(componentType, x3); 1410 else if (x == null && componentType.isPrimitive()) 1411 x2 = componentType.getPrimitiveDefault(); 1412 else 1413 x2 = convertToType(x, componentType); 1414 } 1415 Array.set(array, i.getAndIncrement(), x2); 1416 }); 1417 return array; 1418 } 1419 1420 //----------------------------------------------------------------------------------------------------------------- 1421 // BeanSession bridge methods — SPI seam to commons.bean. 1422 //----------------------------------------------------------------------------------------------------------------- 1423 1424 /** 1425 * Bridge implementation of {@link BeanSession#convertToType(Object, Object)} that dispatches to the 1426 * {@link ClassMeta}-typed overload when the supplied {@code targetType} is a {@link ClassMeta}, or to the 1427 * {@link Class}-typed overload when it is a {@link Class}. 1428 * 1429 * <p> 1430 * The bean-modeling layer calls this method through the {@link BeanSession} interface so it can request type 1431 * conversion without referencing the marshalling-side {@link ClassMeta} concrete type. 1432 * 1433 * @param value The value to convert. May be <jk>null</jk>. 1434 * @param targetType A {@link ClassMeta} or {@link Class} describing the target type. Can be <jk>null</jk> (treated as the {@code Object} type). 1435 * @return The converted value. 1436 * @throws IllegalArgumentException If {@code targetType} is neither a {@link ClassMeta} nor a {@link Class}. 1437 */ 1438 @Override /* BeanSession */ 1439 public final Object convertToType(Object value, Object targetType) { 1440 if (targetType == null) 1441 return convertToType(value, (ClassMeta<?>) null); 1442 if (targetType instanceof ClassMeta<?> targetType2) 1443 return convertToType(value, targetType2); 1444 if (targetType instanceof Class<?> targetType2) 1445 return convertToType(value, targetType2); 1446 throw iaex("Unsupported targetType for convertToType: %s", cn(targetType)); 1447 } 1448 1449 /** 1450 * Bridge implementation of {@link BeanSession#convertToMemberType(Object, Object, Object)} that dispatches to the 1451 * {@link ClassMeta}-typed overload when the supplied {@code targetType} is a {@link ClassMeta}, or to the 1452 * {@link Class}-typed overload when it is a {@link Class}. 1453 * 1454 * @param outer The outer-class instance for non-static inner classes. May be <jk>null</jk>. 1455 * @param value The value to convert. May be <jk>null</jk>. 1456 * @param targetType A {@link ClassMeta} or {@link Class} describing the target type. Can be <jk>null</jk> (treated as the {@code Object} type). 1457 * @return The converted value. 1458 * @throws IllegalArgumentException If {@code targetType} is neither a {@link ClassMeta} nor a {@link Class}. 1459 */ 1460 @Override /* BeanSession */ 1461 public final Object convertToMemberType(Object outer, Object value, Object targetType) { 1462 if (targetType == null) 1463 return convertToMemberType(outer, value, (ClassMeta<?>) null); 1464 if (targetType instanceof ClassMeta<?> targetType2) 1465 return convertToMemberType(outer, value, targetType2); 1466 if (targetType instanceof Class<?> targetType2) 1467 return convertToMemberType(outer, value, targetType2); 1468 throw iaex("Unsupported targetType for convertToMemberType: %s", cn(targetType)); 1469 } 1470 1471 /** 1472 * Bridge implementation of {@link BeanSession#parseToMap(CharSequence)} that delegates to 1473 * {@link Json5Map#ofString(CharSequence)} paired with this session. 1474 * 1475 * <p> 1476 * Used by {@link BeanPropertyMeta#setPropertyValue} to parse a {@link CharSequence} into a {@link Map} when 1477 * the property is map-typed. Lifted out of {@link BeanPropertyMeta} so the bean-modeling layer no longer 1478 * references the marshalling-side JSON parser. 1479 * 1480 * <p> 1481 * Uses {@link Json5Map} so that historic JSON5-form inputs (e.g. annotation default 1482 * values written as <c>{a:'b'}</c>) continue to parse — the strict-JSON {@link JsonMap} retargeting in v10.0 1483 * would otherwise break those call sites. 1484 * 1485 * @param value The JSON-formatted character sequence to parse. Can be <jk>null</jk> (returns an empty map). 1486 * @return The parsed {@link Json5Map} (empty if {@code value} is <jk>null</jk>), never <jk>null</jk>. 1487 */ 1488 @Override /* BeanSession */ 1489 public final Map<?,?> parseToMap(CharSequence value) { 1490 return Json5Map.ofString(value).session(this); 1491 } 1492 1493 /** 1494 * Bridge implementation of {@link BeanSession#parseToList(CharSequence)} that delegates to 1495 * {@link Json5List#Json5List(CharSequence)} paired with this session. 1496 * 1497 * <p> 1498 * Used by {@link BeanPropertyMeta#setPropertyValue} to parse a {@link CharSequence} into a {@link Collection} 1499 * when the property is collection-typed. Lifted out of {@link BeanPropertyMeta} so the bean-modeling layer 1500 * no longer references the marshalling-side JSON parser. 1501 * 1502 * <p> 1503 * Uses {@link Json5List} for the same back-compat reasons as 1504 * {@link #parseToMap(CharSequence)}. 1505 * 1506 * @param value The JSON-formatted character sequence to parse. Must not be <jk>null</jk>. 1507 * @return The parsed {@link Json5List}. 1508 */ 1509 @Override /* BeanSession */ 1510 public final Collection<?> parseToList(CharSequence value) { 1511 reqnn("value", value); 1512 return new Json5List(value).setBeanSession(this); 1513 } 1514 1515}