blob: e8b0d0b86d97d98f63e5f40e532ffd97a2b9381e [file] [log] [blame]
package org.codehaus.jackson.map.introspect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.ClassIntrospector;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.MapperConfig;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.type.SimpleType;
import org.codehaus.jackson.map.util.ClassUtil;
import org.codehaus.jackson.type.JavaType;
public class BasicClassIntrospector
extends ClassIntrospector<BasicBeanDescription>
{
/* We keep a small set of pre-constructed descriptions to use for
* common non-structured values, such as Numbers and Strings.
* This is strictly performance optimization to reduce what is
* usually one-time cost, but seems useful for some cases considering
* simplicity.
*/
protected final static BasicBeanDescription STRING_DESC;
static {
AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(String.class, null, null);
STRING_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(String.class), ac);
}
protected final static BasicBeanDescription BOOLEAN_DESC;
static {
AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Boolean.TYPE, null, null);
BOOLEAN_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Boolean.TYPE), ac);
}
protected final static BasicBeanDescription INT_DESC;
static {
AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Integer.TYPE, null, null);
INT_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Integer.TYPE), ac);
}
protected final static BasicBeanDescription LONG_DESC;
static {
AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Long.TYPE, null, null);
LONG_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Long.TYPE), ac);
}
// // // Then static filter singletons
/**
* @since 1.8
* @deprecated Since 1.9 just don't use
*/
@Deprecated
public final static GetterMethodFilter DEFAULT_GETTER_FILTER = new GetterMethodFilter();
/**
* @since 1.8
* @deprecated Since 1.9 just don't use
*/
@Deprecated
public final static SetterMethodFilter DEFAULT_SETTER_FILTER = new SetterMethodFilter();
/**
* @since 1.8
* @deprecated Since 1.9 just don't use
*/
@Deprecated
public final static SetterAndGetterMethodFilter DEFAULT_SETTER_AND_GETTER_FILTER = new SetterAndGetterMethodFilter();
protected final static MethodFilter MINIMAL_FILTER = new MinimalMethodFilter();
/*
/**********************************************************
/* Life cycle
/**********************************************************
*/
public final static BasicClassIntrospector instance = new BasicClassIntrospector();
public BasicClassIntrospector() { }
/*
/**********************************************************
/* Factory method impls
/**********************************************************
*/
@Override
public BasicBeanDescription forSerialization(SerializationConfig cfg,
JavaType type, MixInResolver r)
{
// minor optimization: for JDK types do minimal introspection
BasicBeanDescription desc = _findCachedDesc(type);
if (desc == null) {
desc = BasicBeanDescription.forSerialization(collectProperties(cfg, type, r, true));
}
return desc;
}
@Override
public BasicBeanDescription forDeserialization(DeserializationConfig cfg,
JavaType type, MixInResolver r)
{
// minor optimization: for JDK types do minimal introspection
BasicBeanDescription desc = _findCachedDesc(type);
if (desc == null) {
desc = BasicBeanDescription.forDeserialization(collectProperties(cfg, type, r, false));
}
return desc;
}
@Override
public BasicBeanDescription forCreation(DeserializationConfig cfg,
JavaType type, MixInResolver r)
{
BasicBeanDescription desc = _findCachedDesc(type);
if (desc == null) {
desc = BasicBeanDescription.forDeserialization(collectProperties(cfg, type, r, false));
}
return desc;
}
@Override
public BasicBeanDescription forClassAnnotations(MapperConfig<?> cfg,
JavaType type, MixInResolver r)
{
boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
AnnotationIntrospector ai = cfg.getAnnotationIntrospector();
AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(), (useAnnotations ? ai : null), r);
return BasicBeanDescription.forOtherUse(cfg, type, ac);
}
@Override
public BasicBeanDescription forDirectClassAnnotations(MapperConfig<?> cfg,
JavaType type, MixInResolver r)
{
boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
AnnotationIntrospector ai = cfg.getAnnotationIntrospector();
AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(type.getRawClass(),
(useAnnotations ? ai : null), r);
return BasicBeanDescription.forOtherUse(cfg, type, ac);
}
/*
/**********************************************************
/* Overridable helper methods
/**********************************************************
*/
/**
* @since 1.9
*/
public POJOPropertiesCollector collectProperties(MapperConfig<?> config,
JavaType type, MixInResolver r, boolean forSerialization)
{
AnnotatedClass ac = classWithCreators(config, type, r);
ac.resolveMemberMethods(MINIMAL_FILTER);
ac.resolveFields();
return constructPropertyCollector(config, ac, type, forSerialization).collect();
}
/**
* Overridable method called for creating {@link POJOPropertiesCollector} instance
* to use; override is needed if a custom sub-class is to be used.
*
* @since 1.9
*/
protected POJOPropertiesCollector constructPropertyCollector(MapperConfig<?> config,
AnnotatedClass ac, JavaType type,
boolean forSerialization)
{
return new POJOPropertiesCollector(config, forSerialization, type, ac);
}
/**
* @since 1.9
*/
public AnnotatedClass classWithCreators(MapperConfig<?> config,
JavaType type, MixInResolver r)
{
boolean useAnnotations = config.isAnnotationProcessingEnabled();
AnnotationIntrospector ai = config.getAnnotationIntrospector();
AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(), (useAnnotations ? ai : null), r);
ac.resolveMemberMethods(MINIMAL_FILTER);
// true -> include all creators, not just default constructor
ac.resolveCreators(true);
return ac;
}
/**
* Method called to see if type is one of core JDK types
* that we have cached for efficiency.
*
* @since 1.9
*/
protected BasicBeanDescription _findCachedDesc(JavaType type)
{
Class<?> cls = type.getRawClass();
if (cls == String.class) {
return STRING_DESC;
}
if (cls == Boolean.TYPE) {
return BOOLEAN_DESC;
}
if (cls == Integer.TYPE) {
return INT_DESC;
}
if (cls == Long.TYPE) {
return LONG_DESC;
}
return null;
}
/**
* Helper method for getting access to filter that only guarantees
* that methods used for serialization are to be included.
*
* @deprecated Since 1.9 just don't use
*/
@Deprecated
protected MethodFilter getSerializationMethodFilter(SerializationConfig cfg)
{
return DEFAULT_GETTER_FILTER;
}
/**
* Helper method for getting access to filter that only guarantees
* that methods used for deserialization are to be included.
*
* @deprecated Since 1.9 just don't use
*/
@Deprecated
protected MethodFilter getDeserializationMethodFilter(DeserializationConfig cfg)
{
/* [JACKSON-88]: may also need to include getters (at least for
* Collection and Map types)
*/
if (cfg.isEnabled(DeserializationConfig.Feature.USE_GETTERS_AS_SETTERS)) {
return DEFAULT_SETTER_AND_GETTER_FILTER;
}
return DEFAULT_SETTER_FILTER;
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Going forward, we will only do very minimal filtering;
* mostly just gets rid of static methods really.
*
* @since 1.9
*/
private static class MinimalMethodFilter
implements MethodFilter
{
@Override
public boolean includeMethod(Method m)
{
if (Modifier.isStatic(m.getModifiers())) {
return false;
}
int pcount = m.getParameterTypes().length;
return (pcount <= 2);
}
}
/**
* Filter used to only include methods that have signature that is
* compatible with "getters": take no arguments, are non-static,
* and return something.
*
* @deprecated Since 1.9 just don't use
*/
@Deprecated
public static class GetterMethodFilter
implements MethodFilter
{
private GetterMethodFilter() { }
@Override
public boolean includeMethod(Method m)
{
return ClassUtil.hasGetterSignature(m);
}
}
/**
* Filter used to only include methods that have signature that is
* compatible with "setters": take one and only argument and
* are non-static.
*<p>
* Actually, also need to include 2-arg methods to support
* "any setters"; as well as 0-arg getters as long as they
* return Collection or Map type.
*
* @deprecated Since 1.9 just don't use
*/
@Deprecated
public static class SetterMethodFilter
implements MethodFilter
{
@Override
public boolean includeMethod(Method m)
{
// First: we can't use static methods
if (Modifier.isStatic(m.getModifiers())) {
return false;
}
int pcount = m.getParameterTypes().length;
// Ok; multiple acceptable parameter counts:
switch (pcount) {
case 1:
// Regular setters take just one param, so include:
return true;
case 2:
/* 2-arg version only for "AnySetters"; they are not
* auto-detected, and need to have an annotation.
* However, due to annotation inheritance we do, we
* don't yet know if sub-classes might have annotations...
* so shouldn't leave out any methods quite yet.
*/
//if (m.getAnnotation(JsonAnySetter.class) != null) { ... }
return true;
}
return false;
}
}
/**
* Filter used if some getters (namely, once needed for "setterless
* collection injection") are also needed, not just setters.
*
* @deprecated Since 1.9 just don't use
*/
@Deprecated
public final static class SetterAndGetterMethodFilter
extends SetterMethodFilter
{
@SuppressWarnings("deprecation")
@Override
public boolean includeMethod(Method m)
{
if (super.includeMethod(m)) {
return true;
}
if (!ClassUtil.hasGetterSignature(m)) {
return false;
}
// but furthermore, only accept Collections & Maps, for now
Class<?> rt = m.getReturnType();
if (Collection.class.isAssignableFrom(rt)
|| Map.class.isAssignableFrom(rt)) {
return true;
}
return false;
}
}
}