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