001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau; 018 019import static org.apache.juneau.commons.utils.Utils.*; 020 021import java.lang.annotation.*; 022import java.util.*; 023 024import org.apache.juneau.commons.collections.*; 025import org.apache.juneau.commons.function.*; 026import org.apache.juneau.commons.reflect.*; 027 028/** 029 * Parent class for all classes that traverse POJOs. 030 * 031 * <h5 class='topic'>Description</h5> 032 * <p> 033 * Base class that serves as the parent class for all serializers and other classes that traverse POJOs. 034 * 035 * <h5 class='section'>Notes:</h5><ul> 036 * <li class='note'>This class is thread safe and reusable. 037 * </ul> 038 * 039 */ 040public abstract class BeanTraverseContext extends BeanContextable { 041 /** 042 * Builder class. 043 */ 044 public abstract static class Builder extends BeanContextable.Builder { 045 046 private boolean detectRecursions; 047 private boolean ignoreRecursions; 048 private int initialDepth; 049 private int maxDepth; 050 051 /** 052 * Constructor, default settings. 053 */ 054 protected Builder() { 055 detectRecursions = env("BeanTraverseContext.detectRecursions", false); 056 ignoreRecursions = env("BeanTraverseContext.ignoreRecursions", false); 057 initialDepth = env("BeanTraverseContext.initialDepth", 0); 058 maxDepth = env("BeanTraverseContext.maxDepth", 100); 059 } 060 061 /** 062 * Copy constructor. 063 * 064 * @param copyFrom The bean to copy from. 065 */ 066 protected Builder(BeanTraverseContext copyFrom) { 067 super(copyFrom); 068 detectRecursions = copyFrom.getDetectRecursions(); 069 ignoreRecursions = copyFrom.getIgnoreRecursions(); 070 initialDepth = copyFrom.getInitialDepth(); 071 maxDepth = copyFrom.getMaxDepth(); 072 } 073 074 /** 075 * Copy constructor. 076 * 077 * @param copyFrom The builder to copy from. 078 */ 079 protected Builder(Builder copyFrom) { 080 super(copyFrom); 081 detectRecursions = copyFrom.detectRecursions; 082 ignoreRecursions = copyFrom.ignoreRecursions; 083 initialDepth = copyFrom.initialDepth; 084 maxDepth = copyFrom.maxDepth; 085 } 086 087 @Override /* Overridden from Builder */ 088 public Builder annotations(Annotation...values) { 089 super.annotations(values); 090 return this; 091 } 092 093 @Override /* Overridden from Builder */ 094 public Builder apply(AnnotationWorkList work) { 095 super.apply(work); 096 return this; 097 } 098 099 @Override /* Overridden from Builder */ 100 public Builder applyAnnotations(Class<?>...from) { 101 super.applyAnnotations(from); 102 return this; 103 } 104 105 @Override /* Overridden from Builder */ 106 public Builder applyAnnotations(Object...from) { 107 super.applyAnnotations(from); 108 return this; 109 } 110 111 @Override /* Overridden from Builder */ 112 public Builder beanClassVisibility(Visibility value) { 113 super.beanClassVisibility(value); 114 return this; 115 } 116 117 @Override /* Overridden from Builder */ 118 public Builder beanConstructorVisibility(Visibility value) { 119 super.beanConstructorVisibility(value); 120 return this; 121 } 122 123 @Override /* Overridden from Builder */ 124 public Builder beanContext(BeanContext value) { 125 super.beanContext(value); 126 return this; 127 } 128 129 @Override /* Overridden from Builder */ 130 public Builder beanContext(BeanContext.Builder value) { 131 super.beanContext(value); 132 return this; 133 } 134 135 @Override /* Overridden from Builder */ 136 public Builder beanDictionary(java.lang.Class<?>...values) { 137 super.beanDictionary(values); 138 return this; 139 } 140 141 @Override /* Overridden from Builder */ 142 public Builder beanFieldVisibility(Visibility value) { 143 super.beanFieldVisibility(value); 144 return this; 145 } 146 147 @Override /* Overridden from Builder */ 148 public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) { 149 super.beanInterceptor(on, value); 150 return this; 151 } 152 153 @Override /* Overridden from Builder */ 154 public Builder beanMapPutReturnsOldValue() { 155 super.beanMapPutReturnsOldValue(); 156 return this; 157 } 158 159 @Override /* Overridden from Builder */ 160 public Builder beanMethodVisibility(Visibility value) { 161 super.beanMethodVisibility(value); 162 return this; 163 } 164 165 @Override /* Overridden from Builder */ 166 public Builder beanProperties(Class<?> beanClass, String properties) { 167 super.beanProperties(beanClass, properties); 168 return this; 169 } 170 171 @Override /* Overridden from Builder */ 172 public Builder beanProperties(Map<String,Object> values) { 173 super.beanProperties(values); 174 return this; 175 } 176 177 @Override /* Overridden from Builder */ 178 public Builder beanProperties(String beanClassName, String properties) { 179 super.beanProperties(beanClassName, properties); 180 return this; 181 } 182 183 @Override /* Overridden from Builder */ 184 public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) { 185 super.beanPropertiesExcludes(beanClass, properties); 186 return this; 187 } 188 189 @Override /* Overridden from Builder */ 190 public Builder beanPropertiesExcludes(Map<String,Object> values) { 191 super.beanPropertiesExcludes(values); 192 return this; 193 } 194 195 @Override /* Overridden from Builder */ 196 public Builder beanPropertiesExcludes(String beanClassName, String properties) { 197 super.beanPropertiesExcludes(beanClassName, properties); 198 return this; 199 } 200 201 @Override /* Overridden from Builder */ 202 public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) { 203 super.beanPropertiesReadOnly(beanClass, properties); 204 return this; 205 } 206 207 @Override /* Overridden from Builder */ 208 public Builder beanPropertiesReadOnly(Map<String,Object> values) { 209 super.beanPropertiesReadOnly(values); 210 return this; 211 } 212 213 @Override /* Overridden from Builder */ 214 public Builder beanPropertiesReadOnly(String beanClassName, String properties) { 215 super.beanPropertiesReadOnly(beanClassName, properties); 216 return this; 217 } 218 219 @Override /* Overridden from Builder */ 220 public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) { 221 super.beanPropertiesWriteOnly(beanClass, properties); 222 return this; 223 } 224 225 @Override /* Overridden from Builder */ 226 public Builder beanPropertiesWriteOnly(Map<String,Object> values) { 227 super.beanPropertiesWriteOnly(values); 228 return this; 229 } 230 231 @Override /* Overridden from Builder */ 232 public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { 233 super.beanPropertiesWriteOnly(beanClassName, properties); 234 return this; 235 } 236 237 @Override /* Overridden from Builder */ 238 public Builder beansRequireDefaultConstructor() { 239 super.beansRequireDefaultConstructor(); 240 return this; 241 } 242 243 @Override /* Overridden from Builder */ 244 public Builder beansRequireSerializable() { 245 super.beansRequireSerializable(); 246 return this; 247 } 248 249 @Override /* Overridden from Builder */ 250 public Builder beansRequireSettersForGetters() { 251 super.beansRequireSettersForGetters(); 252 return this; 253 } 254 255 @Override /* Overridden from Builder */ 256 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 257 super.cache(value); 258 return this; 259 } 260 261 @Override /* Overridden from Context.Builder */ 262 public abstract Builder copy(); 263 264 @Override /* Overridden from Builder */ 265 public Builder debug() { 266 super.debug(); 267 return this; 268 } 269 270 @Override /* Overridden from Builder */ 271 public Builder debug(boolean value) { 272 super.debug(value); 273 return this; 274 } 275 276 /** 277 * Automatically detect POJO recursions. 278 * 279 * <p> 280 * When enabled, specifies that recursions should be checked for during traversal. 281 * 282 * <p> 283 * Recursions can occur when traversing models that aren't true trees but rather contain loops. 284 * <br>In general, unchecked recursions cause stack-overflow-errors. 285 * <br>These show up as {@link BeanRecursionException BeanRecursionException} with the message <js>"Depth too deep. Stack overflow occurred."</js>. 286 * 287 * <h5 class='section'>Notes:</h5><ul> 288 * <li class='note'> 289 * Checking for recursion can cause a small performance penalty. 290 * </ul> 291 * 292 * <h5 class='section'>Example:</h5> 293 * <p class='bjava'> 294 * <jc>// Create a serializer that automatically checks for recursions.</jc> 295 * WriterSerializer <jv>serializer</jv> = JsonSerializer 296 * .<jsm>create</jsm>() 297 * .detectRecursions() 298 * .build(); 299 * 300 * <jc>// Create a POJO model with a recursive loop.</jc> 301 * <jk>public class</jk> MyBean { 302 * <jk>public</jk> Object <jf>f</jf>; 303 * } 304 * MyBean <jv>bean</jv> = <jk>new</jk> MyBean(); 305 * <jv>bean</jv>.<jf>f</jf> = <jv>bean</jv>; 306 * 307 * <jc>// Throws a SerializeException and not a StackOverflowError</jc> 308 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jv>bean</jv>); 309 * </p> 310 * 311 * @return This object. 312 */ 313 public Builder detectRecursions() { 314 return detectRecursions(true); 315 } 316 317 /** 318 * Same as {@link #detectRecursions()} but allows you to explicitly specify the value. 319 * 320 * @param value The value for this setting. 321 * @return This object. 322 */ 323 public Builder detectRecursions(boolean value) { 324 detectRecursions = value; 325 return this; 326 } 327 328 @Override /* Overridden from Builder */ 329 public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) { 330 super.dictionaryOn(on, values); 331 return this; 332 } 333 334 @Override /* Overridden from Builder */ 335 public Builder disableBeansRequireSomeProperties() { 336 super.disableBeansRequireSomeProperties(); 337 return this; 338 } 339 340 @Override /* Overridden from Builder */ 341 public Builder disableIgnoreMissingSetters() { 342 super.disableIgnoreMissingSetters(); 343 return this; 344 } 345 346 @Override /* Overridden from Builder */ 347 public Builder disableIgnoreTransientFields() { 348 super.disableIgnoreTransientFields(); 349 return this; 350 } 351 352 @Override /* Overridden from Builder */ 353 public Builder disableIgnoreUnknownNullBeanProperties() { 354 super.disableIgnoreUnknownNullBeanProperties(); 355 return this; 356 } 357 358 @Override /* Overridden from Builder */ 359 public Builder disableInterfaceProxies() { 360 super.disableInterfaceProxies(); 361 return this; 362 } 363 364 @Override /* Overridden from Builder */ 365 public <T> Builder example(Class<T> pojoClass, String json) { 366 super.example(pojoClass, json); 367 return this; 368 } 369 370 @Override /* Overridden from Builder */ 371 public <T> Builder example(Class<T> pojoClass, T o) { 372 super.example(pojoClass, o); 373 return this; 374 } 375 376 @Override /* Overridden from Builder */ 377 public Builder findFluentSetters() { 378 super.findFluentSetters(); 379 return this; 380 } 381 382 @Override /* Overridden from Builder */ 383 public Builder findFluentSetters(Class<?> on) { 384 super.findFluentSetters(on); 385 return this; 386 } 387 388 @Override /* Overridden from Context.Builder */ 389 public HashKey hashKey() { 390 // @formatter:off 391 return HashKey.of( 392 super.hashKey(), 393 detectRecursions, 394 ignoreRecursions, 395 initialDepth, 396 maxDepth 397 ); 398 // @formatter:on 399 } 400 401 @Override /* Overridden from Builder */ 402 public Builder ignoreInvocationExceptionsOnGetters() { 403 super.ignoreInvocationExceptionsOnGetters(); 404 return this; 405 } 406 407 @Override /* Overridden from Builder */ 408 public Builder ignoreInvocationExceptionsOnSetters() { 409 super.ignoreInvocationExceptionsOnSetters(); 410 return this; 411 } 412 413 /** 414 * Ignore recursion errors. 415 * 416 * <p> 417 * When enabled, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>. 418 * 419 * <p> 420 * For example, if a model contains the links A->B->C->A, then the JSON generated will look like 421 * the following when this setting is <jk>true</jk>... 422 * 423 * <p class='bjson'> 424 * {A:{B:{C:<jk>null</jk>}}} 425 * </p> 426 * 427 * <h5 class='section'>Notes:</h5><ul> 428 * <li class='note'> 429 * Checking for recursion can cause a small performance penalty. 430 * </ul> 431 * 432 * <h5 class='section'>Example:</h5> 433 * <p class='bjava'> 434 * <jc>// Create a serializer ignores recursions.</jc> 435 * WriterSerializer <jv>serializer</jv> = JsonSerializer 436 * .<jsm>create</jsm>() 437 * .ignoreRecursions() 438 * .build(); 439 * 440 * <jc>// Create a POJO model with a recursive loop.</jc> 441 * <jk>public class</jk> MyBean { 442 * <jk>public</jk> Object <jf>f</jf>; 443 * } 444 * MyBean <jv>bean</jv> = <jk>new</jk> MyBean(); 445 * <jv>bean</jv>.<jf>f</jf> = <jv>bean</jv>; 446 * 447 * <jc>// Produces "{f:null}"</jc> 448 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jv>bean</jv>); 449 * </p> 450 * 451 * @return This object. 452 */ 453 public Builder ignoreRecursions() { 454 return ignoreRecursions(true); 455 } 456 457 /** 458 * Same as {@link #ignoreRecursions()} but allows you to explicitly specify the value. 459 * 460 * @param value The value for this setting. 461 * @return This object. 462 */ 463 public Builder ignoreRecursions(boolean value) { 464 ignoreRecursions = value; 465 return this; 466 } 467 468 @Override /* Overridden from Builder */ 469 public Builder ignoreUnknownBeanProperties() { 470 super.ignoreUnknownBeanProperties(); 471 return this; 472 } 473 474 @Override /* Overridden from Builder */ 475 public Builder ignoreUnknownEnumValues() { 476 super.ignoreUnknownEnumValues(); 477 return this; 478 } 479 480 @Override /* Overridden from Builder */ 481 public Builder impl(Context value) { 482 super.impl(value); 483 return this; 484 } 485 486 @Override /* Overridden from Builder */ 487 public Builder implClass(Class<?> interfaceClass, Class<?> implClass) { 488 super.implClass(interfaceClass, implClass); 489 return this; 490 } 491 492 @Override /* Overridden from Builder */ 493 public Builder implClasses(Map<Class<?>,Class<?>> values) { 494 super.implClasses(values); 495 return this; 496 } 497 498 /** 499 * Initial depth. 500 * 501 * <p> 502 * The initial indentation level at the root. 503 * 504 * <p> 505 * Useful when constructing document fragments that need to be indented at a certain level when whitespace is enabled. 506 * 507 * <h5 class='section'>Example:</h5> 508 * <p class='bjava'> 509 * <jc>// Create a serializer with whitespace enabled and an initial depth of 2.</jc> 510 * WriterSerializer <jv>serializer</jv> = JsonSerializer 511 * .<jsm>create</jsm>() 512 * .ws() 513 * .initialDepth(2) 514 * .build(); 515 * 516 * <jc>// Produces "\t\t{\n\t\t\t'foo':'bar'\n\t\t}\n"</jc> 517 * String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean()); 518 * </p> 519 * 520 * @param value 521 * The new value for this setting. 522 * <br>The default is <c>0</c>. 523 * @return This object. 524 */ 525 public Builder initialDepth(int value) { 526 initialDepth = value; 527 return this; 528 } 529 530 @Override /* Overridden from Builder */ 531 public Builder interfaceClass(Class<?> on, Class<?> value) { 532 super.interfaceClass(on, value); 533 return this; 534 } 535 536 @Override /* Overridden from Builder */ 537 public Builder interfaces(java.lang.Class<?>...value) { 538 super.interfaces(value); 539 return this; 540 } 541 542 @Override /* Overridden from Builder */ 543 public Builder locale(Locale value) { 544 super.locale(value); 545 return this; 546 } 547 548 /** 549 * Max traversal depth. 550 * 551 * <p> 552 * When enabled, abort traversal if specified depth is reached in the POJO tree. 553 * 554 * <p> 555 * If this depth is exceeded, an exception is thrown. 556 * 557 * <p> 558 * This prevents stack overflows from occurring when trying to traverse models with recursive references. 559 * 560 * <h5 class='section'>Example:</h5> 561 * <p class='bjava'> 562 * <jc>// Create a serializer that throws an exception if the depth reaches greater than 20.</jc> 563 * WriterSerializer <jv>serializer</jv> = JsonSerializer 564 * .<jsm>create</jsm>() 565 * .maxDepth(20) 566 * .build(); 567 * </p> 568 * 569 * <h5 class='section'>See Also:</h5><ul> 570 * <li class='jm'>{@link Builder#maxDepth(int)} 571 * </ul> 572 * 573 * @param value 574 * The new value for this setting. 575 * <br>The default is <c>100</c>. 576 * @return This object. 577 */ 578 public Builder maxDepth(int value) { 579 maxDepth = value; 580 return this; 581 } 582 583 @Override /* Overridden from Builder */ 584 public Builder mediaType(MediaType value) { 585 super.mediaType(value); 586 return this; 587 } 588 589 @Override /* Overridden from Builder */ 590 public Builder notBeanClasses(java.lang.Class<?>...values) { 591 super.notBeanClasses(values); 592 return this; 593 } 594 595 @Override /* Overridden from Builder */ 596 public Builder notBeanPackages(String...values) { 597 super.notBeanPackages(values); 598 return this; 599 } 600 601 @Override /* Overridden from Builder */ 602 public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) { 603 super.propertyNamer(on, value); 604 return this; 605 } 606 607 @Override /* Overridden from Builder */ 608 public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) { 609 super.propertyNamer(value); 610 return this; 611 } 612 613 @Override /* Overridden from Builder */ 614 public Builder sortProperties() { 615 super.sortProperties(); 616 return this; 617 } 618 619 @Override /* Overridden from Builder */ 620 public Builder sortProperties(java.lang.Class<?>...on) { 621 super.sortProperties(on); 622 return this; 623 } 624 625 @Override /* Overridden from Builder */ 626 public Builder stopClass(Class<?> on, Class<?> value) { 627 super.stopClass(on, value); 628 return this; 629 } 630 631 @Override /* Overridden from Builder */ 632 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) { 633 super.swap(normalClass, swappedClass, swapFunction); 634 return this; 635 } 636 637 @Override /* Overridden from Builder */ 638 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) { 639 super.swap(normalClass, swappedClass, swapFunction, unswapFunction); 640 return this; 641 } 642 643 @Override /* Overridden from Builder */ 644 public Builder swaps(Class<?>...values) { 645 super.swaps(values); 646 return this; 647 } 648 649 @Override /* Overridden from Builder */ 650 public Builder swaps(Object...values) { 651 super.swaps(values); 652 return this; 653 } 654 655 @Override /* Overridden from Builder */ 656 public Builder timeZone(TimeZone value) { 657 super.timeZone(value); 658 return this; 659 } 660 661 @Override /* Overridden from Builder */ 662 public Builder type(Class<? extends org.apache.juneau.Context> value) { 663 super.type(value); 664 return this; 665 } 666 667 @Override /* Overridden from Builder */ 668 public Builder typeName(Class<?> on, String value) { 669 super.typeName(on, value); 670 return this; 671 } 672 673 @Override /* Overridden from Builder */ 674 public Builder typePropertyName(Class<?> on, String value) { 675 super.typePropertyName(on, value); 676 return this; 677 } 678 679 @Override /* Overridden from Builder */ 680 public Builder typePropertyName(String value) { 681 super.typePropertyName(value); 682 return this; 683 } 684 685 @Override /* Overridden from Builder */ 686 public Builder useEnumNames() { 687 super.useEnumNames(); 688 return this; 689 } 690 691 @Override /* Overridden from Builder */ 692 public Builder useJavaBeanIntrospector() { 693 super.useJavaBeanIntrospector(); 694 return this; 695 } 696 } 697 698 private final boolean detectRecursions; 699 private final boolean ignoreRecursions; 700 private final int initialDepth; 701 private final int maxDepth; 702 private final boolean actualDetectRecursions; 703 704 /** 705 * Constructor 706 * 707 * @param builder The builder for this object. 708 */ 709 protected BeanTraverseContext(Builder builder) { 710 super(builder); 711 712 detectRecursions = builder.detectRecursions; 713 ignoreRecursions = builder.ignoreRecursions; 714 initialDepth = builder.initialDepth; 715 maxDepth = builder.maxDepth; 716 717 actualDetectRecursions = detectRecursions || ignoreRecursions || super.isDebug(); 718 } 719 720 @Override /* Overridden from Context */ 721 public abstract Builder copy(); 722 723 /** 724 * Initial depth. 725 * 726 * @see Builder#initialDepth(int) 727 * @return 728 * The initial indentation level at the root. 729 */ 730 public final int getInitialDepth() { return initialDepth; } 731 732 /** 733 * Max traversal depth. 734 * 735 * @see Builder#maxDepth(int) 736 * @return 737 * The depth at which traversal is aborted if depth is reached in the POJO tree. 738 * <br>If this depth is exceeded, an exception is thrown. 739 */ 740 public final int getMaxDepth() { return maxDepth; } 741 742 /** 743 * Automatically detect POJO recursions. 744 * 745 * @see Builder#detectRecursions() 746 * @return 747 * <jk>true</jk> if recursions should be checked for during traversal. 748 */ 749 public final boolean isDetectRecursions() { return actualDetectRecursions; } 750 751 /** 752 * Ignore recursion errors. 753 * 754 * @see Builder#ignoreRecursions() 755 * @return 756 * <jk>true</jk> if when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>. 757 * <br>Otherwise, an exception is thrown with the message <js>"Recursion occurred, stack=..."</js>. 758 */ 759 public final boolean isIgnoreRecursions() { return ignoreRecursions; } 760 761 /** 762 * Detect recursions flag (raw value). 763 * 764 * @return The detect recursions flag. 765 */ 766 protected final boolean getDetectRecursions() { return detectRecursions; } 767 768 /** 769 * Ignore recursions flag (raw value). 770 * 771 * @return The ignore recursions flag. 772 */ 773 protected final boolean getIgnoreRecursions() { return ignoreRecursions; } 774 775 @Override /* Overridden from BeanContextable */ 776 protected FluentMap<String,Object> properties() { 777 return super.properties() 778 .a("detectRecursions", detectRecursions) 779 .a("ignoreRecursions", ignoreRecursions) 780 .a("initialDepth", initialDepth) 781 .a("maxDepth", maxDepth); 782 } 783}