Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector Maven / Gradle / Ivy
Go to download
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
package com.fasterxml.jackson.databind.introspect;
import java.lang.reflect.Modifier;
import java.util.*;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.ConstructorDetector;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jdk14.JDK14Util;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* Helper class used for aggregating information about all possible
* properties of a POJO.
*/
public class POJOPropertiesCollector
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Configuration settings
*/
protected final MapperConfig> _config;
/**
* Handler used for name-mangling of getter, mutator (setter/with) methods
*
* @since 2.12
*/
protected final AccessorNamingStrategy _accessorNaming;
/**
* True if introspection is done for serialization (giving
* precedence for serialization annotations), or not (false, deserialization)
*/
protected final boolean _forSerialization;
/**
* Type of POJO for which properties are being collected.
*/
protected final JavaType _type;
/**
* Low-level introspected class information (methods, fields etc)
*/
protected final AnnotatedClass _classDef;
protected final VisibilityChecker> _visibilityChecker;
protected final AnnotationIntrospector _annotationIntrospector;
/**
* @since 2.9
*/
protected final boolean _useAnnotations;
/**
* @since 2.15
*/
protected final boolean _isRecordType;
/*
/**********************************************************
/* Collected property information
/**********************************************************
*/
/**
* State flag we keep to indicate whether actual property information
* has been collected or not.
*/
protected boolean _collected;
/**
* Set of logical property information collected so far.
*
* Since 2.6, this has been constructed (more) lazily, to defer
* throwing of exceptions for potential conflicts in cases where
* this may not be an actual problem.
*/
protected LinkedHashMap _properties;
protected List _creatorProperties;
/**
* @since 2.18
*/
protected PotentialCreators _potentialCreators;
/**
* A set of "field renamings" that have been discovered, indicating
* intended renaming of other accessors: key is the implicit original
* name and value intended name to use instead.
*
* Note that these renamings are applied earlier than "regular" (explicit)
* renamings and affect implicit name: their effect may be changed by
* further renaming based on explicit indicators.
* The main use case is to effectively relink accessors based on fields
* discovered, and used to sort of correct otherwise missing linkage between
* fields and other accessors.
*
* @since 2.11
*/
protected Map _fieldRenameMappings;
protected LinkedList _anyGetters;
/**
* @since 2.12
*/
protected LinkedList _anyGetterField;
protected LinkedList _anySetters;
protected LinkedList _anySetterField;
/**
* Accessors (field or "getter" method annotated with
* {@link com.fasterxml.jackson.annotation.JsonKey}
*
* @since 2.12
*/
protected LinkedList _jsonKeyAccessors;
/**
* Accessors (field or "getter" method) annotated with
* {@link com.fasterxml.jackson.annotation.JsonValue}
*/
protected LinkedList _jsonValueAccessors;
/**
* Lazily collected list of properties that can be implicitly
* ignored during serialization; only updated when collecting
* information for deserialization purposes
*/
protected HashSet _ignoredPropertyNames;
/**
* Lazily collected list of members that were annotated to
* indicate that they represent mutators for deserializer
* value injection.
*/
protected LinkedHashMap _injectables;
/**
* Lazily accessed information about POJO format overrides
*
* @since 2.17
*/
protected JsonFormat.Value _formatOverrides;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @since 2.12
*/
protected POJOPropertiesCollector(MapperConfig> config, boolean forSerialization,
JavaType type, AnnotatedClass classDef,
AccessorNamingStrategy accessorNaming)
{
_config = config;
_forSerialization = forSerialization;
_type = type;
_classDef = classDef;
_isRecordType = _type.isRecordType();
if (config.isAnnotationProcessingEnabled()) {
_useAnnotations = true;
_annotationIntrospector = _config.getAnnotationIntrospector();
} else {
_useAnnotations = false;
_annotationIntrospector = AnnotationIntrospector.nopInstance();
}
_visibilityChecker = _config.getDefaultVisibilityChecker(type.getRawClass(),
classDef);
_accessorNaming = accessorNaming;
}
/*
/**********************************************************
/* Public API
/**********************************************************
*/
public MapperConfig> getConfig() {
return _config;
}
public JavaType getType() {
return _type;
}
/**
* @since 2.15
*/
public boolean isRecordType() {
return _isRecordType;
}
public AnnotatedClass getClassDef() {
return _classDef;
}
public AnnotationIntrospector getAnnotationIntrospector() {
return _annotationIntrospector;
}
public List getProperties() {
// make sure we return a copy, so caller can remove entries if need be:
Map props = getPropertyMap();
return new ArrayList<>(props.values());
}
// @since 2.18
public PotentialCreators getPotentialCreators() {
if (!_collected) {
collectAll();
}
return _potentialCreators;
}
public Map getInjectables() {
if (!_collected) {
collectAll();
}
return _injectables;
}
/**
* @since 2.12
*/
public AnnotatedMember getJsonKeyAccessor() {
if (!_collected) {
collectAll();
}
// If @JsonKey defined, must have a single one
if (_jsonKeyAccessors != null) {
if (_jsonKeyAccessors.size() > 1) {
if (!_resolveFieldVsGetter(_jsonKeyAccessors)) {
reportProblem("Multiple 'as-key' properties defined (%s vs %s)",
_jsonKeyAccessors.get(0),
_jsonKeyAccessors.get(1));
}
}
// otherwise we won't greatly care
return _jsonKeyAccessors.get(0);
}
return null;
}
/**
* @since 2.9
*/
public AnnotatedMember getJsonValueAccessor()
{
if (!_collected) {
collectAll();
}
// If @JsonValue defined, must have a single one
// 15-Jan-2023, tatu: Except let's try resolving "getter-over-field" case at least
if (_jsonValueAccessors != null) {
if (_jsonValueAccessors.size() > 1) {
if (!_resolveFieldVsGetter(_jsonValueAccessors)) {
reportProblem("Multiple 'as-value' properties defined (%s vs %s)",
_jsonValueAccessors.get(0),
_jsonValueAccessors.get(1));
}
}
// otherwise we won't greatly care
return _jsonValueAccessors.get(0);
}
return null;
}
/**
* Alias for {@link #getAnyGetterMethod()}.
*
* @deprecated Since 2.12 use separate {@link #getAnyGetterMethod()} and
* {@link #getAnyGetterField()}.
*/
@Deprecated // since 2.12
public AnnotatedMember getAnyGetter() {
return getAnyGetterMethod();
}
/**
* @since 2.12 (before only had "getAnyGetter()")
*/
public AnnotatedMember getAnyGetterField()
{
if (!_collected) {
collectAll();
}
if (_anyGetterField != null) {
if (_anyGetterField.size() > 1) {
reportProblem("Multiple 'any-getter' fields defined (%s vs %s)",
_anyGetterField.get(0), _anyGetterField.get(1));
}
return _anyGetterField.getFirst();
}
return null;
}
/**
* @since 2.12 (before only had "getAnyGetter()")
*/
public AnnotatedMember getAnyGetterMethod()
{
if (!_collected) {
collectAll();
}
if (_anyGetters != null) {
if (_anyGetters.size() > 1) {
reportProblem("Multiple 'any-getter' methods defined (%s vs %s)",
_anyGetters.get(0), _anyGetters.get(1));
}
return _anyGetters.getFirst();
}
return null;
}
public AnnotatedMember getAnySetterField()
{
if (!_collected) {
collectAll();
}
if (_anySetterField != null) {
if (_anySetterField.size() > 1) {
reportProblem("Multiple 'any-setter' fields defined (%s vs %s)",
_anySetterField.get(0), _anySetterField.get(1));
}
return _anySetterField.getFirst();
}
return null;
}
public AnnotatedMethod getAnySetterMethod()
{
if (!_collected) {
collectAll();
}
if (_anySetters != null) {
if (_anySetters.size() > 1) {
reportProblem("Multiple 'any-setter' methods defined (%s vs %s)",
_anySetters.get(0), _anySetters.get(1));
}
return _anySetters.getFirst();
}
return null;
}
/**
* Accessor for set of properties that are explicitly marked to be ignored
* via per-property markers (but NOT class annotations).
*/
public Set getIgnoredPropertyNames() {
return _ignoredPropertyNames;
}
/**
* Accessor to find out whether type specified requires inclusion
* of Object Identifier.
*/
public ObjectIdInfo getObjectIdInfo()
{
ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_classDef);
if (info != null) { // 2.1: may also have different defaults for refs:
info = _annotationIntrospector.findObjectReferenceInfo(_classDef, info);
}
return info;
}
// Method called by main "getProperties()" method; left
// "protected" for unit tests
protected Map getPropertyMap() {
if (!_collected) {
collectAll();
}
return _properties;
}
/**
* @since 2.17
*/
public JsonFormat.Value getFormatOverrides() {
if (_formatOverrides == null) {
JsonFormat.Value format = null;
// Let's check both per-type defaults and annotations;
// per-type defaults having higher precedence, so start with annotations
if (_annotationIntrospector != null) {
format = _annotationIntrospector.findFormat(_classDef);
}
JsonFormat.Value v = _config.getDefaultPropertyFormat(_type.getRawClass());
if (v != null) {
if (format == null) {
format = v;
} else {
format = format.withOverrides(v);
}
}
_formatOverrides = (format == null) ? JsonFormat.Value.empty() : format;
}
return _formatOverrides;
}
/*
/**********************************************************************
/* Public API: main-level collection
/**********************************************************************
*/
/**
* Internal method that will collect actual property information.
*
* @since 2.6
*/
protected void collectAll()
{
_potentialCreators = new PotentialCreators();
// First: gather basic accessors
LinkedHashMap props = new LinkedHashMap();
// 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records
// altogether (unless we find a good reason to detect them)
// 17-Apr-2023: Need Records' fields for serialization for cases
// like [databind#3628], [databind#3895] and [databind#3992]
// 22-Jul-2024, tatu: ... and for deserialization sometimes too [databind#4626]
_addFields(props); // note: populates _fieldRenameMappings
_addMethods(props);
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
// inner classes, see [databind#1502]
// 13-May-2023, PJ: Need to avoid adding creators for Records when serializing [databind#3925]
// 18-May-2024, tatu: Serialization side does, however, require access to renaming
// etc (see f.ex [databind#4452]) so let's not skip
if (!_classDef.isNonStaticInnerClass()) { // && !(_forSerialization && isRecord)) {
_addCreators(props);
}
// Remove ignored properties, first; this MUST precede annotation merging
// since logic relies on knowing exactly which accessor has which annotation
_removeUnwantedProperties(props);
// and then remove unneeded accessors (wrt read-only, read-write)
_removeUnwantedAccessors(props);
// Rename remaining properties
_renameProperties(props);
// and now add injectables, but taking care to avoid overlapping ones
// via creator and regular properties
_addInjectables(props);
// then merge annotations, to simplify further processing
// 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of
// annotations from getting properly merged
for (POJOPropertyBuilder property : props.values()) {
property.mergeAnnotations(_forSerialization);
}
// And use custom naming strategy, if applicable...
// 18-Jan-2021, tatu: To be done before trimming, to resolve
// [databind#3368]
PropertyNamingStrategy naming = _findNamingStrategy();
if (naming != null) {
_renameUsing(props, naming);
}
// Sort by visibility (explicit over implicit); drop all but first of member
// type (getter, setter etc) if there is visibility difference
for (POJOPropertyBuilder property : props.values()) {
property.trimByVisibility();
}
// 22-Jul-2024, tatu: And now drop Record Fields once their effect
// (annotations) has been applied. But just for deserialization
if (_isRecordType && !_forSerialization) {
for (POJOPropertyBuilder property : props.values()) {
property.removeFields();
}
}
// and, if required, apply wrapper name: note, MUST be done after
// annotations are merged.
if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
_renameWithWrappers(props);
}
// well, almost last: there's still ordering...
_sortProperties(props);
_properties = props;
_collected = true;
}
/*
/**********************************************************************
/* Property introspection: Fields
/**********************************************************************
*/
/**
* Method for collecting basic information on all fields found
*/
protected void _addFields(Map props)
{
final AnnotationIntrospector ai = _annotationIntrospector;
/* 28-Mar-2013, tatu: For deserialization we may also want to remove
* final fields, as often they won't make very good mutators...
* (although, maybe surprisingly, JVM _can_ force setting of such fields!)
*/
final boolean pruneFinalFields = !_forSerialization && !_config.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS);
final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER);
for (AnnotatedField f : _classDef.fields()) {
// @JsonKey?
if (Boolean.TRUE.equals(ai.hasAsKey(_config, f))) {
if (_jsonKeyAccessors == null) {
_jsonKeyAccessors = new LinkedList<>();
}
_jsonKeyAccessors.add(f);
}
// @JsonValue?
if (Boolean.TRUE.equals(ai.hasAsValue(f))) {
if (_jsonValueAccessors == null) {
_jsonValueAccessors = new LinkedList<>();
}
_jsonValueAccessors.add(f);
continue;
}
// 12-October-2020, dominikrebhan: [databind#1458] Support @JsonAnyGetter on
// fields and allow @JsonAnySetter to be declared as well.
boolean anyGetter = Boolean.TRUE.equals(ai.hasAnyGetter(f));
boolean anySetter = Boolean.TRUE.equals(ai.hasAnySetter(f));
if (anyGetter || anySetter) {
// @JsonAnyGetter?
if (anyGetter) {
if (_anyGetterField == null) {
_anyGetterField = new LinkedList<>();
}
_anyGetterField.add(f);
}
// @JsonAnySetter?
if (anySetter) {
if (_anySetterField == null) {
_anySetterField = new LinkedList<>();
}
_anySetterField.add(f);
}
continue;
}
String implName = ai.findImplicitPropertyName(f);
if (implName == null) {
implName = f.getName();
}
// 27-Aug-2020, tatu: [databind#2800] apply naming strategy for
// fields too, to allow use of naming conventions.
implName = _accessorNaming.modifyFieldName(f, implName);
if (implName == null) {
continue;
}
final PropertyName implNameP = _propNameFromSimple(implName);
// [databind#2527: Field-based renaming can be applied early (here),
// or at a later point, but probably must be done before pruning
// final fields. So let's do it early here
final PropertyName rename = ai.findRenameByField(_config, f, implNameP);
if ((rename != null) && !rename.equals(implNameP)) {
if (_fieldRenameMappings == null) {
_fieldRenameMappings = new HashMap<>();
}
_fieldRenameMappings.put(rename, implNameP);
}
PropertyName pn;
if (_forSerialization) {
// 18-Aug-2011, tatu: As per existing unit tests, we should only
// use serialization annotation (@JsonSerialize) when serializing
// fields, and similarly for deserialize-only annotations... so
// no fallbacks in this particular case.
pn = ai.findNameForSerialization(f);
} else {
pn = ai.findNameForDeserialization(f);
}
boolean hasName = (pn != null);
boolean nameExplicit = hasName;
if (nameExplicit && pn.isEmpty()) { // empty String meaning "use default name", here just means "same as field name"
pn = _propNameFromSimple(implName);
nameExplicit = false;
}
// having explicit name means that field is visible; otherwise need to check the rules
boolean visible = (pn != null);
if (!visible) {
visible = _visibilityChecker.isFieldVisible(f);
}
// and finally, may also have explicit ignoral
boolean ignored = ai.hasIgnoreMarker(f);
// 13-May-2015, tatu: Moved from earlier place (AnnotatedClass) in 2.6
if (f.isTransient()) {
// 20-May-2016, tatu: as per [databind#1184] explicit annotation should override
// "default" `transient`
if (!hasName) {
// 25-Nov-2022, tatu: [databind#3682] Drop transient Fields early;
// only retain if also have ignoral annotations (for name or ignoral)
if (transientAsIgnoral) {
ignored = true;
// 18-Jul-2023, tatu: [databind#3948] Need to retain if there was explicit
// ignoral marker
} else if (!ignored) {
continue;
}
}
}
/* [databind#190]: this is the place to prune final fields, if they are not
* to be used as mutators. Must verify they are not explicitly included.
* Also: if 'ignored' is set, need to include until a later point, to
* avoid losing ignoral information.
*/
if (pruneFinalFields && (pn == null) && !ignored
&& Modifier.isFinal(f.getModifiers())) {
continue;
}
_property(props, implName).addField(f, pn, nameExplicit, visible, ignored);
}
}
/*
/**********************************************************************
/* Property introspection: Creators (constructors, factory methods)
/**********************************************************************
*/
// Completely rewritten in 2.18
protected void _addCreators(Map props)
{
final PotentialCreators creators = _potentialCreators;
// First, resolve explicit annotations for all potential Creators
// (but do NOT filter out DISABLED ones yet!)
List constructors = _collectCreators(_classDef.getConstructors());
List factories = _collectCreators(_classDef.getFactoryMethods());
// Then find what is the Primary Constructor (if one exists for type):
// for Java Records and potentially other types too ("data classes"):
// Needs to be done early to get implicit names populated
final PotentialCreator primary;
if (_isRecordType) {
primary = JDK14Util.findCanonicalRecordConstructor(_config, _classDef, constructors);
} else {
primary = _annotationIntrospector.findDefaultCreator(_config, _classDef,
constructors, factories);
}
// Next: remove creators marked as explicitly disabled
_removeDisabledCreators(constructors);
_removeDisabledCreators(factories);
// And then remove non-annotated static methods that do not look like factories
_removeNonFactoryStaticMethods(factories, primary);
// and use annotations to find explicitly chosen Creators
if (_useAnnotations) { // can't have explicit ones without Annotation introspection
// Start with Constructors as they have higher precedence:
_addExplicitlyAnnotatedCreators(creators, constructors, props, false);
// followed by Factory methods (lower precedence)
_addExplicitlyAnnotatedCreators(creators, factories, props,
creators.hasPropertiesBased());
}
// If no Explicitly annotated creators (or Default one) found, look
// for ones with explicitly-named ({@code @JsonProperty}) parameters
if (!creators.hasPropertiesBased()) {
// only discover constructor Creators?
_addCreatorsWithAnnotatedNames(creators, constructors, primary);
}
// But if no annotation-based Creators found, find/use Primary Creator
// detected earlier, if any
if (primary != null) {
if (!creators.hasPropertiesBased()) {
// ... but only process if still included as a candidate
if (constructors.remove(primary)
|| factories.remove(primary)) {
// But wait! Could be delegating
if (_isDelegatingConstructor(primary)) {
creators.addExplicitDelegating(primary);
} else {
creators.setPropertiesBased(_config, primary, "Primary");
}
}
}
}
// One more thing: if neither explicit (constructor or factory) nor
// canonical (constructor?), consider implicit Constructor with
// all named.
final ConstructorDetector ctorDetector = _config.getConstructorDetector();
if (!creators.hasPropertiesBasedOrDelegating()
&& !ctorDetector.requireCtorAnnotation()) {
// But only if no default constructor available OR if we are configured
// to prefer properties-based Creators
if ((_classDef.getDefaultConstructor() == null)
|| ctorDetector.singleArgCreatorDefaultsToProperties()) {
_addImplicitConstructor(creators, constructors, props);
}
}
// Anything else left, add as possible implicit Creators
// ... but first, trim non-visible
_removeNonVisibleCreators(constructors);
_removeNonVisibleCreators(factories);
creators.setImplicitDelegating(constructors, factories);
// And finally add logical properties for the One Properties-based
// creator selected (if any):
PotentialCreator propsCtor = creators.propertiesBased;
if (propsCtor == null) {
_creatorProperties = Collections.emptyList();
} else {
_creatorProperties = new ArrayList<>();
_addCreatorParams(props, propsCtor, _creatorProperties);
}
}
// Method to determine if given non-explictly-annotated constructor
// looks like delegating one
private boolean _isDelegatingConstructor(PotentialCreator ctor)
{
// First things first: could be
switch (ctor.creatorModeOrDefault()) {
case DELEGATING:
return true;
case DISABLED:
case PROPERTIES:
return false;
default: // case DEFAULT:
}
// Only consider single-arg case, for now
if (ctor.paramCount() == 1) {
// Main thing: @JsonValue makes it delegating:
if ((_jsonValueAccessors != null) && !_jsonValueAccessors.isEmpty()) {
return true;
}
}
return false;
}
private List _collectCreators(List extends AnnotatedWithParams> ctors)
{
if (ctors.isEmpty()) {
return Collections.emptyList();
}
List result = new ArrayList<>();
for (AnnotatedWithParams ctor : ctors) {
JsonCreator.Mode creatorMode = _useAnnotations
? _annotationIntrospector.findCreatorAnnotation(_config, ctor) : null;
// 06-Jul-2024, tatu: Can't yet drop DISABLED ones; add all (for now)
result.add(new PotentialCreator(ctor, creatorMode));
}
return (result == null) ? Collections.emptyList() : result;
}
private void _removeDisabledCreators(List ctors)
{
Iterator it = ctors.iterator();
while (it.hasNext()) {
// explicitly prevented? Remove
if (it.next().creatorMode() == JsonCreator.Mode.DISABLED) {
it.remove();
}
}
}
private void _removeNonVisibleCreators(List ctors)
{
Iterator it = ctors.iterator();
while (it.hasNext()) {
PotentialCreator ctor = it.next();
if (!_visibilityChecker.isCreatorVisible(ctor.creator())) {
it.remove();
}
}
}
private void _removeNonFactoryStaticMethods(List ctors,
PotentialCreator canonical)
{
final Class> rawType = _type.getRawClass();
Iterator it = ctors.iterator();
while (it.hasNext()) {
// explicit mode? Retain (for now)
PotentialCreator ctor = it.next();
if (ctor.isAnnotated()) {
continue;
}
// Do not trim canonical either
if (canonical == ctor) {
continue;
}
// Copied from `BasicBeanDescription.isFactoryMethod()`
AnnotatedWithParams factory = ctor.creator();
if (rawType.isAssignableFrom(factory.getRawType())
&& ctor.paramCount() == 1) {
String name = factory.getName();
if ("valueOf".equals(name)) {
continue;
} else if ("fromString".equals(name)) {
Class> cls = factory.getRawParameterType(0);
if (cls == String.class || CharSequence.class.isAssignableFrom(cls)) {
continue;
}
}
}
it.remove();
}
}
private void _addExplicitlyAnnotatedCreators(PotentialCreators collector,
List ctors,
Map props,
boolean skipPropsBased)
{
final ConstructorDetector ctorDetector = _config.getConstructorDetector();
Iterator it = ctors.iterator();
while (it.hasNext()) {
PotentialCreator ctor = it.next();
// If no explicit annotation, skip for now (may be discovered
// at a later point)
if (!ctor.isAnnotated()) {
continue;
}
it.remove();
boolean isPropsBased;
switch (ctor.creatorMode()) {
case DELEGATING:
isPropsBased = false;
break;
case PROPERTIES:
isPropsBased = true;
break;
case DEFAULT:
default:
isPropsBased = _isExplicitlyAnnotatedCreatorPropsBased(ctor,
props, ctorDetector);
}
if (isPropsBased) {
// Skipping done if we already got higher-precedence Creator
if (!skipPropsBased) {
collector.setPropertiesBased(_config, ctor, "explicit");
}
} else {
collector.addExplicitDelegating(ctor);
}
}
}
private boolean _isExplicitlyAnnotatedCreatorPropsBased(PotentialCreator ctor,
Map props, ConstructorDetector ctorDetector)
{
if (ctor.paramCount() == 1) {
// Is ambiguity/heuristics allowed?
switch (ctorDetector.singleArgMode()) {
case DELEGATING:
return false;
case PROPERTIES:
return true;
case REQUIRE_MODE:
throw new IllegalArgumentException(String.format(
"Single-argument constructor (%s) is annotated but no 'mode' defined; `ConstructorDetector`"
+ "configured with `SingleArgConstructor.REQUIRE_MODE`",
ctor.creator()));
case HEURISTIC:
default:
}
}
// First: if explicit names found, is Properties-based
ctor.introspectParamNames(_config);
if (ctor.hasExplicitNames()) {
return true;
}
// Second: [databind#3180] @JsonValue indicates delegating
if ((_jsonValueAccessors != null) && !_jsonValueAccessors.isEmpty()) {
return false;
}
if (ctor.paramCount() == 1) {
// One more possibility: implicit name that maps to implied
// property with at least one visible accessor
String implName = ctor.implicitNameSimple(0);
if (implName != null) {
POJOPropertyBuilder prop = props.get(implName);
if ((prop != null) && prop.anyVisible() && !prop.anyIgnorals()) {
return true;
}
}
// Second: injectable also suffices
if ((_annotationIntrospector != null)
&& _annotationIntrospector.findInjectableValue(ctor.param(0)) != null) {
return true;
}
return false;
}
// Trickiest case: rely on existence of implicit names and/or injectables
return ctor.hasNameOrInjectForAllParams(_config);
}
private void _addCreatorsWithAnnotatedNames(PotentialCreators collector,
List ctors, PotentialCreator defaultCtor)
{
final List found = _findCreatorsWithAnnotatedNames(ctors);
// 16-Jul-2024, tatu: [databind#4620] If Default Creator found, it
// will be used to resolve candidate to use, if any
if (defaultCtor != null) {
if (found.contains(defaultCtor)) {
collector.setPropertiesBased(_config, defaultCtor, "implicit");
return;
}
}
for (PotentialCreator ctor : found) {
collector.setPropertiesBased(_config, ctor, "implicit");
}
}
private List _findCreatorsWithAnnotatedNames(List ctors)
{
List found = null;
Iterator it = ctors.iterator();
while (it.hasNext()) {
PotentialCreator ctor = it.next();
// Ok: existence of explicit (annotated) names infers properties-based:
ctor.introspectParamNames(_config);
if (!ctor.hasExplicitNames()) {
continue;
}
it.remove();
if (found == null) {
found = new ArrayList<>(4);
}
found.add(ctor);
}
return (found == null) ? Collections.emptyList() : found;
}
private boolean _addImplicitConstructor(PotentialCreators collector,
List ctors, Map props)
{
// Must have one and only one candidate
if (ctors.size() != 1) {
return false;
}
final PotentialCreator ctor = ctors.get(0);
// which needs to be visible
if (!_visibilityChecker.isCreatorVisible(ctor.creator())) {
return false;
}
ctor.introspectParamNames(_config);
// As usual, 1-param case is distinct
if (ctor.paramCount() != 1) {
if (!ctor.hasNameOrInjectForAllParams(_config)) {
return false;
}
} else {
// First things first: if only param has Injectable, must be Props-based
if ((_annotationIntrospector != null)
&& _annotationIntrospector.findInjectableValue(ctor.param(0)) != null) {
// props-based, continue
} else {
// may have explicit preference
final ConstructorDetector ctorDetector = _config.getConstructorDetector();
if (ctorDetector.singleArgCreatorDefaultsToDelegating()) {
return false;
}
// if not, prefer Properties-based if explicit preference OR
// property with same name with at least one visible accessor
if (!ctorDetector.singleArgCreatorDefaultsToProperties()) {
POJOPropertyBuilder prop = props.get(ctor.implicitNameSimple(0));
if ((prop == null) || !prop.anyVisible() || prop.anyIgnorals()) {
return false;
}
}
}
}
ctors.remove(0);
collector.setPropertiesBased(_config, ctor, "implicit");
return true;
}
private void _addCreatorParams(Map props,
PotentialCreator ctor, List creatorProps)
{
final int paramCount = ctor.paramCount();
for (int i = 0; i < paramCount; ++i) {
final AnnotatedParameter param = ctor.param(i);
final PropertyName explName = ctor.explicitName(i);
PropertyName implName = ctor.implicitName(i);
final boolean hasExplicit = (explName != null);
final POJOPropertyBuilder prop;
if (!hasExplicit && (implName == null)) {
// Important: if neither implicit nor explicit name, cannot make use of
// this creator parameter -- may or may not be a problem, verified at a later point.
prop = null;
} else {
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
if (implName != null) {
String n = _checkRenameByField(implName.getSimpleName());
implName = PropertyName.construct(n);
}
prop = (implName == null)
? _property(props, explName) : _property(props, implName);
prop.addCtor(param, hasExplicit ? explName : implName, hasExplicit, true, false);
}
creatorProps.add(prop);
}
ctor.assignPropertyDefs(creatorProps);
}
/*
/**********************************************************************
/* Property introspection: Methods (getters, setters etc)
/**********************************************************************
*/
/**
* Method for collecting basic information on all accessor methods found
*/
protected void _addMethods(Map props)
{
for (AnnotatedMethod m : _classDef.memberMethods()) {
// For methods, handling differs between getters and setters; and
// we will also only consider entries that either follow the bean
// naming convention or are explicitly marked: just being visible
// is not enough (unlike with fields)
int argCount = m.getParameterCount();
if (argCount == 0) { // getters (including 'any getter')
_addGetterMethod(props, m, _annotationIntrospector);
} else if (argCount == 1) { // setters
_addSetterMethod(props, m, _annotationIntrospector);
} else if (argCount == 2) { // any setter?
if (Boolean.TRUE.equals(_annotationIntrospector.hasAnySetter(m))) {
if (_anySetters == null) {
_anySetters = new LinkedList<>();
}
_anySetters.add(m);
}
}
}
}
protected void _addGetterMethod(Map props,
AnnotatedMethod m, AnnotationIntrospector ai)
{
// Very first thing: skip if not returning any value
// 06-May-2020, tatu: [databind#2675] changes handling slightly...
{
final Class> rt = m.getRawReturnType();
if ((rt == Void.TYPE) ||
((rt == Void.class) && !_config.isEnabled(MapperFeature.ALLOW_VOID_VALUED_PROPERTIES))) {
return;
}
}
// any getter?
// @JsonAnyGetter?
if (Boolean.TRUE.equals(ai.hasAnyGetter(m))) {
if (_anyGetters == null) {
_anyGetters = new LinkedList<>();
}
_anyGetters.add(m);
return;
}
// @JsonKey?
if (Boolean.TRUE.equals(ai.hasAsKey(_config, m))) {
if (_jsonKeyAccessors == null) {
_jsonKeyAccessors = new LinkedList<>();
}
_jsonKeyAccessors.add(m);
return;
}
// @JsonValue?
if (Boolean.TRUE.equals(ai.hasAsValue(m))) {
if (_jsonValueAccessors == null) {
_jsonValueAccessors = new LinkedList<>();
}
_jsonValueAccessors.add(m);
return;
}
String implName; // from naming convention
boolean visible;
PropertyName pn = ai.findNameForSerialization(m);
boolean nameExplicit = (pn != null);
if (!nameExplicit) { // no explicit name; must consider implicit
implName = ai.findImplicitPropertyName(m);
if (implName == null) {
implName = _accessorNaming.findNameForRegularGetter(m, m.getName());
}
if (implName == null) { // if not, must skip
implName = _accessorNaming.findNameForIsGetter(m, m.getName());
if (implName == null) {
return;
}
visible = _visibilityChecker.isIsGetterVisible(m);
} else {
visible = _visibilityChecker.isGetterVisible(m);
}
} else { // explicit indication of inclusion, but may be empty
// we still need implicit name to link with other pieces
implName = ai.findImplicitPropertyName(m);
if (implName == null) {
implName = _accessorNaming.findNameForRegularGetter(m, m.getName());
if (implName == null) {
implName = _accessorNaming.findNameForIsGetter(m, m.getName());
}
}
// if not regular getter name, use method name as is
if (implName == null) {
implName = m.getName();
}
if (pn.isEmpty()) {
// !!! TODO: use PropertyName for implicit names too
pn = _propNameFromSimple(implName);
nameExplicit = false;
}
visible = true;
}
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
implName = _checkRenameByField(implName);
boolean ignore = ai.hasIgnoreMarker(m);
_property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore);
}
protected void _addSetterMethod(Map props,
AnnotatedMethod m, AnnotationIntrospector ai)
{
String implName; // from naming convention
boolean visible;
PropertyName pn = ai.findNameForDeserialization(m);
boolean nameExplicit = (pn != null);
if (!nameExplicit) { // no explicit name; must follow naming convention
implName = ai.findImplicitPropertyName(m);
if (implName == null) {
implName = _accessorNaming.findNameForMutator(m, m.getName());
}
if (implName == null) { // if not, must skip
return;
}
visible = _visibilityChecker.isSetterVisible(m);
} else { // explicit indication of inclusion, but may be empty
// we still need implicit name to link with other pieces
implName = ai.findImplicitPropertyName(m);
if (implName == null) {
implName = _accessorNaming.findNameForMutator(m, m.getName());
}
// if not regular getter name, use method name as is
if (implName == null) {
implName = m.getName();
}
if (pn.isEmpty()) {
// !!! TODO: use PropertyName for implicit names too
pn = _propNameFromSimple(implName);
nameExplicit = false;
}
visible = true;
}
// 27-Dec-2019, tatu: [databind#2527] may need to rename according to field
implName = _checkRenameByField(implName);
final boolean ignore = ai.hasIgnoreMarker(m);
_property(props, implName)
.addSetter(m, pn, nameExplicit, visible, ignore);
}
protected void _addInjectables(Map props)
{
// first fields, then methods, to allow overriding
for (AnnotatedField f : _classDef.fields()) {
_doAddInjectable(_annotationIntrospector.findInjectableValue(f), f);
}
for (AnnotatedMethod m : _classDef.memberMethods()) {
// for now, only allow injection of a single arg (to be changed in future?)
if (m.getParameterCount() != 1) {
continue;
}
_doAddInjectable(_annotationIntrospector.findInjectableValue(m), m);
}
}
protected void _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember m)
{
if (injectable == null) {
return;
}
Object id = injectable.getId();
if (_injectables == null) {
_injectables = new LinkedHashMap();
}
AnnotatedMember prev = _injectables.put(id, m);
if (prev != null) {
// 12-Apr-2017, tatu: Let's allow masking of Field by Method
if (prev.getClass() == m.getClass()) {
reportProblem("Duplicate injectable value with id '%s' (of type %s)",
id, ClassUtil.classNameOf(id));
}
}
}
private PropertyName _propNameFromSimple(String simpleName) {
return PropertyName.construct(simpleName, null);
}
// @since 2.11
private String _checkRenameByField(String implName) {
if (_fieldRenameMappings != null) {
PropertyName p = _fieldRenameMappings.get(_propNameFromSimple(implName));
if (p != null) {
implName = p.getSimpleName();
return implName;
}
}
return implName;
}
/*
/**********************************************************
/* Internal methods; removing ignored properties
/**********************************************************
*/
/**
* Method called to get rid of candidate properties that are marked
* as ignored.
*/
protected void _removeUnwantedProperties(Map props)
{
Iterator it = props.values().iterator();
while (it.hasNext()) {
POJOPropertyBuilder prop = it.next();
// First: if nothing visible, just remove altogether
if (!prop.anyVisible()) {
it.remove();
continue;
}
// Otherwise, check ignorals
if (prop.anyIgnorals()) {
// Special handling for Records, as they do not have mutators so relying on constructors
// with (mostly) implicitly-named parameters...
// 20-Jul-2023, tatu: This can be harmful, see f.ex [databind#3992] so
// only use special handling for deserialization
if (isRecordType() && !_forSerialization) {
// ...so can only remove ignored field and/or accessors, not constructor parameters that are needed
// for instantiation...
prop.removeIgnored();
// ...which will then be ignored (the incoming property value) during deserialization
_collectIgnorals(prop.getName());
continue;
}
// first: if one or more ignorals, and no explicit markers, remove the whole thing
// 16-May-2022, tatu: NOTE! As per [databind#3357] need to consider
// only explicit inclusion by accessors OTHER than ones with ignoral marker
if (!prop.anyExplicitsWithoutIgnoral()) {
it.remove();
_collectIgnorals(prop.getName());
continue;
}
// otherwise just remove ones marked to be ignored
prop.removeIgnored();
if (!prop.couldDeserialize()) {
_collectIgnorals(prop.getName());
}
}
}
}
/**
* Method called to further get rid of unwanted individual accessors,
* based on read/write settings and rules for "pulling in" accessors
* (or not).
*/
protected void _removeUnwantedAccessors(Map props)
{
// 15-Jan-2023, tatu: Avoid pulling in mutators for Records; Fields mostly
// since there should not be setters.
// 22-Jul-2024, tatu: Actually do pull them to fix [databind#4630]
final boolean inferMutators = _config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS);
Iterator it = props.values().iterator();
while (it.hasNext()) {
POJOPropertyBuilder prop = it.next();
// 26-Jan-2017, tatu: [databind#935]: need to denote removal of
// 16-May-2020, tatu: [databind#2719]: need to pass `this` to allow
// addition of ignorals wrt explicit name
prop.removeNonVisible(inferMutators, _forSerialization ? null : this);
}
}
/**
* Helper method called to add explicitly ignored properties to a list
* of known ignored properties; this helps in proper reporting of
* errors.
*/
protected void _collectIgnorals(String name)
{
if (!_forSerialization && (name != null)) {
if (_ignoredPropertyNames == null) {
_ignoredPropertyNames = new HashSet<>();
}
_ignoredPropertyNames.add(name);
}
}
/*
/**********************************************************
/* Internal methods; renaming properties
/**********************************************************
*/
protected void _renameProperties(Map props)
{
// With renaming need to do in phases: first, find properties to rename
Iterator> it = props.entrySet().iterator();
LinkedList renamed = null;
while (it.hasNext()) {
Map.Entry entry = it.next();
POJOPropertyBuilder prop = entry.getValue();
Collection l = prop.findExplicitNames();
// no explicit names? Implicit one is fine as is
if (l.isEmpty()) {
continue;
}
it.remove(); // need to replace with one or more renamed
if (renamed == null) {
renamed = new LinkedList<>();
}
// simple renaming? Just do it
if (l.size() == 1) {
PropertyName n = l.iterator().next();
renamed.add(prop.withName(n));
continue;
}
// but this may be problematic...
renamed.addAll(prop.explode(l));
/*
String newName = prop.findNewName();
if (newName != null) {
if (renamed == null) {
renamed = new LinkedList<>();
}
prop = prop.withSimpleName(newName);
renamed.add(prop);
it.remove();
}
*/
}
// and if any were renamed, merge back in...
if (renamed != null) {
for (POJOPropertyBuilder prop : renamed) {
String name = prop.getName();
POJOPropertyBuilder old = props.get(name);
if (old == null) {
props.put(name, prop);
} else {
old.addAll(prop);
}
// replace the creatorProperty too, if there is one
if (_replaceCreatorProperty(_creatorProperties, prop)) {
// [databind#2001]: New name of property was ignored previously? Remove from ignored
// 01-May-2018, tatu: I have a feeling this will need to be revisited at some point,
// to avoid removing some types of removals, possibly. But will do for now.
// 16-May-2020, tatu: ... and so the day came, [databind#2118] failed
// when explicit rename added to ignorals (for READ_ONLY) was suddenly
// removed from ignoral list. So, added a guard statement above so that
// ignoral is ONLY removed if there was matching creator property.
//
// Chances are this is not the last tweak we need but... that bridge then etc
if (_ignoredPropertyNames != null) {
_ignoredPropertyNames.remove(name);
}
}
}
}
}
protected void _renameUsing(Map propMap,
PropertyNamingStrategy naming)
{
// [databind#4409]: Need to skip renaming for Enums, unless Enums are handled as OBJECT format
if (_type.isEnumType()) {
if (getFormatOverrides().getShape() != JsonFormat.Shape.OBJECT) {
return;
}
}
POJOPropertyBuilder[] props = propMap.values().toArray(new POJOPropertyBuilder[propMap.size()]);
propMap.clear();
for (POJOPropertyBuilder prop : props) {
PropertyName fullName = prop.getFullName();
String rename = null;
// As per [databind#428] need to skip renaming if property has
// explicitly defined name, unless feature is enabled
if (!prop.isExplicitlyNamed() || _config.isEnabled(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING)) {
if (_forSerialization) {
if (prop.hasGetter()) {
rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName());
} else if (prop.hasField()) {
rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName());
}
} else {
if (prop.hasSetter()) {
rename = naming.nameForSetterMethod(_config, prop.getSetterUnchecked(), fullName.getSimpleName());
} else if (prop.hasConstructorParameter()) {
rename = naming.nameForConstructorParameter(_config, prop.getConstructorParameter(), fullName.getSimpleName());
} else if (prop.hasField()) {
rename = naming.nameForField(_config, prop.getFieldUnchecked(), fullName.getSimpleName());
} else if (prop.hasGetter()) {
// Plus, when getter-as-setter is used, need to convert that too..
// (should we verify that's enabled? For now, assume it's ok always)
rename = naming.nameForGetterMethod(_config, prop.getGetterUnchecked(), fullName.getSimpleName());
}
}
}
final String simpleName;
if ((rename != null) && !fullName.hasSimpleName(rename)) {
prop = prop.withSimpleName(rename);
simpleName = rename;
} else {
simpleName = fullName.getSimpleName();
}
// Need to consider case where there may already be something in there...
POJOPropertyBuilder old = propMap.get(simpleName);
if (old == null) {
propMap.put(simpleName, prop);
} else {
old.addAll(prop);
}
// replace the creatorProperty too, if there is one
_replaceCreatorProperty(_creatorProperties, prop);
}
}
protected void _renameWithWrappers(Map props)
{
// 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME',
// need another round of renaming...
Iterator> it = props.entrySet().iterator();
LinkedList renamed = null;
while (it.hasNext()) {
Map.Entry entry = it.next();
POJOPropertyBuilder prop = entry.getValue();
AnnotatedMember member = prop.getPrimaryMember();
if (member == null) {
continue;
}
PropertyName wrapperName = _annotationIntrospector.findWrapperName(member);
// One trickier part (wrt [#24] of JAXB annotations: wrapper that
// indicates use of actual property... But hopefully has been taken care
// of previously
if (wrapperName == null || !wrapperName.hasSimpleName()) {
continue;
}
if (!wrapperName.equals(prop.getFullName())) {
if (renamed == null) {
renamed = new LinkedList<>();
}
prop = prop.withName(wrapperName);
renamed.add(prop);
it.remove();
}
}
// and if any were renamed, merge back in...
if (renamed != null) {
for (POJOPropertyBuilder prop : renamed) {
String name = prop.getName();
POJOPropertyBuilder old = props.get(name);
if (old == null) {
props.put(name, prop);
} else {
old.addAll(prop);
}
}
}
}
/*
/**********************************************************
/* Internal methods, sorting
/**********************************************************
*/
// First, order by(explicit ordering and/or alphabetic),
// then by (optional) index (if any)
// and then implicitly order creator properties before others)
protected void _sortProperties(Map props)
{
// Then how about explicit ordering?
final AnnotationIntrospector intr = _annotationIntrospector;
Boolean alpha = intr.findSerializationSortAlphabetically(_classDef);
final boolean sortAlpha = (alpha == null)
? _config.shouldSortPropertiesAlphabetically()
: alpha.booleanValue();
final boolean indexed = _anyIndexed(props.values());
String[] propertyOrder = intr.findSerializationPropertyOrder(_classDef);
// no sorting? no need to shuffle, then
if (!sortAlpha && !indexed && (_creatorProperties == null) && (propertyOrder == null)) {
return;
}
int size = props.size();
Map all;
// Need to (re)sort alphabetically?
if (sortAlpha) {
all = new TreeMap();
} else {
all = new LinkedHashMap(size+size);
}
for (POJOPropertyBuilder prop : props.values()) {
all.put(prop.getName(), prop);
}
Map ordered = new LinkedHashMap<>(size+size);
// Ok: primarily by explicit order
if (propertyOrder != null) {
for (String name : propertyOrder) {
POJOPropertyBuilder w = all.remove(name);
if (w == null) { // will also allow use of "implicit" names for sorting
for (POJOPropertyBuilder prop : props.values()) {
if (name.equals(prop.getInternalName())) {
w = prop;
// plus re-map to external name, to avoid dups:
name = prop.getName();
break;
}
}
}
if (w != null) {
ordered.put(name, w);
}
}
}
// Second (starting with 2.11): index, if any:
if (indexed) {
Map byIndex = new TreeMap<>();
Iterator> it = all.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
POJOPropertyBuilder prop = entry.getValue();
Integer index = prop.getMetadata().getIndex();
if (index != null) {
byIndex.put(index, prop);
it.remove();
}
}
for (POJOPropertyBuilder prop : byIndex.values()) {
ordered.put(prop.getName(), prop);
}
}
// Third by sorting Creator properties before other unordered properties
// (unless strict ordering is requested)
if ((_creatorProperties != null)
&& (!sortAlpha || _config.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST))) {
/* As per [databind#311], this is bit delicate; but if alphabetic ordering
* is mandated, at least ensure creator properties are in alphabetic
* order. Related question of creator vs non-creator is punted for now,
* so creator properties still fully predate non-creator ones.
*/
Collection cr;
// 18-Jun-2024, tatu: [databind#4580] We may want to retain declaration
// order regardless
boolean sortCreatorPropsByAlpha = sortAlpha
&& !_config.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_BY_DECLARATION_ORDER);
if (sortCreatorPropsByAlpha) {
TreeMap sorted =
new TreeMap();
for (POJOPropertyBuilder prop : _creatorProperties) {
if (prop != null) {
sorted.put(prop.getName(), prop);
}
}
cr = sorted.values();
} else {
cr = _creatorProperties;
}
for (POJOPropertyBuilder prop : cr) {
if (prop == null) {
continue;
}
// 16-Jan-2016, tatu: Related to [databind#1317], make sure not to accidentally
// add back pruned creator properties!
String name = prop.getName();
// 27-Nov-2019, tatu: Not sure why, but we should NOT remove it from `all` tho:
// if (all.remove(name) != null) {
if (all.containsKey(name)) {
ordered.put(name, prop);
}
}
}
// And finally whatever is left (trying to put again will not change ordering)
ordered.putAll(all);
props.clear();
props.putAll(ordered);
}
private boolean _anyIndexed(Collection props) {
for (POJOPropertyBuilder prop : props) {
if (prop.getMetadata().hasIndex()) {
return true;
}
}
return false;
}
/*
/**********************************************************
/* Internal methods, conflict resolution
/**********************************************************
*/
/**
* Method that will be given a {@link List} with 2 or more accessors
* that may be in conflict: it will need to remove lower-priority accessors
* to leave just a single highest-priority accessor to use.
* If this succeeds method returns {@code true}, otherwise {@code false}.
*
* NOTE: method will directly modify given {@code List} directly, regardless
* of whether it ultimately succeeds or not.
*
* @return True if seeming conflict was resolved and there only remains
* single accessor
*/
protected boolean _resolveFieldVsGetter(List accessors) {
do {
AnnotatedMember acc1 = accessors.get(0);
AnnotatedMember acc2 = accessors.get(1);
if (acc1 instanceof AnnotatedField) {
if (acc2 instanceof AnnotatedMethod) {
// Method has precedence, remove first entry
accessors.remove(0);
continue;
}
} else if (acc1 instanceof AnnotatedMethod) {
// Method has precedence, remove second entry
if (acc2 instanceof AnnotatedField) {
accessors.remove(1);
continue;
}
}
// Not a field/method pair; fail
return false;
} while (accessors.size() > 1);
return true;
}
/*
/**********************************************************
/* Internal methods; helpers
/**********************************************************
*/
protected void reportProblem(String msg, Object... args) {
if (args.length > 0) {
msg = String.format(msg, args);
}
throw new IllegalArgumentException("Problem with definition of "+_classDef+": "+msg);
}
protected POJOPropertyBuilder _property(Map props,
PropertyName name) {
String simpleName = name.getSimpleName();
POJOPropertyBuilder prop = props.get(simpleName);
if (prop == null) {
prop = new POJOPropertyBuilder(_config, _annotationIntrospector,
_forSerialization, name);
props.put(simpleName, prop);
}
return prop;
}
// !!! TODO: deprecate, require use of PropertyName
protected POJOPropertyBuilder _property(Map props,
String implName)
{
POJOPropertyBuilder prop = props.get(implName);
if (prop == null) {
prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization,
PropertyName.construct(implName));
props.put(implName, prop);
}
return prop;
}
private PropertyNamingStrategy _findNamingStrategy()
{
Object namingDef = _annotationIntrospector.findNamingStrategy(_classDef);
if (namingDef == null) {
return _config.getPropertyNamingStrategy();
}
if (namingDef instanceof PropertyNamingStrategy) {
return (PropertyNamingStrategy) namingDef;
}
// Alas, there's no way to force return type of "either class
// X or Y" -- need to throw an exception after the fact
if (!(namingDef instanceof Class)) {
reportProblem("AnnotationIntrospector returned PropertyNamingStrategy definition of type %s"
+ "; expected type `PropertyNamingStrategy` or `Class` instead",
ClassUtil.classNameOf(namingDef));
}
Class> namingClass = (Class>)namingDef;
// 09-Nov-2015, tatu: Need to consider pseudo-value of STD, which means "use default"
if (namingClass == PropertyNamingStrategy.class) {
return null;
}
if (!PropertyNamingStrategy.class.isAssignableFrom(namingClass)) {
reportProblem("AnnotationIntrospector returned Class %s; expected `Class`",
ClassUtil.classNameOf(namingClass));
}
HandlerInstantiator hi = _config.getHandlerInstantiator();
if (hi != null) {
PropertyNamingStrategy pns = hi.namingStrategyInstance(_config, _classDef, namingClass);
if (pns != null) {
return pns;
}
}
return (PropertyNamingStrategy) ClassUtil.createInstance(namingClass,
_config.canOverrideAccessModifiers());
}
// Method called to make sure secondary _creatorProperties entries are updated
// when main properties are recreated (for some renaming, cleaving)
protected boolean _replaceCreatorProperty(List creatorProperties,
POJOPropertyBuilder prop)
{
final AnnotatedParameter ctorParam = prop.getConstructorParameter();
if (creatorProperties != null) {
for (int i = 0, len = creatorProperties.size(); i < len; ++i) {
POJOPropertyBuilder cprop = creatorProperties.get(i);
if (cprop != null) {
if (cprop.getConstructorParameter() == ctorParam) {
creatorProperties.set(i, prop);
return true;
}
}
}
}
return false;
}
}