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.groupbyinc.common.jackson.databind.introspect.POJOPropertiesCollector Maven / Gradle / Ivy
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.JsonProperty;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.util.BeanUtil;
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;
/**
* True if introspection is done for serialization (giving
* precedence for serialization annotations), or not (false, deserialization)
*/
protected final boolean _forSerialization;
/**
* @since 2.5
*/
protected final boolean _stdBeanNaming;
/**
* 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;
/**
* Prefix used by auto-detected mutators ("setters"): usually "set",
* but differs for builder objects ("with" by default).
*/
protected final String _mutatorPrefix;
/*
/**********************************************************
/* 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 LinkedList _creatorProperties;
protected LinkedList _anyGetters;
protected LinkedList _anySetters;
protected LinkedList _anySetterField;
/**
* Method(s) marked with 'JsonValue' annotation
*
* NOTE: before 2.9, was `AnnotatedMethod`; with 2.9 allows fields too
*/
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;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected POJOPropertiesCollector(MapperConfig> config, boolean forSerialization,
JavaType type, AnnotatedClass classDef, String mutatorPrefix)
{
_config = config;
_stdBeanNaming = config.isEnabled(MapperFeature.USE_STD_BEAN_NAMING);
_forSerialization = forSerialization;
_type = type;
_classDef = classDef;
_mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix;
if (config.isAnnotationProcessingEnabled()) {
_useAnnotations = true;
_annotationIntrospector = _config.getAnnotationIntrospector();
} else {
_useAnnotations = false;
_annotationIntrospector = AnnotationIntrospector.nopInstance();
}
_visibilityChecker = _config.getDefaultVisibilityChecker(type.getRawClass(),
classDef);
}
/*
/**********************************************************
/* Public API
/**********************************************************
*/
public MapperConfig> getConfig() {
return _config;
}
public JavaType getType() {
return _type;
}
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());
}
public Map getInjectables() {
if (!_collected) {
collectAll();
}
return _injectables;
}
@Deprecated // since 2.9
public AnnotatedMethod getJsonValueMethod() {
AnnotatedMember m = getJsonValueAccessor();
if (m instanceof AnnotatedMethod) {
return (AnnotatedMethod) m;
}
return null;
}
/**
* @since 2.9
*/
public AnnotatedMember getJsonValueAccessor()
{
if (!_collected) {
collectAll();
}
// If @JsonValue defined, must have a single one
if (_jsonValueAccessors != null) {
if (_jsonValueAccessors.size() > 1) {
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;
}
public AnnotatedMember getAnyGetter()
{
if (!_collected) {
collectAll();
}
if (_anyGetters != null) {
if (_anyGetters.size() > 1) {
reportProblem("Multiple 'any-getters' 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 for finding Class to use as POJO builder, if any.
*/
public Class> findPOJOBuilderClass() {
return _annotationIntrospector.findPOJOBuilder(_classDef);
}
// for unit tests:
protected Map getPropertyMap() {
if (!_collected) {
collectAll();
}
return _properties;
}
/*
/**********************************************************
/* Public API: main-level collection
/**********************************************************
*/
/**
* Internal method that will collect actual property information.
*
* @since 2.6
*/
protected void collectAll()
{
LinkedHashMap props = new LinkedHashMap();
// First: gather basic data
_addFields(props);
_addMethods(props);
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
// inner classes, see [databind#1502]
if (!_classDef.isNonStaticInnerClass()) {
_addCreators(props);
}
_addInjectables(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)
_removeUnwantedAccessor(props);
// Rename remaining properties
_renameProperties(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...
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();
}
/* 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;
}
/*
/**********************************************************
/* Overridable internal methods, adding members
/**********************************************************
*/
/**
* 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()) {
String implName = ai.findImplicitPropertyName(f);
// @JsonValue?
if (Boolean.TRUE.equals(ai.hasAsValue(f))) {
if (_jsonValueAccessors == null) {
_jsonValueAccessors = new LinkedList<>();
}
_jsonValueAccessors.add(f);
continue;
}
// @JsonAnySetter?
if (Boolean.TRUE.equals(ai.hasAnySetter(f))) {
if (_anySetterField == null) {
_anySetterField = new LinkedList();
}
_anySetterField.add(f);
continue;
}
if (implName == null) {
implName = f.getName();
}
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) {
visible = false;
if (transientAsIgnoral) {
ignored = true;
}
}
}
/* [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 included 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);
}
}
/**
* Method for collecting basic information on constructor(s) found
*/
protected void _addCreators(Map props)
{
// can be null if annotation processing is disabled...
if (!_useAnnotations) {
return;
}
for (AnnotatedConstructor ctor : _classDef.getConstructors()) {
if (_creatorProperties == null) {
_creatorProperties = new LinkedList();
}
for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) {
_addCreatorParam(props, ctor.getParameter(i));
}
}
for (AnnotatedMethod factory : _classDef.getFactoryMethods()) {
if (_creatorProperties == null) {
_creatorProperties = new LinkedList();
}
for (int i = 0, len = factory.getParameterCount(); i < len; ++i) {
_addCreatorParam(props, factory.getParameter(i));
}
}
}
/**
* @since 2.4
*/
protected void _addCreatorParam(Map props,
AnnotatedParameter param)
{
// JDK 8, paranamer, Scala can give implicit name
String impl = _annotationIntrospector.findImplicitPropertyName(param);
if (impl == null) {
impl = "";
}
PropertyName pn = _annotationIntrospector.findNameForDeserialization(param);
boolean expl = (pn != null && !pn.isEmpty());
if (!expl) {
if (impl.isEmpty()) {
// 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.
return;
}
// Also: if this occurs, there MUST be explicit annotation on creator itself
JsonCreator.Mode creatorMode = _annotationIntrospector.findCreatorAnnotation(_config,
param.getOwner());
if ((creatorMode == null) || (creatorMode == JsonCreator.Mode.DISABLED)) {
return;
}
pn = PropertyName.construct(impl);
}
// shouldn't need to worry about @JsonIgnore, since creators only added
// if so annotated
/* 13-May-2015, tatu: We should try to start with implicit name, similar to how
* fields and methods work; but unlike those, we don't necessarily have
* implicit name to use (pre-Java8 at least). So:
*/
POJOPropertyBuilder prop = (expl && impl.isEmpty())
? _property(props, pn) : _property(props, impl);
prop.addCtor(param, pn, expl, true, false);
_creatorProperties.add(prop);
}
/**
* Method for collecting basic information on all fields found
*/
protected void _addMethods(Map props)
{
final AnnotationIntrospector ai = _annotationIntrospector;
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, ai);
} else if (argCount == 1) { // setters
_addSetterMethod(props, m, ai);
} else if (argCount == 2) { // any getter?
if (ai != null) {
if (Boolean.TRUE.equals(ai.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
if (!m.hasReturnType()) {
return;
}
// any getter?
// @JsonAnyGetter?
if (Boolean.TRUE.equals(ai.hasAnyGetter(m))) {
if (_anyGetters == null) {
_anyGetters = new LinkedList();
}
_anyGetters.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 = BeanUtil.okNameForRegularGetter(m, m.getName(), _stdBeanNaming);
}
if (implName == null) { // if not, must skip
implName = BeanUtil.okNameForIsGetter(m, m.getName(), _stdBeanNaming);
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 = BeanUtil.okNameForGetter(m, _stdBeanNaming);
}
// 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;
}
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 == null) ? null : ai.findNameForDeserialization(m);
boolean nameExplicit = (pn != null);
if (!nameExplicit) { // no explicit name; must follow naming convention
implName = (ai == null) ? null : ai.findImplicitPropertyName(m);
if (implName == null) {
implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming);
}
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 == null) ? null : ai.findImplicitPropertyName(m);
if (implName == null) {
implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming);
}
// 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;
}
boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
_property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore);
}
protected void _addInjectables(Map props)
{
final AnnotationIntrospector ai = _annotationIntrospector;
// first fields, then methods, to allow overriding
for (AnnotatedField f : _classDef.fields()) {
_doAddInjectable(ai.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(ai.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()) {
String type = id.getClass().getName();
throw new IllegalArgumentException("Duplicate injectable value with id '"
+String.valueOf(id)+"' (of type "+type+")");
}
}
}
private PropertyName _propNameFromSimple(String simpleName) {
return PropertyName.construct(simpleName, null);
}
/*
/**********************************************************
/* 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()) {
// first: if one or more ignorals, and no explicit markers, remove the whole thing
if (!prop.isExplicitlyIncluded()) {
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 _removeUnwantedAccessor(Map props)
{
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
JsonProperty.Access acc = prop.removeNonVisible(inferMutators);
if (acc == JsonProperty.Access.READ_ONLY) {
_collectIgnorals(prop.getName());
}
}
}
/**
* Helper method called to add explicitly ignored properties to a list
* of known ignored properties; this helps in proper reporting of
* errors.
*/
private void _collectIgnorals(String name)
{
if (!_forSerialization) {
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
_updateCreatorProperty(prop, _creatorProperties);
// [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.
if (_ignoredPropertyNames != null) {
_ignoredPropertyNames.remove(name);
}
}
}
}
protected void _renameUsing(Map propMap,
PropertyNamingStrategy naming)
{
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.getSetter(), fullName.getSimpleName());
} else if (prop.hasConstructorParameter()) {
rename = naming.nameForConstructorParameter(_config, prop.getConstructorParameter(), fullName.getSimpleName());
} else if (prop.hasField()) {
rename = naming.nameForField(_config, prop.getField(), 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.getGetter(), 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
_updateCreatorProperty(prop, _creatorProperties);
}
}
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);
}
}
}
}
/*
/**********************************************************
/* Overridable internal methods, sorting, other stuff
/**********************************************************
*/
/* First, order by [JACKSON-90] (explicit ordering and/or alphabetic)
* and then for [JACKSON-170] (implicitly order creator properties before others)
*/
protected void _sortProperties(Map props)
{
// Then how about explicit ordering?
AnnotationIntrospector intr = _annotationIntrospector;
Boolean alpha = intr.findSerializationSortAlphabetically((Annotated) _classDef);
boolean sort;
if (alpha == null) {
sort = _config.shouldSortPropertiesAlphabetically();
} else {
sort = alpha.booleanValue();
}
String[] propertyOrder = intr.findSerializationPropertyOrder(_classDef);
// no sorting? no need to shuffle, then
if (!sort && (_creatorProperties == null) && (propertyOrder == null)) {
return;
}
int size = props.size();
Map all;
// Need to (re)sort alphabetically?
if (sort) {
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.get(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);
}
}
}
// And secondly by sorting Creator properties before other unordered properties
if (_creatorProperties != null) {
/* 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;
if (sort) {
TreeMap sorted =
new TreeMap();
for (POJOPropertyBuilder prop : _creatorProperties) {
sorted.put(prop.getName(), prop);
}
cr = sorted.values();
} else {
cr = _creatorProperties;
}
for (POJOPropertyBuilder prop : cr) {
// 16-Jan-2016, tatu: Related to [databind#1317], make sure not to accidentally
// add back pruned creator properties!
String name = prop.getName();
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);
}
/*
/**********************************************************
/* 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)) {
throw new IllegalStateException("AnnotationIntrospector returned PropertyNamingStrategy definition of type "
+namingDef.getClass().getName()+"; expected type PropertyNamingStrategy or Class instead");
}
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)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "
+namingClass.getName()+"; expected Class");
}
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());
}
protected void _updateCreatorProperty(POJOPropertyBuilder prop, List creatorProperties) {
if (creatorProperties != null) {
final String intName = prop.getInternalName();
for (int i = 0, len = creatorProperties.size(); i < len; ++i) {
if (creatorProperties.get(i).getInternalName().equals(intName)) {
creatorProperties.set(i, prop);
break;
}
}
}
}
}