package com.fasterxml.jackson.jr.annotationsupport;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;
import com.fasterxml.jackson.jr.ob.impl.POJODefinition;
public class AnnotationBasedIntrospector
{
protected final Class<?> _type;
protected final boolean _forSerialization;
protected final JsonAutoDetect.Value _visibility;
protected final Map<String, APropBuilder> _props = new HashMap<String, APropBuilder>();
protected Set<String> _ignorableNames;
protected AnnotationBasedIntrospector(Class<?> type, boolean serialization,
JsonAutoDetect.Value visibility) {
_type = type;
_forSerialization = serialization;
_ignorableNames = serialization ? null : new HashSet<String>();
JsonAutoDetect ann = _find(type, JsonAutoDetect.class);
if (ann == null) {
_visibility = visibility;
} else {
_visibility = visibility.withOverrides(JsonAutoDetect.Value.from(ann));
}
}
public static POJODefinition pojoDefinitionForDeserialization(JSONReader r,
Class<?> pojoType, JsonAutoDetect.Value visibility) {
return new AnnotationBasedIntrospector(pojoType, false, visibility)
.introspectDefinition();
}
public static POJODefinition pojoDefinitionForSerialization(JSONWriter w,
Class<?> pojoType, JsonAutoDetect.Value visibility) {
return new AnnotationBasedIntrospector(pojoType, true, visibility)
.introspectDefinition();
}
protected POJODefinition introspectDefinition()
{
_findFields();
_findMethods();
Constructor<?> defaultCtor = null;
Constructor<?> stringCtor = null;
Constructor<?> longCtor = null;
if (!_forSerialization) {
for (Constructor<?> ctor : _type.getDeclaredConstructors()) {
Class<?>[] argTypes = ctor.getParameterTypes();
if (argTypes.length == 0) {
defaultCtor = ctor;
} else if (argTypes.length == 1) {
Class<?> argType = argTypes[0];
if (argType == String.class) {
stringCtor = ctor;
} else if (argType == Long.class || argType == Long.TYPE) {
longCtor = ctor;
} else {
continue;
}
} else {
continue;
}
}
}
POJODefinition def = new POJODefinition(_type,
_pruneProperties(_forSerialization),
defaultCtor, stringCtor, longCtor);
if (_ignorableNames != null) {
def = def.withIgnorals(_ignorableNames);
}
return def;
}
protected POJODefinition.Prop[] _pruneProperties(boolean sortProperties)
{
List<APropBuilder> renamed = null;
Iterator<APropBuilder> it = _props.values().iterator();
while (it.hasNext()) {
final APropBuilder prop = it.next();
if (prop.anyIgnorals()) {
if (!prop.anyExplicit()) {
it.remove();
_addIgnoral(prop.name);
} else {
prop.removeIgnored();
if (!prop.couldDeserialize()) {
_addIgnoral(prop.name);
}
}
continue;
}
if (!prop.anyVisible()) {
it.remove();
continue;
}
prop.removeNonVisible();
String explName = prop.findPrimaryExplicitName(_forSerialization);
if (explName != null) {
it.remove();
if (renamed == null) {
renamed = new LinkedList<APropBuilder>();
}
renamed.add(prop.withName(explName));
}
}
if (renamed != null) {
for (APropBuilder prop : renamed) {
APropBuilder orig = _props.get(prop.name);
if (orig == null) {
_props.put(prop.name, prop);
continue;
}
APropBuilder merged = APropBuilder.merge(orig, prop);
_props.put(prop.name, merged);
}
}
final Collection<String> ignorableNames = _findIgnorableNames();
if (!ignorableNames.isEmpty()) {
if (_ignorableNames != null) {
_ignorableNames.addAll(ignorableNames);
}
for (String ignorableName : ignorableNames) {
_findAndRemoveByName(ignorableName);
}
}
final int propCount = _props.size();
final POJODefinition.Prop[] result = new POJODefinition.Prop[propCount];
int i = 0;
final boolean collectAliases = !_forSerialization;
if (sortProperties) {
List<String> nameOrder = _findNameSortOrder();
if (!nameOrder.isEmpty()) {
for (String name : nameOrder) {
APropBuilder prop = _findAndRemoveByName(name);
if (prop != null) {
result[i++] = prop.asProperty(collectAliases);
}
}
}
TreeMap<String, APropBuilder> sorted = new TreeMap<String, APropBuilder>(_props);
for (APropBuilder prop : sorted.values()) {
result[i++] = prop.asProperty(collectAliases);
}
} else {
for (APropBuilder prop : _props.values()) {
result[i++] = prop.asProperty(collectAliases);
}
}
return result;
}
protected void _findFields() {
_findFields(_type);
}
protected void _findFields(final Class<?> currType)
{
if (currType == null || currType == Object.class) {
return;
}
_findFields(currType.getSuperclass());
for (Field f : currType.getDeclaredFields()) {
if (f.isEnumConstant() || f.isSynthetic()) {
continue;
}
final String implName = f.getName();
APropAccessor<Field> acc;
if (Boolean.TRUE.equals(_hasIgnoreMarker(f))) {
acc = APropAccessor.createIgnorable(implName, f);
} else {
final String explName = _findExplicitName(f);
if (explName != null) {
if (explName.isEmpty()) {
acc = APropAccessor.createVisible(implName, f);
} else {
acc = APropAccessor.createExplicit(explName, f);
}
} else {
acc = APropAccessor.createImplicit(explName, f,
_isFieldVisible(f));
}
}
_propBuilder(implName).field = acc;
}
}
protected void _findMethods() {
_findMethods(_type);
}
protected void _findMethods(final Class<?> currType)
{
if (currType == null || currType == Object.class) {
return;
}
_findMethods(currType.getSuperclass());
for (Method m : currType.getDeclaredMethods()) {
final int flags = m.getModifiers();
if (Modifier.isStatic(flags)
|| m.isSynthetic() || m.isBridge()) {
continue;
}
int argCount = m.getParameterCount();
if (argCount == 0) {
_checkGetterMethod(m);
} else if (argCount == 1) {
_checkSetterMethod(m);
}
}
}
protected void _checkGetterMethod(Method m)
{
Class<?> resultType = m.getReturnType();
if (resultType == Void.class) {
return;
}
final String name0 = m.getName();
String implName = null;
boolean isIsGetter = false;
if (name0.startsWith("get")) {
if (name0.length() > 3) {
implName = _decap(name0.substring(3));
}
} else if (name0.startsWith("is")) {
if (name0.length() > 2) {
implName = _decap(name0.substring(2));
isIsGetter = true;
}
}
APropAccessor<Method> acc;
if (implName == null) {
final String explName = _findExplicitName(m);
if (explName == null) {
return;
}
implName = name0;
if (Boolean.TRUE.equals(_hasIgnoreMarker(m))) {
acc = APropAccessor.createIgnorable(implName, m);
} else {
if (explName.isEmpty()) {
acc = APropAccessor.createVisible(implName, m);
} else {
acc = APropAccessor.createExplicit(explName, m);
}
}
} else {
if (Boolean.TRUE.equals(_hasIgnoreMarker(m))) {
acc = APropAccessor.createIgnorable(implName, m);
} else {
final String explName = _findExplicitName(m);
if (explName == null) {
acc = APropAccessor.createImplicit(implName, m,
_isGetterVisible(m, isIsGetter));
} else if (explName.isEmpty()) {
acc = APropAccessor.createVisible(implName, m);
} else {
acc = APropAccessor.createExplicit(explName, m);
}
}
}
_propBuilder(implName).getter = acc;
}
protected void _checkSetterMethod(Method m)
{
final String name0 = m.getName();
String implName;
if (name0.startsWith("set") && (name0.length() > 3)) {
implName = _decap(name0.substring(3));
} else {
implName = null;
}
APropAccessor<Method> acc;
if (implName == null) {
final String explName = _findExplicitName(m);
if (explName == null) {
return;
}
implName = name0;
if (Boolean.TRUE.equals(_hasIgnoreMarker(m))) {
acc = APropAccessor.createIgnorable(implName, m);
} else {
if (explName.isEmpty()) {
acc = APropAccessor.createVisible(implName, m);
} else {
acc = APropAccessor.createExplicit(explName, m);
}
}
} else {
if (Boolean.TRUE.equals(_hasIgnoreMarker(m))) {
acc = APropAccessor.createIgnorable(implName, m);
} else {
final String explName = _findExplicitName(m);
if (explName == null) {
acc = APropAccessor.createImplicit(implName, m,
_isSetterVisible(m));
} else if (explName.isEmpty()) {
acc = APropAccessor.createVisible(implName, m);
} else {
acc = APropAccessor.createExplicit(explName, m);
}
}
}
_propBuilder(implName).setter = acc;
}
protected boolean _isFieldVisible(Field f) {
return !Modifier.isTransient(f.getModifiers())
&& _visibility.getFieldVisibility().isVisible(f);
}
protected boolean _isGetterVisible(Method m, boolean isIsGetter) {
if (isIsGetter) {
return _visibility.getIsGetterVisibility().isVisible(m);
}
return _visibility.getGetterVisibility().isVisible(m);
}
protected boolean _isSetterVisible(Method m) {
return _visibility.getSetterVisibility().isVisible(m);
}
protected Boolean (AnnotatedElement m) {
JsonIgnore ann = _find(m, JsonIgnore.class);
return (ann != null) && ann.value();
}
protected String _findExplicitName(AnnotatedElement m) {
JsonProperty ann = _find(m, JsonProperty.class);
return (ann == null) ? null : ann.value();
}
protected List<String> _findNameSortOrder() {
JsonPropertyOrder ann = _find(_type, JsonPropertyOrder.class);
if (ann == null) {
return Collections.emptyList();
}
return Arrays.asList(ann.value());
}
protected Collection<String> _findIgnorableNames() {
JsonIgnoreProperties ann = _find(_type, JsonIgnoreProperties.class);
if (ann == null) {
return Collections.emptyList();
}
return Arrays.asList(ann.value());
}
protected <ANN extends Annotation> ANN _find(AnnotatedElement elem, Class<ANN> annotationType) {
return elem.getAnnotation(annotationType);
}
protected APropBuilder _propBuilder(String name) {
APropBuilder b = _props.get(name);
if (b == null) {
b = new APropBuilder(name);
_props.put(name, b);
}
return b;
}
protected void _addIgnoral(String name) {
if (_ignorableNames != null) {
_ignorableNames.add(name);
}
}
protected APropBuilder _findAndRemoveByName(String name) {
APropBuilder prop = _props.remove(name);
if (prop == null) {
for (APropBuilder p2 : _props.values()) {
prop = _props.remove(p2.origName);
if (prop != null) {
break;
}
}
}
return prop;
}
protected static String _decap(String name) {
char c = name.charAt(0);
char lowerC = Character.toLowerCase(c);
if (c != lowerC) {
if ((name.length() == 1)
|| !Character.isUpperCase(name.charAt(1))) {
char chars[] = name.toCharArray();
chars[0] = lowerC;
return new String(chars);
}
}
return name;
}
protected static class APropBuilder
implements Comparable<APropBuilder>
{
public final String origName;
public final String name;
protected APropAccessor<Field> field;
protected APropAccessor<Method> getter;
protected APropAccessor<Method> setter;
public APropBuilder(String n) {
origName = n;
name = n;
}
protected APropBuilder(APropBuilder base, String n) {
origName = base.origName;
name = n;
}
public POJODefinition.Prop asProperty(boolean collectAliases) {
Set<String> aliases = collectAliases ? collectAliases() : null;
return new POJODefinition.Prop(name,
(field == null) ? null : field.accessor,
(setter == null) ? null : setter.accessor,
(getter == null) ? null : getter.accessor,
null,
aliases);
}
public static APropBuilder merge(APropBuilder b1, APropBuilder b2) {
APropBuilder newB = new APropBuilder(b1.name);
newB.field = _merge(b1.field, b2.field);
newB.getter = _merge(b1.getter, b2.getter);
newB.setter = _merge(b1.setter, b2.setter);
return newB;
}
private static <A extends AccessibleObject> APropAccessor<A> _merge(APropAccessor<A> a1, APropAccessor<A> a2)
{
if (a1 == null) {
return a2;
}
if (a2 == null) {
return a1;
}
if (a1.isNameExplicit) {
return a1;
}
if (a2.isNameExplicit) {
return a2;
}
if (a1.isExplicit) {
return a1;
}
if (a2.isExplicit) {
return a2;
}
return a1;
}
public APropBuilder withName(String newName) {
APropBuilder newB = new APropBuilder(this, newName);
newB.field = field;
newB.getter = getter;
newB.setter = setter;
return newB;
}
public void removeIgnored() {
if ((field != null) && field.isToIgnore) {
field = null;
}
if ((getter != null) && getter.isToIgnore) {
getter = null;
}
if ((setter != null) && setter.isToIgnore) {
setter = null;
}
}
public void removeNonVisible() {
if ((field != null) && !field.isVisible) {
field = null;
}
if ((getter != null) && !getter.isVisible) {
getter = null;
}
if ((setter != null) && !setter.isVisible) {
setter = null;
}
}
public Set<String> collectAliases() {
Set<String> collectedAliases = null;
collectedAliases = _collectAliases(field, collectedAliases);
collectedAliases = _collectAliases(getter, collectedAliases);
collectedAliases = _collectAliases(setter, collectedAliases);
return collectedAliases;
}
private static Set<String> _collectAliases(APropAccessor<?> acc, Set<String> collectedAliases) {
if (acc != null) {
AnnotatedElement accOb = acc.accessor;
if (accOb != null) {
JsonAlias ann = accOb.getAnnotation(JsonAlias.class);
if (ann != null) {
final String[] names = ann.value();
if (collectedAliases == null) {
collectedAliases = new HashSet<String>();
}
for (String alias : names) {
collectedAliases.add(alias);
}
}
}
}
return collectedAliases;
}
private String _firstExplicit(APropAccessor<?> acc1,
APropAccessor<?> acc2,
APropAccessor<?> acc3) {
if (acc1 != null && acc1.isNameExplicit) {
return acc1.name;
}
if (acc2 != null && acc2.isNameExplicit) {
return acc2.name;
}
if (acc3 != null && acc3.isNameExplicit) {
return acc3.name;
}
return null;
}
public String findPrimaryExplicitName(boolean forSer) {
if (forSer) {
return _firstExplicit(getter, setter, field);
}
return _firstExplicit(setter, getter, field);
}
public boolean anyVisible() {
return ((field != null) && field.isVisible)
|| ((getter != null) && getter.isVisible)
|| ((setter != null) && setter.isVisible);
}
public boolean anyExplicit() {
return ((field != null) && field.isExplicit)
|| ((getter != null) && getter.isExplicit)
|| ((setter != null) && setter.isExplicit);
}
public boolean anyIgnorals() {
return ((field != null) && field.isToIgnore)
|| ((getter != null) && getter.isToIgnore)
|| ((setter != null) && setter.isToIgnore);
}
public boolean couldDeserialize() {
return (field != null) || (setter != null);
}
@Override
public int compareTo(APropBuilder o) {
return name.compareTo(o.name);
}
}
protected static class APropAccessor<ACC extends AccessibleObject> {
public final String name;
public final ACC accessor;
public final boolean isExplicit, isNameExplicit;
public final boolean isToIgnore, isVisible;
protected APropAccessor(String n, ACC acc,
boolean expl, boolean nameExpl,
boolean ignore, boolean visible)
{
name = n;
accessor = acc;
isExplicit = expl;
isNameExplicit = nameExpl;
isToIgnore = ignore;
isVisible = visible;
}
public static <T extends AccessibleObject> APropAccessor<T> createIgnorable(String name, T accessor) {
return new APropAccessor<T>(name, accessor,
false, false, true, false);
}
public static <T extends AccessibleObject> APropAccessor<T> createImplicit(String name, T accessor,
boolean visible) {
return new APropAccessor<T>(name, accessor,
false, false, false, visible);
}
public static <T extends AccessibleObject> APropAccessor<T> createVisible(String name, T accessor) {
return new APropAccessor<T>(name, accessor,
true, false, false, true);
}
public static <T extends AccessibleObject> APropAccessor<T> createExplicit(String name, T accessor) {
return new APropAccessor<T>(name, accessor,
true, true, false, true);
}
}
}