ConstructorDetector.java
package tools.jackson.databind.cfg;
import tools.jackson.databind.util.ClassUtil;
/**
* Configurable handler used to select aspects of selecting
* constructor(s) to use as "Creators" for POJOs.
* Defines the API for handlers, a pre-defined set of standard instances
* and methods for constructing alternative configurations.
*
* @since 2.12
*/
public final class ConstructorDetector
implements java.io.Serializable
{
private static final long serialVersionUID = 3L;
/**
* See {@link #allowImplicitWithDefaultConstructor} for details.
*
* @since 3.0
*/
public final static boolean DEFAULT_ALLOW_IMPLICIT_WITH_DEFAULT_CONSTRUCTOR = true;
/**
* Definition of alternate handling modes of single-argument constructors
* that are annotated with {@link com.fasterxml.jackson.annotation.JsonCreator}
* but without "mode" definition (or explicit name for the argument):
* this is the case where two interpretations
* are possible -- "properties" (in which case the argument is named parameter
* of a JSON Object) and "delegating (in which case the argument maps to the
* whole JSON value).
*<p>
* Default choice is {@code HEURISTIC} (which is Jackson pre-2.12 always uses)
*<p>
* NOTE: does NOT have any effect if explicit {@code @JsonCreator} annotation
* is required.
*
* @since 2.12
*/
public enum SingleArgConstructor {
/**
* Assume "delegating" mode if not explicitly annotated otherwise
*/
DELEGATING,
/**
* Assume "properties" mode if not explicitly annotated otherwise
*/
PROPERTIES,
/**
* Use heuristics to see if "properties" mode is to be used (POJO has a
* property with the same name as the implicit name [if available] of
* the constructor argument).
* Note: this is the default choice for Jackson versions before 2.12.
*/
HEURISTIC,
/**
* Refuse to decide implicit mode and instead throw a
* {@link tools.jackson.databind.exc.InvalidDefinitionException}
* in ambiguous case.
*/
REQUIRE_MODE
}
/*
/**********************************************************************
/* Global default instances to use
/**********************************************************************
*/
/**
* Instance used by default, which:
*<ul>
* <li>Uses {@link SingleArgConstructor#HEURISTIC} for single-argument constructor case
* </li>
* <li>Does not require explicit {@code @JsonCreator} annotations (so allows
* auto-detection of Visible constructors} (except for JDK types)
* </li>
* <li>Does not allow auto-detection of Visible constructors for so-called JDK
* types; that is, classes in packages {@code java.*} and {@code javax.*}
* </li>
*</ul>
*/
public final static ConstructorDetector DEFAULT
= new ConstructorDetector(SingleArgConstructor.HEURISTIC);
/**
* Instance similar to {@link #DEFAULT} except that for single-argument case
* uses setting of {@link SingleArgConstructor#PROPERTIES}.
*/
public final static ConstructorDetector USE_PROPERTIES_BASED
= new ConstructorDetector(SingleArgConstructor.PROPERTIES);
/**
* Instance similar to {@link #DEFAULT} except that for single-argument case
* uses setting of {@link SingleArgConstructor#DELEGATING}.
*/
public final static ConstructorDetector USE_DELEGATING
= new ConstructorDetector(SingleArgConstructor.DELEGATING);
/**
* Instance similar to {@link #DEFAULT} except that for single-argument case
* uses setting of {@link SingleArgConstructor#REQUIRE_MODE}.
*/
public final static ConstructorDetector EXPLICIT_ONLY
= new ConstructorDetector(SingleArgConstructor.REQUIRE_MODE);
/*
/**********************************************************************
/* Configuration
/**********************************************************************
*/
protected final SingleArgConstructor _singleArgMode;
/**
* Whether explicit {@link com.fasterxml.jackson.annotation.JsonCreator}
* is always required for detecting constructors (even if visible) other
* than the default (no argument) constructor.
*/
protected final boolean _requireCtorAnnotation;
/**
* Whether auto-detection of constructors of "JDK types" (those in
* packages {@code java.} and {@code javax.}) is allowed or not
*/
protected final boolean _allowJDKTypeCtors;
/**
* Whether implicit detection of Properties-based Constructors is allowed
* when there is the default (no-parameter) constructor available.
*
* @since 3.0
*/
protected final boolean _allowImplicitWithDefaultConstructor;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
protected ConstructorDetector(SingleArgConstructor singleArgMode,
boolean requireCtorAnnotation,
boolean allowJDKTypeCtors,
boolean allowImplicitWithDefaultConstructor)
{
_singleArgMode = singleArgMode;
_requireCtorAnnotation = requireCtorAnnotation;
_allowJDKTypeCtors = allowJDKTypeCtors;
_allowImplicitWithDefaultConstructor = allowImplicitWithDefaultConstructor;
}
/**
* Constructors used for default configurations which only varies
* by {@code _singleArgMode}
*/
protected ConstructorDetector(SingleArgConstructor singleArgMode) {
this(singleArgMode, false, false, DEFAULT_ALLOW_IMPLICIT_WITH_DEFAULT_CONSTRUCTOR);
}
public ConstructorDetector withSingleArgMode(SingleArgConstructor singleArgMode) {
return new ConstructorDetector(singleArgMode,
_requireCtorAnnotation, _allowJDKTypeCtors,
_allowImplicitWithDefaultConstructor);
}
public ConstructorDetector withRequireAnnotation(boolean state) {
return new ConstructorDetector(_singleArgMode,
state, _allowJDKTypeCtors,
_allowImplicitWithDefaultConstructor);
}
public ConstructorDetector withAllowJDKTypeConstructors(boolean state) {
return new ConstructorDetector(_singleArgMode,
_requireCtorAnnotation, state,
_allowImplicitWithDefaultConstructor);
}
/**
* Mutant factory method that can be used to change setting that
* {@link #allowImplicitWithDefaultConstructor} returns.
*
* @since 3.0
*/
public ConstructorDetector withAllowImplicitWithDefaultConstructor(boolean state) {
return new ConstructorDetector(_singleArgMode,
_requireCtorAnnotation, _allowJDKTypeCtors,
state);
}
/*
/**********************************************************************
/* API
/**********************************************************************
*/
public SingleArgConstructor singleArgMode() {
return _singleArgMode;
}
public boolean requireCtorAnnotation() {
return _requireCtorAnnotation;
}
public boolean allowJDKTypeConstructors() {
return _allowJDKTypeCtors;
}
/**
* Method that can be used to determine whether implicit detection of
* Properties-based Constructors is allowed when there is a default
* (no-parameter) constructor available.
* Note that this only matters if {@link #shouldIntrospectImplicitConstructors}
* returns {@code true}.
*
* @since 3.0
*/
public boolean allowImplicitWithDefaultConstructor() {
return _allowImplicitWithDefaultConstructor;
}
public boolean singleArgCreatorDefaultsToDelegating() {
return _singleArgMode == SingleArgConstructor.DELEGATING;
}
public boolean singleArgCreatorDefaultsToProperties() {
return _singleArgMode == SingleArgConstructor.PROPERTIES;
}
/**
* Accessor that combines checks for whether implicit creators are allowed
* and, if so, whether JDK type constructors are allowed (if type is JDK type)
* to determine whether implicit constructor
* detection should be enabled for given type or not.
*
* @param rawType Value type to consider
*
* @return True if implicit constructor detection should be enabled; false if not
*/
public boolean shouldIntrospectImplicitConstructors(Class<?> rawType) {
// May not allow implicit creator introspection at all:
if (_requireCtorAnnotation) {
return false;
}
// But if it is allowed, may further limit use for JDK types
if (!_allowJDKTypeCtors) {
if (ClassUtil.isJDKClass(rawType)) {
// 18-Sep-2020, tatu: Looks like must make an exception for Exception
// types (ha!) -- at this point, single-String-arg constructor
// is to be auto-detected
if (!Throwable.class.isAssignableFrom(rawType)) {
return false;
}
}
}
return true;
}
}