com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
package com.fasterxml.jackson.databind.introspect;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.PropertyMetadata;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.util.BeanUtil;
/**
* Helper class used for aggregating information about a single
* potential POJO property.
*/
public class POJOPropertyBuilder
extends BeanPropertyDefinition
implements Comparable
{
/**
* Whether property is being composed for serialization
* (true) or deserialization (false)
*/
protected final boolean _forSerialization;
protected final AnnotationIntrospector _annotationIntrospector;
/**
* External name of logical property; may change with
* renaming (by new instance being constructed using
* a new name)
*/
protected final PropertyName _name;
/**
* Original internal name, derived from accessor, of this
* property. Will not be changed by renaming.
*/
protected final PropertyName _internalName;
protected Linked _fields;
protected Linked _ctorParameters;
protected Linked _getters;
protected Linked _setters;
public POJOPropertyBuilder(PropertyName internalName,
AnnotationIntrospector annotationIntrospector, boolean forSerialization)
{
_internalName = internalName;
_name = internalName;
_annotationIntrospector = annotationIntrospector;
_forSerialization = forSerialization;
}
@Deprecated // since 2.3
public POJOPropertyBuilder(String simpleInternalName,
AnnotationIntrospector annotationIntrospector, boolean forSerialization)
{
this(new PropertyName(simpleInternalName), annotationIntrospector, forSerialization);
}
public POJOPropertyBuilder(POJOPropertyBuilder src, PropertyName newName)
{
_internalName = src._internalName;
_name = newName;
_annotationIntrospector = src._annotationIntrospector;
_fields = src._fields;
_ctorParameters = src._ctorParameters;
_getters = src._getters;
_setters = src._setters;
_forSerialization = src._forSerialization;
}
/*
/**********************************************************
/* Fluent factory methods
/**********************************************************
*/
@Deprecated // since 2.3
@Override
public POJOPropertyBuilder withName(String newName) {
return withSimpleName(newName);
}
@Override
public POJOPropertyBuilder withName(PropertyName newName) {
return new POJOPropertyBuilder(this, newName);
}
@Override
public POJOPropertyBuilder withSimpleName(String newSimpleName)
{
PropertyName newName = _name.withSimpleName(newSimpleName);
return (newName == _name) ? this : new POJOPropertyBuilder(this, newName);
}
/*
/**********************************************************
/* Comparable implementation: sort alphabetically, except
/* that properties with constructor parameters sorted
/* before other properties
/**********************************************************
*/
@Override
public int compareTo(POJOPropertyBuilder other)
{
// first, if one has ctor params, that should come first:
if (_ctorParameters != null) {
if (other._ctorParameters == null) {
return -1;
}
} else if (other._ctorParameters != null) {
return 1;
}
/* otherwise sort by external name (including sorting of
* ctor parameters)
*/
return getName().compareTo(other.getName());
}
/*
/**********************************************************
/* BeanPropertyDefinition implementation, name/type
/**********************************************************
*/
@Override
public String getName() {
return (_name == null) ? null : _name.getSimpleName();
}
@Override
public PropertyName getFullName() {
return _name;
}
@Override
public String getInternalName() { return _internalName.getSimpleName(); }
@Override
public PropertyName getWrapperName() {
/* 13-Mar-2013, tatu: Accessing via primary member SHOULD work,
* due to annotation merging. However, I have seen some problems
* with this access (for other annotations)... so if this should
* occur, try commenting out full traversal code
*/
AnnotatedMember member = getPrimaryMember();
return (member == null || _annotationIntrospector == null) ? null
: _annotationIntrospector.findWrapperName(member);
/*
return fromMemberAnnotations(new WithMember() {
@Override
public PropertyName withMember(AnnotatedMember member) {
return _annotationIntrospector.findWrapperName(member);
}
});
*/
}
@Override
public boolean isExplicitlyIncluded() {
return _anyExplicitNames(_fields)
|| _anyExplicitNames(_getters)
|| _anyExplicitNames(_setters)
|| _anyExplicitNames(_ctorParameters)
;
}
/*
/**********************************************************
/* BeanPropertyDefinition implementation, accessor access
/**********************************************************
*/
@Override
public boolean hasGetter() { return _getters != null; }
@Override
public boolean hasSetter() { return _setters != null; }
@Override
public boolean hasField() { return _fields != null; }
@Override
public boolean hasConstructorParameter() { return _ctorParameters != null; }
@Override
public boolean couldSerialize() {
return (_getters != null) || (_fields != null);
}
@Override
public AnnotatedMethod getGetter()
{
if (_getters == null) {
return null;
}
// If multiple, verify that they do not conflict...
AnnotatedMethod getter = _getters.value;
Linked next = _getters.next;
for (; next != null; next = next.next) {
/* [JACKSON-255] Allow masking, i.e. report exception only if
* declarations in same class, or there's no inheritance relationship
* (sibling interfaces etc)
*/
AnnotatedMethod nextGetter = next.value;
Class> getterClass = getter.getDeclaringClass();
Class> nextClass = nextGetter.getDeclaringClass();
if (getterClass != nextClass) {
if (getterClass.isAssignableFrom(nextClass)) { // next is more specific
getter = nextGetter;
continue;
}
if (nextClass.isAssignableFrom(getterClass)) { // getter more specific
continue;
}
}
/* [Issue#238]: Also, regular getters have precedence over "is-getters", so
* latter can be skipped to resolve otherwise conflict.
* This is bit ugly as we have to re-process naming (as determination of type
* is not retained), but should work.
*/
boolean thisIsGetter = BeanUtil.okNameForIsGetter(getter, getter.getName()) != null;
boolean nextIsGetter = BeanUtil.okNameForIsGetter(nextGetter, nextGetter.getName()) != null;
if (thisIsGetter != nextIsGetter) {
if (thisIsGetter) {
getter = nextGetter;
}
continue;
}
throw new IllegalArgumentException("Conflicting getter definitions for property \""+getName()+"\": "
+getter.getFullName()+" vs "+nextGetter.getFullName());
}
return getter;
}
@Override
public AnnotatedMethod getSetter()
{
if (_setters == null) {
return null;
}
// If multiple, verify that they do not conflict...
AnnotatedMethod setter = _setters.value;
Linked next = _setters.next;
for (; next != null; next = next.next) {
/* [JACKSON-255] Allow masking, i.e. report exception only if
* declarations in same class, or there's no inheritance relationship
* (sibling interfaces etc)
*/
AnnotatedMethod nextSetter = next.value;
Class> setterClass = setter.getDeclaringClass();
Class> nextClass = nextSetter.getDeclaringClass();
if (setterClass != nextClass) {
if (setterClass.isAssignableFrom(nextClass)) { // next is more specific
setter = nextSetter;
continue;
}
if (nextClass.isAssignableFrom(setterClass)) { // getter more specific
continue;
}
}
throw new IllegalArgumentException("Conflicting setter definitions for property \""+getName()+"\": "
+setter.getFullName()+" vs "+nextSetter.getFullName());
}
return setter;
}
@Override
public AnnotatedField getField()
{
if (_fields == null) {
return null;
}
// If multiple, verify that they do not conflict...
AnnotatedField field = _fields.value;
Linked next = _fields.next;
for (; next != null; next = next.next) {
AnnotatedField nextField = next.value;
Class> fieldClass = field.getDeclaringClass();
Class> nextClass = nextField.getDeclaringClass();
if (fieldClass != nextClass) {
if (fieldClass.isAssignableFrom(nextClass)) { // next is more specific
field = nextField;
continue;
}
if (nextClass.isAssignableFrom(fieldClass)) { // getter more specific
continue;
}
}
throw new IllegalArgumentException("Multiple fields representing property \""+getName()+"\": "
+field.getFullName()+" vs "+nextField.getFullName());
}
return field;
}
@Override
public AnnotatedParameter getConstructorParameter()
{
if (_ctorParameters == null) {
return null;
}
/* Hmmh. Checking for constructor parameters is trickier; for one,
* we must allow creator and factory method annotations.
* If this is the case, constructor parameter has the precedence.
*
* So, for now, just try finding the first constructor parameter;
* if none, first factory method. And don't check for dups, if we must,
* can start checking for them later on.
*/
Linked curr = _ctorParameters;
do {
if (curr.value.getOwner() instanceof AnnotatedConstructor) {
return curr.value;
}
curr = curr.next;
} while (curr != null);
return _ctorParameters.value;
}
@Override
public AnnotatedMember getAccessor()
{
AnnotatedMember m = getGetter();
if (m == null) {
m = getField();
}
return m;
}
@Override
public AnnotatedMember getMutator()
{
AnnotatedMember m = getConstructorParameter();
if (m == null) {
m = getSetter();
if (m == null) {
m = getField();
}
}
return m;
}
@Override
public AnnotatedMember getNonConstructorMutator() {
AnnotatedMember m = getSetter();
if (m == null) {
m = getField();
}
return m;
}
@Override
public AnnotatedMember getPrimaryMember() {
if (_forSerialization) {
return getAccessor();
}
return getMutator();
}
/*
/**********************************************************
/* Implementations of refinement accessors
/**********************************************************
*/
@Override
public Class>[] findViews() {
return fromMemberAnnotations(new WithMember[]>() {
@Override
public Class>[] withMember(AnnotatedMember member) {
return _annotationIntrospector.findViews(member);
}
});
}
@Override
public AnnotationIntrospector.ReferenceProperty findReferenceType() {
return fromMemberAnnotations(new WithMember() {
@Override
public AnnotationIntrospector.ReferenceProperty withMember(AnnotatedMember member) {
return _annotationIntrospector.findReferenceType(member);
}
});
}
@Override
public boolean isTypeId() {
Boolean b = fromMemberAnnotations(new WithMember() {
@Override
public Boolean withMember(AnnotatedMember member) {
return _annotationIntrospector.isTypeId(member);
}
});
return (b != null) && b.booleanValue();
}
@Override
public PropertyMetadata getMetadata() {
final Boolean b = _findRequired();
final String desc = _findDescription();
if (b == null) {
return (desc == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL
: PropertyMetadata.STD_REQUIRED_OR_OPTIONAL.withDescription(desc);
}
return PropertyMetadata.construct(b.booleanValue(), _findDescription());
}
protected Boolean _findRequired() {
Boolean b = fromMemberAnnotations(new WithMember() {
@Override
public Boolean withMember(AnnotatedMember member) {
return _annotationIntrospector.hasRequiredMarker(member);
}
});
return b;
}
protected String _findDescription() {
return fromMemberAnnotations(new WithMember() {
@Override
public String withMember(AnnotatedMember member) {
return _annotationIntrospector.findPropertyDescription(member);
}
});
}
@Override
public ObjectIdInfo findObjectIdInfo() {
return fromMemberAnnotations(new WithMember() {
@Override
public ObjectIdInfo withMember(AnnotatedMember member) {
ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(member);
if (info != null) {
info = _annotationIntrospector.findObjectReferenceInfo(member, info);
}
return info;
}
});
}
/*
/**********************************************************
/* Data aggregation
/**********************************************************
*/
public void addField(AnnotatedField a, String ename, boolean visible, boolean ignored) {
_fields = new Linked(a, _fields, ename, visible, ignored);
}
public void addCtor(AnnotatedParameter a, String ename, boolean visible, boolean ignored) {
_ctorParameters = new Linked(a, _ctorParameters, ename, visible, ignored);
}
public void addGetter(AnnotatedMethod a, String ename, boolean visible, boolean ignored) {
_getters = new Linked(a, _getters, ename, visible, ignored);
}
public void addSetter(AnnotatedMethod a, String ename, boolean visible, boolean ignored) {
_setters = new Linked(a, _setters, ename, visible, ignored);
}
/**
* Method for adding all property members from specified collector into
* this collector.
*/
public void addAll(POJOPropertyBuilder src)
{
_fields = merge(_fields, src._fields);
_ctorParameters = merge(_ctorParameters, src._ctorParameters);
_getters= merge(_getters, src._getters);
_setters = merge(_setters, src._setters);
}
private static Linked merge(Linked chain1, Linked chain2)
{
if (chain1 == null) {
return chain2;
}
if (chain2 == null) {
return chain1;
}
return chain1.append(chain2);
}
/*
/**********************************************************
/* Modifications
/**********************************************************
*/
/**
* Method called to remove all entries that are marked as
* ignored.
*/
public void removeIgnored()
{
_fields = _removeIgnored(_fields);
_getters = _removeIgnored(_getters);
_setters = _removeIgnored(_setters);
_ctorParameters = _removeIgnored(_ctorParameters);
}
/**
* @deprecated Since 2.2, use variant that takes boolean argument
*/
@Deprecated
public void removeNonVisible() {
removeNonVisible(false);
}
public void removeNonVisible(boolean force)
{
/* 21-Aug-2011, tatu: This is tricky part -- if and when allow
* non-visible property elements to be "pulled in" by visible
* counterparts?
* For now, we will only do this to pull in setter or field used
* as setter, if an explicit getter is found.
*/
/*
* 28-Mar-2013, tatu: Also, as per [Issue#195], may force removal
* if inferred properties are NOT supported.
*/
_getters = _removeNonVisible(_getters);
_ctorParameters = _removeNonVisible(_ctorParameters);
if (force || (_getters == null)) {
_fields = _removeNonVisible(_fields);
_setters = _removeNonVisible(_setters);
}
}
/**
* Method called to trim unnecessary entries, such as implicit
* getter if there is an explict one available. This is important
* for later stages, to avoid unnecessary conflicts.
*/
public void trimByVisibility()
{
_fields = _trimByVisibility(_fields);
_getters = _trimByVisibility(_getters);
_setters = _trimByVisibility(_setters);
_ctorParameters = _trimByVisibility(_ctorParameters);
}
@SuppressWarnings("unchecked")
public void mergeAnnotations(boolean forSerialization)
{
if (forSerialization) {
if (_getters != null) {
AnnotationMap ann = _mergeAnnotations(0, _getters, _fields, _ctorParameters, _setters);
_getters = _getters.withValue(_getters.value.withAnnotations(ann));
} else if (_fields != null) {
AnnotationMap ann = _mergeAnnotations(0, _fields, _ctorParameters, _setters);
_fields = _fields.withValue(_fields.value.withAnnotations(ann));
}
} else { // for deserialization
if (_ctorParameters != null) {
AnnotationMap ann = _mergeAnnotations(0, _ctorParameters, _setters, _fields, _getters);
_ctorParameters = _ctorParameters.withValue(_ctorParameters.value.withAnnotations(ann));
} else if (_setters != null) {
AnnotationMap ann = _mergeAnnotations(0, _setters, _fields, _getters);
_setters = _setters.withValue(_setters.value.withAnnotations(ann));
} else if (_fields != null) {
AnnotationMap ann = _mergeAnnotations(0, _fields, _getters);
_fields = _fields.withValue(_fields.value.withAnnotations(ann));
}
}
}
private AnnotationMap _mergeAnnotations(int index, Linked extends AnnotatedMember>... nodes)
{
AnnotationMap ann = nodes[index].value.getAllAnnotations();
++index;
for (; index < nodes.length; ++index) {
if (nodes[index] != null) {
return AnnotationMap.merge(ann, _mergeAnnotations(index, nodes));
}
}
return ann;
}
private Linked _removeIgnored(Linked node)
{
if (node == null) {
return node;
}
return node.withoutIgnored();
}
private Linked _removeNonVisible(Linked node)
{
if (node == null) {
return node;
}
return node.withoutNonVisible();
}
private Linked _trimByVisibility(Linked node)
{
if (node == null) {
return node;
}
return node.trimByVisibility();
}
/*
/**********************************************************
/* Accessors for aggregate information
/**********************************************************
*/
private boolean _anyExplicitNames(Linked n)
{
for (; n != null; n = n.next) {
if (n.explicitName != null && n.explicitName.length() > 0) {
return true;
}
}
return false;
}
public boolean anyVisible() {
return _anyVisible(_fields)
|| _anyVisible(_getters)
|| _anyVisible(_setters)
|| _anyVisible(_ctorParameters)
;
}
private boolean _anyVisible(Linked n)
{
for (; n != null; n = n.next) {
if (n.isVisible) {
return true;
}
}
return false;
}
public boolean anyIgnorals() {
return _anyIgnorals(_fields)
|| _anyIgnorals(_getters)
|| _anyIgnorals(_setters)
|| _anyIgnorals(_ctorParameters)
;
}
private boolean _anyIgnorals(Linked n)
{
for (; n != null; n = n.next) {
if (n.isMarkedIgnored) {
return true;
}
}
return false;
}
/**
* Method called to check whether property represented by this collector
* should be renamed from the implicit name; and also verify that there
* are no conflicting rename definitions.
*/
public String findNewName()
{
Linked extends AnnotatedMember> renamed = null;
renamed = findRenamed(_fields, renamed);
renamed = findRenamed(_getters, renamed);
renamed = findRenamed(_setters, renamed);
renamed = findRenamed(_ctorParameters, renamed);
return (renamed == null) ? null : renamed.explicitName;
}
private Linked extends AnnotatedMember> findRenamed(Linked extends AnnotatedMember> node,
Linked extends AnnotatedMember> renamed)
{
for (; node != null; node = node.next) {
String explName = node.explicitName;
if (explName == null) {
continue;
}
// different from default name?
if (explName.equals(_name)) { // nope, skip
continue;
}
if (renamed == null) {
renamed = node;
} else {
// different from an earlier renaming? problem
if (!explName.equals(renamed.explicitName)) {
throw new IllegalStateException("Conflicting property name definitions: '"
+renamed.explicitName+"' (for "+renamed.value+") vs '"
+node.explicitName+"' (for "+node.value+")");
}
}
}
return renamed;
}
// For trouble-shooting
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("[Property '").append(_name)
.append("'; ctors: ").append(_ctorParameters)
.append(", field(s): ").append(_fields)
.append(", getter(s): ").append(_getters)
.append(", setter(s): ").append(_setters)
;
sb.append("]");
return sb.toString();
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
/**
* Helper method used for finding annotation values, from accessors
* relevant to current usage (deserialization, serialization)
*/
protected T fromMemberAnnotations(WithMember func)
{
T result = null;
if (_annotationIntrospector != null) {
if (_forSerialization) {
if (_getters != null) {
result = func.withMember(_getters.value);
}
} else {
if (_ctorParameters != null) {
result = func.withMember(_ctorParameters.value);
}
if (result == null && _setters != null) {
result = func.withMember(_setters.value);
}
}
if (result == null && _fields != null) {
result = func.withMember(_fields.value);
}
}
return result;
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
private interface WithMember
{
public T withMember(AnnotatedMember member);
}
/**
* Node used for creating simple linked lists to efficiently store small sets
* of things.
*/
private final static class Linked
{
public final T value;
public final Linked next;
public final String explicitName;
public final boolean isVisible;
public final boolean isMarkedIgnored;
public Linked(T v, Linked n,
String explName, boolean visible, boolean ignored)
{
value = v;
next = n;
// ensure that we'll never have missing names
if (explName == null) {
explicitName = null;
} else {
explicitName = (explName.length() == 0) ? null : explName;
}
isVisible = visible;
isMarkedIgnored = ignored;
}
public Linked withValue(T newValue)
{
if (newValue == value) {
return this;
}
return new Linked(newValue, next, explicitName, isVisible, isMarkedIgnored);
}
public Linked withNext(Linked newNext) {
if (newNext == next) {
return this;
}
return new Linked(value, newNext, explicitName, isVisible, isMarkedIgnored);
}
public Linked withoutIgnored()
{
if (isMarkedIgnored) {
return (next == null) ? null : next.withoutIgnored();
}
if (next != null) {
Linked newNext = next.withoutIgnored();
if (newNext != next) {
return withNext(newNext);
}
}
return this;
}
public Linked withoutNonVisible()
{
Linked newNext = (next == null) ? null : next.withoutNonVisible();
return isVisible ? withNext(newNext) : newNext;
}
/**
* Method called to append given node(s) at the end of this
* node chain.
*/
private Linked append(Linked appendable)
{
if (next == null) {
return withNext(appendable);
}
return withNext(next.append(appendable));
}
public Linked trimByVisibility()
{
if (next == null) {
return this;
}
Linked newNext = next.trimByVisibility();
if (explicitName != null) { // this already has highest; how about next one?
if (newNext.explicitName == null) { // next one not, drop it
return withNext(null);
}
// both have it, keep
return withNext(newNext);
}
if (newNext.explicitName != null) { // next one has higher, return it...
return newNext;
}
// neither has explicit name; how about visibility?
if (isVisible == newNext.isVisible) { // same; keep both in current order
return withNext(newNext);
}
return isVisible ? withNext(null) : newNext;
}
@Override
public String toString() {
String msg = value.toString()+"[visible="+isVisible+"]";
if (next != null) {
msg = msg + ", "+next.toString();
}
return msg;
}
}
}