package com.fasterxml.jackson.jr.ob.impl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class POJODefinition
{
private final static int MAX_CACHED = 100;
private final static Prop[] NO_PROPS = new Prop[0];
protected final static ConcurrentHashMap<Class<?>, POJODefinition> DEFS
= new ConcurrentHashMap<Class<?>, POJODefinition>(50, 0.75f, 4);
protected final Class<?> _type;
protected final Prop[] _properties;
public final Constructor<?> defaultCtor;
public final Constructor<?> stringCtor;
public final Constructor<?> longCtor;
public POJODefinition(Class<?> type, Prop[] props,
Constructor<?> defaultCtor0, Constructor<?> stringCtor0, Constructor<?> longCtor0)
{
_type = type;
_properties = props;
defaultCtor = defaultCtor0;
stringCtor = stringCtor0;
longCtor = longCtor0;
}
public static POJODefinition find(Class<?> forType)
{
try {
return _find(forType);
} catch (Exception e) {
throw new IllegalArgumentException(String.format
("Failed to introspect ClassDefinition for type '%s': %s",
forType.getName(), e.getMessage()), e);
}
}
public static POJODefinition _find(Class<?> forType) {
POJODefinition def = DEFS.get(forType);
if (def == null) {
def = _construct(forType);
if (DEFS.size() >= MAX_CACHED) {
DEFS.clear();
}
DEFS.putIfAbsent(forType, def);
}
return def;
}
public Prop[] properties() {
return _properties;
}
private static POJODefinition _construct(Class<?> beanType)
{
Map<String,Prop> propsByName = new TreeMap<String,Prop>();
_introspect(beanType, propsByName);
Constructor<?> defaultCtor = null;
Constructor<?> stringCtor = null;
Constructor<?> longCtor = null;
for (Constructor<?> ctor : beanType.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;
}
}
Prop[] props;
if (propsByName.isEmpty()) {
props = NO_PROPS;
} else {
props = propsByName.values().toArray(NO_PROPS);
}
return new POJODefinition(beanType, props, defaultCtor, stringCtor, longCtor);
}
private static void _introspect(Class<?> currType, Map<String,Prop> props)
{
if (currType == null || currType == Object.class) {
return;
}
_introspect(currType.getSuperclass(), props);
for (Field f : currType.getDeclaredFields()) {
if (!Modifier.isPublic(f.getModifiers())
|| f.isEnumConstant() || f.isSynthetic()) {
continue;
}
_propFrom(props, f.getName()).field = f;
}
for (Method m : currType.getDeclaredMethods()) {
final int flags = m.getModifiers();
if (Modifier.isStatic(flags)
|| m.isSynthetic() || m.isBridge()) {
continue;
}
Class<?> argTypes[] = m.getParameterTypes();
if (argTypes.length == 0) {
if (!Modifier.isPublic(flags)) {
continue;
}
Class<?> resultType = m.getReturnType();
if (resultType == Void.class) {
continue;
}
String name = m.getName();
if (name.startsWith("get")) {
if (name.length() > 3) {
name = decap(name.substring(3));
_propFrom(props, name).getter = m;
}
} else if (name.startsWith("is")) {
if (name.length() > 2) {
name = decap(name.substring(2));
_propFrom(props, name).isGetter = m;
}
}
} else if (argTypes.length == 1) {
String name = m.getName();
if (!name.startsWith("set") || name.length() == 3) {
continue;
}
name = decap(name.substring(3));
_propFrom(props, name).setter = m;
}
}
}
private static Prop _propFrom(Map<String,Prop> props, String name) {
Prop prop = props.get(name);
if (prop == null) {
prop = new Prop(name);
props.put(name, prop);
}
return prop;
}
private 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;
}
public static final class Prop
{
public final String name;
public Method setter, getter, isGetter;
public Field field;
public Prop(String name) {
this.name = name;
}
public boolean hasSetter() {
return (setter != null) || (field != null);
}
}
}