org.codehaus.jackson.map.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 org.codehaus.jackson.map.introspect;
import org.codehaus.jackson.map.BeanPropertyDefinition;
/**
* Helper class used for aggregating information about a single
* potential POJO property.
*
* @since 1.9
*/
public class POJOPropertyBuilder
extends BeanPropertyDefinition
implements Comparable
{
/**
* External name of logical property; may change with
* renaming (by new instance being constructed using
* a new name)
*/
protected final String _name;
/**
* Original internal name, derived from accessor, of this
* property. Will not be changed by renaming.
*/
protected final String _internalName;
protected Node _fields;
protected Node _ctorParameters;
protected Node _getters;
protected Node _setters;
public POJOPropertyBuilder(String internalName)
{
_internalName = internalName;
_name = internalName;
}
public POJOPropertyBuilder(POJOPropertyBuilder src, String newName)
{
_internalName = src._internalName;
_name = newName;
_fields = src._fields;
_ctorParameters = src._ctorParameters;
_getters = src._getters;
_setters = src._setters;
}
/**
* Method for constructing a renamed instance
*/
public POJOPropertyBuilder withName(String newName) {
return 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
/**********************************************************
*/
@Override
public String getName() { return _name; }
@Override
public String getInternalName() { return _internalName; }
@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 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 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;
Node 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;
}
}
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;
Node 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;
Node 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.
*/
Node curr = _ctorParameters;
do {
if (curr.value.getOwner() instanceof AnnotatedConstructor) {
return curr.value;
}
curr = curr.next;
} while (curr != null);
return _ctorParameters.value;
}
/*
/**********************************************************
/* Data aggregation
/**********************************************************
*/
public void addField(AnnotatedField a, String ename, boolean visible, boolean ignored) {
_fields = new Node(a, _fields, ename, visible, ignored);
}
public void addCtor(AnnotatedParameter a, String ename, boolean visible, boolean ignored) {
_ctorParameters = new Node(a, _ctorParameters, ename, visible, ignored);
}
public void addGetter(AnnotatedMethod a, String ename, boolean visible, boolean ignored) {
_getters = new Node(a, _getters, ename, visible, ignored);
}
public void addSetter(AnnotatedMethod a, String ename, boolean visible, boolean ignored) {
_setters = new Node(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 Node merge(Node chain1, Node 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);
}
public void removeNonVisible()
{
/* 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.
*/
_getters = _removeNonVisible(_getters);
_ctorParameters = _removeNonVisible(_ctorParameters);
if (_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 {
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, Node 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 Node _removeIgnored(Node node)
{
if (node == null) {
return node;
}
return node.withoutIgnored();
}
private Node _removeNonVisible(Node node)
{
if (node == null) {
return node;
}
return node.withoutNonVisible();
}
private Node _trimByVisibility(Node node)
{
if (node == null) {
return node;
}
return node.trimByVisibility();
}
/*
/**********************************************************
/* Accessors for aggregate information
/**********************************************************
*/
public boolean anyExplicitNames() {
return _anyExplicitNames(_fields)
|| _anyExplicitNames(_getters)
|| _anyExplicitNames(_setters)
|| _anyExplicitNames(_ctorParameters)
;
}
private boolean _anyExplicitNames(Node 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(Node 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(Node 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()
{
Node 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 Node extends AnnotatedMember> findRenamed(Node extends AnnotatedMember> node,
Node 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 classes
/**********************************************************
*/
/**
* Node used for creating simple linked lists to efficiently store small sets
* of things.
*/
private final static class Node
{
public final T value;
public final Node next;
public final String explicitName;
public final boolean isVisible;
public final boolean isMarkedIgnored;
public Node(T v, Node 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 Node withValue(T newValue)
{
if (newValue == value) {
return this;
}
return new Node(newValue, next, explicitName, isVisible, isMarkedIgnored);
}
public Node withNext(Node newNext) {
if (newNext == next) {
return this;
}
return new Node(value, newNext, explicitName, isVisible, isMarkedIgnored);
}
public Node withoutIgnored()
{
if (isMarkedIgnored) {
return (next == null) ? null : next.withoutIgnored();
}
if (next != null) {
Node newNext = next.withoutIgnored();
if (newNext != next) {
return withNext(newNext);
}
}
return this;
}
public Node withoutNonVisible()
{
Node 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 Node append(Node appendable)
{
if (next == null) {
return withNext(appendable);
}
return withNext(next.append(appendable));
}
public Node trimByVisibility()
{
if (next == null) {
return this;
}
Node 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;
}
}
}