package com.fasterxml.jackson.jr.ob.impl;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.fasterxml.jackson.jr.ob.JSON;
import com.fasterxml.jackson.jr.ob.api.ReaderWriterModifier;
import com.fasterxml.jackson.jr.ob.api.ReaderWriterProvider;
import com.fasterxml.jackson.jr.ob.api.ValueReader;
import com.fasterxml.jackson.jr.type.ResolvedType;
import com.fasterxml.jackson.jr.type.TypeBindings;
import com.fasterxml.jackson.jr.type.TypeResolver;
public class ValueReaderLocator
extends ValueLocatorBase
{
protected final static int MAX_CACHED_READERS = 500;
protected final TypeResolver _typeResolver;
protected final ReaderWriterProvider _readerProvider;
protected final ReaderWriterModifier _readerModifier;
protected final ConcurrentHashMap<ClassKey, ValueReader> _knownReaders;
protected Map<ClassKey, ValueReader> _incompleteReaders;
protected final Object _readerLock;
protected final int _features;
protected final JSONReader _readContext;
private ClassKey _key;
protected ValueReaderLocator(ReaderWriterProvider rwp, ReaderWriterModifier rwm)
{
_features = 0;
_readerProvider = rwp;
_readerModifier = rwm;
_knownReaders = new ConcurrentHashMap<ClassKey, ValueReader>(10, 0.75f, 2);
_typeResolver = new TypeResolver();
_readerLock = new Object();
_readContext = null;
}
protected ValueReaderLocator(ValueReaderLocator base, int features,
JSONReader r) {
_features = features;
_readContext = r;
_readerProvider = base._readerProvider;
_readerModifier = base._readerModifier;
_knownReaders = base._knownReaders;
_typeResolver = base._typeResolver;
_readerLock = base._readerLock;
}
protected ValueReaderLocator(ValueReaderLocator base,
ReaderWriterProvider rwp, ReaderWriterModifier rwm)
{
_knownReaders = new ConcurrentHashMap<ClassKey, ValueReader>(10, 0.75f, 2);
_readerLock = new Object();
_features = base._features;
_readContext = base._readContext;
_readerProvider = rwp;
_readerModifier = rwm;
_typeResolver = base._typeResolver;
}
public final static ValueReaderLocator blueprint(ReaderWriterProvider rwp, ReaderWriterModifier rwm) {
return new ValueReaderLocator(rwp, rwm);
}
public ValueReaderLocator with(ReaderWriterProvider rwp) {
if (rwp == _readerProvider) {
return this;
}
return new ValueReaderLocator(this, rwp, _readerModifier);
}
public ValueReaderLocator with(ReaderWriterModifier rwm) {
if (rwm == _readerModifier) {
return this;
}
return new ValueReaderLocator(this, _readerProvider, rwm);
}
public ValueReaderLocator perOperationInstance(JSONReader r, int features) {
return new ValueReaderLocator(this, features & CACHE_FLAGS, r);
}
public ReaderWriterProvider readerWriterProvider() { return _readerProvider; }
public ReaderWriterModifier readerWriterModifier() { return _readerModifier; }
public ValueReader findReader(Class<?> raw)
{
ClassKey k = (_key == null) ? new ClassKey(raw, _features) : _key.with(raw, _features);
ValueReader vr = _knownReaders.get(k);
if (vr != null) {
return vr;
}
vr = createReader(null, raw, raw);
if (_knownReaders.size() >= MAX_CACHED_READERS) {
_knownReaders.clear();
}
_knownReaders.putIfAbsent(new ClassKey(raw, _features), vr);
return vr;
}
protected ValueReader createReader(Class<?> contextType, Class<?> type, Type genericType)
{
ValueReader r = _createReader(contextType, type, genericType);
if (_readerModifier != null) {
r = _readerModifier.modifyValueReader(_readContext, type, r);
if (r == null) {
throw new IllegalArgumentException("ReaderWriterModifier.modifyValueReader() returned null");
}
}
return r;
}
protected ValueReader _createReader(Class<?> contextType, Class<?> type, Type genericType)
{
if (type == Object.class) {
return AnyReader.std;
}
if (type.isArray()) {
return arrayReader(contextType, type);
}
if (type.isEnum()) {
if (_readerProvider != null) {
ValueReader r = _readerProvider.findValueReader(_readContext, type);
if (r != null) {
return r;
}
}
return enumReader(type);
}
if (Collection.class.isAssignableFrom(type)) {
return collectionReader(contextType, genericType);
}
if (Map.class.isAssignableFrom(type)) {
return mapReader(contextType, genericType);
}
if (_readerProvider != null) {
ValueReader r = _readerProvider.findValueReader(_readContext, type);
if (r != null) {
return r;
}
}
int typeId = _findSimpleType(type, false);
if (typeId > 0) {
return new SimpleValueReader(type, typeId);
}
return beanReader(type);
}
protected ValueReader arrayReader(Class<?> contextType, Class<?> arrayType) {
Class<?> elemType = arrayType.getComponentType();
if (!elemType.isPrimitive()) {
return new ArrayReader(arrayType, elemType,
createReader(contextType, elemType, elemType));
}
int typeId = _findSimpleType(arrayType, false);
if (typeId > 0) {
return new SimpleValueReader(arrayType, typeId);
}
throw new IllegalArgumentException("Deserialization of "+arrayType.getName()+" not (yet) supported");
}
protected ValueReader enumReader(Class<?> enumType) {
Object[] enums = enumType.getEnumConstants();
Map<String,Object> byName = new HashMap<String,Object>();
for (Object e : enums) {
byName.put(e.toString(), e);
}
return new EnumReader(enumType, enums, byName);
}
protected ValueReader collectionReader(Class<?> contextType, Type collectionType)
{
ResolvedType t = _typeResolver.resolve(_bindings(contextType), collectionType);
List<ResolvedType> params = t.typeParametersFor(Collection.class);
return collectionReader(t.erasedType(), params.get(0));
}
protected ValueReader collectionReader(Class<?> collectionType, ResolvedType valueType)
{
final Class<?> rawValueType = valueType.erasedType();
final ValueReader valueReader;
if (Collection.class.isAssignableFrom(rawValueType)) {
List<ResolvedType> params = valueType.typeParametersFor(Collection.class);
valueReader = collectionReader(rawValueType, params.get(0));
} else if (Map.class.isAssignableFrom(rawValueType)) {
List<ResolvedType> params = valueType.typeParametersFor(Map.class);
valueReader = mapReader(rawValueType, params.get(1));
} else {
valueReader = findReader(rawValueType);
}
if (_readerProvider != null) {
ValueReader r = _readerProvider.findCollectionReader(_readContext,
collectionType, valueType, valueReader);
if (r != null) {
return r;
}
}
return new CollectionReader(collectionType, valueReader);
}
protected ValueReader mapReader(Class<?> contextType, Type mapType)
{
ResolvedType t = _typeResolver.resolve(_bindings(contextType), mapType);
List<ResolvedType> params = t.typeParametersFor(Map.class);
return mapReader(t.erasedType(), params.get(1));
}
protected ValueReader mapReader(Class<?> mapType, ResolvedType valueType)
{
final Class<?> rawValueType = valueType.erasedType();
final ValueReader valueReader;
if (Collection.class.isAssignableFrom(rawValueType)) {
List<ResolvedType> params = valueType.typeParametersFor(Collection.class);
valueReader = collectionReader(rawValueType, params.get(0));
} else if (Map.class.isAssignableFrom(rawValueType)) {
List<ResolvedType> params = valueType.typeParametersFor(Map.class);
valueReader = mapReader(rawValueType, params.get(1));
} else {
valueReader = findReader(rawValueType);
}
if (_readerProvider != null) {
ValueReader r = _readerProvider.findMapReader(_readContext,
mapType, valueType, valueReader);
if (r != null) {
return r;
}
}
return new MapReader(mapType, valueReader);
}
protected ValueReader beanReader(Class<?> type)
{
final ClassKey key = new ClassKey(type, _features);
synchronized (_readerLock) {
if (_incompleteReaders == null) {
_incompleteReaders = new HashMap<ClassKey, ValueReader>();
} else {
ValueReader vr = _incompleteReaders.get(key);
if (vr != null) {
return vr;
}
}
final BeanReader def = _resolveBeanForDeser(type, _resolveBeanDef(type));
try {
_incompleteReaders.put(key, def);
for (Map.Entry<String, BeanPropertyReader> entry : def.propertiesByName().entrySet()) {
BeanPropertyReader prop = entry.getValue();
entry.setValue(prop.withReader(createReader(type, prop.rawSetterType(), prop.genericSetterType())));
}
} finally {
_incompleteReaders.remove(key);
}
return def;
}
}
protected POJODefinition _resolveBeanDef(Class<?> raw) {
try {
if (_readerModifier != null) {
POJODefinition def = _readerModifier.pojoDefinitionForDeserialization(_readContext, raw);
if (def != null) {
return def;
}
}
return BeanPropertyIntrospector.instance().pojoDefinitionForDeserialization(_readContext, raw);
} catch (Exception e) {
throw new IllegalArgumentException(String.format
("Failed to introspect ClassDefinition for type '%s': %s",
raw.getName(), e.getMessage()), e);
}
}
protected BeanReader _resolveBeanForDeser(Class<?> raw, POJODefinition beanDef)
{
Constructor<?> defaultCtor = beanDef.defaultCtor;
Constructor<?> stringCtor = beanDef.stringCtor;
Constructor<?> longCtor = beanDef.longCtor;
final boolean forceAccess = JSON.Feature.FORCE_REFLECTION_ACCESS.isEnabled(_features);
if (forceAccess) {
if (defaultCtor != null) {
defaultCtor.setAccessible(true);
}
if (stringCtor != null) {
stringCtor.setAccessible(true);
}
if (longCtor != null) {
longCtor.setAccessible(true);
}
}
final List<POJODefinition.Prop> rawProps = beanDef.getProperties();
final int len = rawProps.size();
final Map<String, BeanPropertyReader> propMap;
Map<String, String> aliasMapping = null;
if (len == 0) {
propMap = Collections.emptyMap();
} else {
propMap = new HashMap<String, BeanPropertyReader>();
final boolean useFields = JSON.Feature.USE_FIELDS.isEnabled(_features);
for (int i = 0; i < len; ++i) {
POJODefinition.Prop rawProp = rawProps.get(i);
Method m = rawProp.setter;
Field f = useFields ? rawProp.field : null;
if (m != null) {
if (forceAccess) {
m.setAccessible(true);
} else if (!Modifier.isPublic(m.getModifiers())) {
m = null;
}
}
if (m == null) {
if (f == null) {
continue;
}
if (forceAccess) {
f.setAccessible(true);
} else if (!Modifier.isPublic(f.getModifiers())) {
continue;
}
}
propMap.put(rawProp.name, new BeanPropertyReader(rawProp.name, f, m));
if (rawProp.hasAliases()) {
if (aliasMapping == null) {
aliasMapping = new HashMap<String, String>();
}
for (String alias : rawProp.aliases()) {
aliasMapping.put(alias, rawProp.name);
}
}
}
}
return new BeanReader(raw, propMap, defaultCtor, stringCtor, longCtor,
beanDef.getIgnorableNames(), aliasMapping);
}
private TypeBindings _bindings(Class<?> ctxt) {
if (ctxt == null) {
return TypeBindings.emptyBindings();
}
return TypeBindings.create(ctxt, (ResolvedType[]) null);
}
}