org.apache.juneau.BeanTraverseContext Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau;
import org.apache.juneau.parser.*;
/**
* Parent class for all classes that traverse POJOs.
*
* Description
*
* Base class that serves as the parent class for all serializers and other classes that traverse POJOs.
*/
public abstract class BeanTraverseContext extends BeanContext {
//-------------------------------------------------------------------------------------------------------------------
// Configurable properties
//-------------------------------------------------------------------------------------------------------------------
private static final String PREFIX = "BeanTraverseContext.";
/**
* Configuration property: Automatically detect POJO recursions.
*
* Property:
*
* - Name:
"BeanTraverseContext.detectRecursions.b"
* - Data type:
Boolean
* - Default:
false
* - Session property:
false
* - Methods:
*
* - {@link BeanTraverseBuilder#detectRecursions(boolean)}
*
- {@link BeanTraverseBuilder#detectRecursions()}
*
*
*
* Description:
*
* Specifies that recursions should be checked for during traversal.
*
*
* Recursions can occur when traversing models that aren't true trees but rather contain loops.
*
In general, unchecked recursions cause stack-overflow-errors.
*
These show up as {@link ParseException ParseExceptions} with the message "Depth too deep. Stack overflow occurred." .
*
*
* The behavior when recursions are detected depends on the value for {@link #BEANTRAVERSE_ignoreRecursions}.
*
*
* For example, if a model contains the links A->B->C->A, then the JSON generated will look like
* the following when BEANTRAVERSE_ignoreRecursions is true ...
*
*
* {A:{B:{C:null }}}
*
*
* Notes:
*
* -
* Checking for recursion can cause a small performance penalty.
*
*
* Example:
*
* // Create a serializer that never adds _type to nodes.
* WriterSerializer s = JsonSerializer
* .create ()
* .detectRecursions()
* .ignoreRecursions()
* .build();
*
* // Same, but use property.
* WriterSerializer s = JsonSerializer
* .create ()
* .set(BEANTRAVERSE_detectRecursions , true )
* .set(BEANTRAVERSE_ignoreRecursions , true )
* .build();
*
* // Create a POJO model with a recursive loop.
* public class A {
* public Object f ;
* }
* A a = new A();
* a.f = a;
*
* // Produces "{f:null}"
* String json = s.serialize(a);
*
*/
public static final String BEANTRAVERSE_detectRecursions = PREFIX + "detectRecursions.b";
/**
* Configuration property: Ignore recursion errors.
*
* Property:
*
* - Name:
"BeanTraverseContext.ignoreRecursions.b"
* - Data type:
Boolean
* - Default:
false
* - Session property:
false
* - Methods:
*
* - {@link BeanTraverseBuilder#ignoreRecursions(boolean)}
*
- {@link BeanTraverseBuilder#ignoreRecursions()}
*
*
*
* Description:
*
* Used in conjunction with {@link #BEANTRAVERSE_detectRecursions}.
*
Setting is ignored if BEANTRAVERSE_detectRecursions is false .
*
*
* If true , when we encounter the same object when traversing a tree, we set the value to null .
*
Otherwise, a {@link BeanRecursionException} is thrown with the message "Recursion occurred, stack=..." .
*/
public static final String BEANTRAVERSE_ignoreRecursions = PREFIX + "ignoreRecursions.b";
/**
* Configuration property: Initial depth.
*
*
Property:
*
* - Name:
"BeanTraverseContext.initialDepth.i"
* - Data type:
Integer
* - Default:
0
* - Session property:
false
* - Methods:
*
* - {@link BeanTraverseBuilder#initialDepth(int)}
*
*
*
* Description:
*
* The initial indentation level at the root.
*
Useful when constructing document fragments that need to be indented at a certain level.
*
*
Example:
*
* // Create a serializer with whitespace enabled and an initial depth of 2.
* WriterSerializer s = JsonSerializer
* .create ()
* .ws()
* .initialDepth(2)
* .build();
*
* // Same, but use property.
* WriterSerializer s = JsonSerializer
* .create ()
* .set(BEANTRAVERSE_useWhitespace , true )
* .set(BEANTRAVERSE_initialDepth , 2)
* .build();
*
* // Produces "\t\t{\n\t\t\t'foo':'bar'\n\t\t}\n"
* String json = s.serialize(new MyBean());
*
*/
public static final String BEANTRAVERSE_initialDepth = PREFIX + "initialDepth.i";
/**
* Configuration property: Max traversal depth.
*
* Property:
*
* - Name:
"BeanTraverseContext.maxDepth.i"
* - Data type:
Integer
* - Default:
100
* - Session property:
false
* - Methods:
*
* - {@link BeanTraverseBuilder#maxDepth(int)}
*
*
*
* Description:
*
* Abort traversal if specified depth is reached in the POJO tree.
*
If this depth is exceeded, an exception is thrown.
*
*
Example:
*
* // Create a serializer that throws an exception if the depth is greater than 20.
* WriterSerializer s = JsonSerializer
* .create ()
* .maxDepth(20)
* .build();
*
* // Same, but use property.
* WriterSerializer s = JsonSerializer
* .create ()
* .set(BEANTRAVERSE_maxDepth , 20)
* .build();
*
*/
public static final String BEANTRAVERSE_maxDepth = PREFIX + "maxDepth.i";
//-------------------------------------------------------------------------------------------------------------------
// Instance
//-------------------------------------------------------------------------------------------------------------------
private final int initialDepth, maxDepth;
private final boolean
detectRecursions,
ignoreRecursions;
/**
* Constructor
*
* @param ps
* The property store containing all the settings for this object.
*/
protected BeanTraverseContext(PropertyStore ps) {
super(ps);
maxDepth = getIntegerProperty(BEANTRAVERSE_maxDepth, 100);
initialDepth = getIntegerProperty(BEANTRAVERSE_initialDepth, 0);
detectRecursions = getBooleanProperty(BEANTRAVERSE_detectRecursions, false);
ignoreRecursions = getBooleanProperty(BEANTRAVERSE_ignoreRecursions, false);
}
@Override /* Context */
public BeanTraverseBuilder builder() {
return null;
}
@Override /* Context */
public BeanTraverseSession createSession(BeanSessionArgs args) {
return new BeanTraverseSession(this, args);
}
//-----------------------------------------------------------------------------------------------------------------
// Properties
//-----------------------------------------------------------------------------------------------------------------
/**
* Configuration property: Initial depth.
*
* @see #BEANTRAVERSE_initialDepth
* @return
* The initial indentation level at the root.
*/
protected final int getInitialDepth() {
return initialDepth;
}
/**
* Configuration property: Max traversal depth.
*
* @see #BEANTRAVERSE_maxDepth
* @return
* The depth at which traversal is aborted if depth is reached in the POJO tree.
*
If this depth is exceeded, an exception is thrown.
*/
protected final int getMaxDepth() {
return maxDepth;
}
/**
* Configuration property: Automatically detect POJO recursions.
* @see #BEANTRAVERSE_detectRecursions
* @return
* true if recursions should be checked for during traversal.
*/
protected final boolean isDetectRecursions() {
return detectRecursions;
}
/**
* Configuration property: Ignore recursion errors.
*
* @see #BEANTRAVERSE_ignoreRecursions
* @return
* true if when we encounter the same object when traversing a tree, we set the value to null .
*
Otherwise, an exception is thrown with the message "Recursion occurred, stack=..." .
*/
protected final boolean isIgnoreRecursions() {
return ignoreRecursions;
}
@Override /* Context */
public ObjectMap asMap() {
return super.asMap()
.append("BeanTraverseContext", new ObjectMap()
.append("maxDepth", maxDepth)
.append("initialDepth", initialDepth)
.append("detectRecursions", detectRecursions)
.append("ignoreRecursions", ignoreRecursions)
);
}
}