View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau;
18  
19  import static org.apache.juneau.commons.utils.Utils.*;
20  
21  import java.lang.annotation.*;
22  import java.util.*;
23  
24  import org.apache.juneau.commons.collections.*;
25  import org.apache.juneau.commons.function.*;
26  import org.apache.juneau.commons.reflect.*;
27  
28  /**
29   * Parent class for all classes that traverse POJOs.
30   *
31   * <h5 class='topic'>Description</h5>
32   * <p>
33   * Base class that serves as the parent class for all serializers and other classes that traverse POJOs.
34   *
35   * <h5 class='section'>Notes:</h5><ul>
36   * 	<li class='note'>This class is thread safe and reusable.
37   * </ul>
38   *
39   */
40  public abstract class BeanTraverseContext extends BeanContextable {
41  	/**
42  	 * Builder class.
43  	 */
44  	public abstract static class Builder extends BeanContextable.Builder {
45  
46  		private boolean detectRecursions;
47  		private boolean ignoreRecursions;
48  		private int initialDepth;
49  		private int maxDepth;
50  
51  		/**
52  		 * Constructor, default settings.
53  		 */
54  		protected Builder() {
55  			detectRecursions = env("BeanTraverseContext.detectRecursions", false);
56  			ignoreRecursions = env("BeanTraverseContext.ignoreRecursions", false);
57  			initialDepth = env("BeanTraverseContext.initialDepth", 0);
58  			maxDepth = env("BeanTraverseContext.maxDepth", 100);
59  		}
60  
61  		/**
62  		 * Copy constructor.
63  		 *
64  		 * @param copyFrom The bean to copy from.
65  		 */
66  		protected Builder(BeanTraverseContext copyFrom) {
67  			super(copyFrom);
68  			detectRecursions = copyFrom.getDetectRecursions();
69  			ignoreRecursions = copyFrom.getIgnoreRecursions();
70  			initialDepth = copyFrom.getInitialDepth();
71  			maxDepth = copyFrom.getMaxDepth();
72  		}
73  
74  		/**
75  		 * Copy constructor.
76  		 *
77  		 * @param copyFrom The builder to copy from.
78  		 */
79  		protected Builder(Builder copyFrom) {
80  			super(copyFrom);
81  			detectRecursions = copyFrom.detectRecursions;
82  			ignoreRecursions = copyFrom.ignoreRecursions;
83  			initialDepth = copyFrom.initialDepth;
84  			maxDepth = copyFrom.maxDepth;
85  		}
86  
87  		@Override /* Overridden from Builder */
88  		public Builder annotations(Annotation...values) {
89  			super.annotations(values);
90  			return this;
91  		}
92  
93  		@Override /* Overridden from Builder */
94  		public Builder apply(AnnotationWorkList work) {
95  			super.apply(work);
96  			return this;
97  		}
98  
99  		@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-&gt;B-&gt;C-&gt;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 }