| package com.fasterxml.jackson.databind.introspect; |
| |
| import java.util.Collection; |
| import java.util.Map; |
| |
| import com.fasterxml.jackson.databind.AnnotationIntrospector; |
| import com.fasterxml.jackson.databind.DeserializationConfig; |
| import com.fasterxml.jackson.databind.JavaType; |
| import com.fasterxml.jackson.databind.SerializationConfig; |
| import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; |
| import com.fasterxml.jackson.databind.cfg.MapperConfig; |
| import com.fasterxml.jackson.databind.type.SimpleType; |
| import com.fasterxml.jackson.databind.util.ClassUtil; |
| import com.fasterxml.jackson.databind.util.LRUMap; |
| |
| public class BasicClassIntrospector |
| extends ClassIntrospector |
| implements java.io.Serializable |
| { |
| private static final long serialVersionUID = 1L; |
| |
| /* 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. |
| * |
| * @since 2.4 |
| */ |
| protected final static BasicBeanDescription STRING_DESC; |
| static { |
| STRING_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(String.class), |
| AnnotatedClassResolver.createPrimordial(String.class)); |
| } |
| protected final static BasicBeanDescription BOOLEAN_DESC; |
| static { |
| BOOLEAN_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Boolean.TYPE), |
| AnnotatedClassResolver.createPrimordial(Boolean.TYPE)); |
| } |
| protected final static BasicBeanDescription INT_DESC; |
| static { |
| INT_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Integer.TYPE), |
| AnnotatedClassResolver.createPrimordial(Integer.TYPE)); |
| } |
| protected final static BasicBeanDescription LONG_DESC; |
| static { |
| LONG_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Long.TYPE), |
| AnnotatedClassResolver.createPrimordial(Long.TYPE)); |
| } |
| |
| /* |
| /********************************************************** |
| /* Life cycle |
| /********************************************************** |
| */ |
| |
| /** |
| * Looks like 'forClassAnnotations()' gets called so frequently that we |
| * should consider caching to avoid some of the lookups. |
| * |
| * @since 2.5 |
| */ |
| protected final LRUMap<JavaType,BasicBeanDescription> _cachedFCA; |
| |
| public BasicClassIntrospector() { |
| // a small cache should go a long way here |
| _cachedFCA = new LRUMap<JavaType,BasicBeanDescription>(16, 64); |
| } |
| |
| @Override |
| public ClassIntrospector copy() { |
| return new BasicClassIntrospector(); |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory method impls |
| /********************************************************** |
| */ |
| |
| @Override |
| public BasicBeanDescription forSerialization(SerializationConfig cfg, |
| JavaType type, MixInResolver r) |
| { |
| // minor optimization: for some JDK types do minimal introspection |
| BasicBeanDescription desc = _findStdTypeDesc(type); |
| if (desc == null) { |
| // As per [databind#550], skip full introspection for some of standard |
| // structured types as well |
| desc = _findStdJdkCollectionDesc(cfg, type); |
| if (desc == null) { |
| desc = BasicBeanDescription.forSerialization(collectProperties(cfg, |
| type, r, true, "set")); |
| } |
| // Also: this is a superset of "forClassAnnotations", so may optimize by optional add: |
| _cachedFCA.putIfAbsent(type, desc); |
| } |
| return desc; |
| } |
| |
| @Override |
| public BasicBeanDescription forDeserialization(DeserializationConfig cfg, |
| JavaType type, MixInResolver r) |
| { |
| // minor optimization: for some JDK types do minimal introspection |
| BasicBeanDescription desc = _findStdTypeDesc(type); |
| if (desc == null) { |
| // As per [Databind#550], skip full introspection for some of standard |
| // structured types as well |
| desc = _findStdJdkCollectionDesc(cfg, type); |
| if (desc == null) { |
| desc = BasicBeanDescription.forDeserialization(collectProperties(cfg, |
| type, r, false, "set")); |
| } |
| // Also: this is a superset of "forClassAnnotations", so may optimize by optional add: |
| _cachedFCA.putIfAbsent(type, desc); |
| } |
| return desc; |
| } |
| |
| @Override |
| public BasicBeanDescription forDeserializationWithBuilder(DeserializationConfig cfg, |
| JavaType type, MixInResolver r) |
| { |
| // no std JDK types with Builders, so: |
| |
| BasicBeanDescription desc = BasicBeanDescription.forDeserialization(collectPropertiesWithBuilder(cfg, |
| type, r, false)); |
| // this is still a superset of "forClassAnnotations", so may optimize by optional add: |
| _cachedFCA.putIfAbsent(type, desc); |
| return desc; |
| } |
| |
| @Override |
| public BasicBeanDescription forCreation(DeserializationConfig cfg, |
| JavaType type, MixInResolver r) |
| { |
| BasicBeanDescription desc = _findStdTypeDesc(type); |
| if (desc == null) { |
| |
| // As per [Databind#550], skip full introspection for some of standard |
| // structured types as well |
| desc = _findStdJdkCollectionDesc(cfg, type); |
| if (desc == null) { |
| desc = BasicBeanDescription.forDeserialization( |
| collectProperties(cfg, type, r, false, "set")); |
| } |
| } |
| // should this be cached for FCA? |
| return desc; |
| } |
| |
| @Override |
| public BasicBeanDescription forClassAnnotations(MapperConfig<?> config, |
| JavaType type, MixInResolver r) |
| { |
| BasicBeanDescription desc = _findStdTypeDesc(type); |
| if (desc == null) { |
| desc = _cachedFCA.get(type); |
| if (desc == null) { |
| desc = BasicBeanDescription.forOtherUse(config, type, |
| _resolveAnnotatedClass(config, type, r)); |
| _cachedFCA.put(type, desc); |
| } |
| } |
| return desc; |
| } |
| |
| @Override |
| public BasicBeanDescription forDirectClassAnnotations(MapperConfig<?> config, |
| JavaType type, MixInResolver r) |
| { |
| BasicBeanDescription desc = _findStdTypeDesc(type); |
| if (desc == null) { |
| desc = BasicBeanDescription.forOtherUse(config, type, |
| _resolveAnnotatedWithoutSuperTypes(config, type, r)); |
| } |
| return desc; |
| } |
| |
| /* |
| /********************************************************** |
| /* Overridable helper methods |
| /********************************************************** |
| */ |
| |
| protected POJOPropertiesCollector collectProperties(MapperConfig<?> config, |
| JavaType type, MixInResolver r, boolean forSerialization, |
| String mutatorPrefix) |
| { |
| return constructPropertyCollector(config, |
| _resolveAnnotatedClass(config, type, r), |
| type, forSerialization, mutatorPrefix); |
| } |
| |
| protected POJOPropertiesCollector collectPropertiesWithBuilder(MapperConfig<?> config, |
| JavaType type, MixInResolver r, boolean forSerialization) |
| { |
| AnnotatedClass ac = _resolveAnnotatedClass(config, type, r); |
| AnnotationIntrospector ai = config.isAnnotationProcessingEnabled() ? config.getAnnotationIntrospector() : null; |
| JsonPOJOBuilder.Value builderConfig = (ai == null) ? null : ai.findPOJOBuilderConfig(ac); |
| String mutatorPrefix = (builderConfig == null) ? JsonPOJOBuilder.DEFAULT_WITH_PREFIX : builderConfig.withPrefix; |
| return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix); |
| } |
| |
| /** |
| * Overridable method called for creating {@link POJOPropertiesCollector} instance |
| * to use; override is needed if a custom sub-class is to be used. |
| */ |
| protected POJOPropertiesCollector constructPropertyCollector(MapperConfig<?> config, |
| AnnotatedClass ac, JavaType type, boolean forSerialization, String mutatorPrefix) |
| { |
| return new POJOPropertiesCollector(config, forSerialization, type, ac, mutatorPrefix); |
| } |
| |
| /** |
| * Method called to see if type is one of core JDK types |
| * that we have cached for efficiency. |
| */ |
| protected BasicBeanDescription _findStdTypeDesc(JavaType type) |
| { |
| Class<?> cls = type.getRawClass(); |
| if (cls.isPrimitive()) { |
| if (cls == Boolean.TYPE) { |
| return BOOLEAN_DESC; |
| } |
| if (cls == Integer.TYPE) { |
| return INT_DESC; |
| } |
| if (cls == Long.TYPE) { |
| return LONG_DESC; |
| } |
| } else { |
| if (cls == String.class) { |
| return STRING_DESC; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Helper method used to decide whether we can omit introspection |
| * for members (methods, fields, constructors); we may do so for |
| * a limited number of container types JDK provides. |
| */ |
| protected boolean _isStdJDKCollection(JavaType type) |
| { |
| if (!type.isContainerType() || type.isArrayType()) { |
| return false; |
| } |
| Class<?> raw = type.getRawClass(); |
| String pkgName = ClassUtil.getPackageName(raw); |
| if (pkgName != null) { |
| if (pkgName.startsWith("java.lang") |
| || pkgName.startsWith("java.util")) { |
| /* 23-Sep-2014, tatu: Should we be conservative here (minimal number |
| * of matches), or ambitious? Let's do latter for now. |
| */ |
| if (Collection.class.isAssignableFrom(raw) |
| || Map.class.isAssignableFrom(raw)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| protected BasicBeanDescription _findStdJdkCollectionDesc(MapperConfig<?> cfg, JavaType type) |
| { |
| if (_isStdJDKCollection(type)) { |
| return BasicBeanDescription.forOtherUse(cfg, type, |
| _resolveAnnotatedClass(cfg, type, cfg)); |
| } |
| return null; |
| } |
| |
| /** |
| * @since 2.9 |
| */ |
| protected AnnotatedClass _resolveAnnotatedClass(MapperConfig<?> config, |
| JavaType type, MixInResolver r) { |
| return AnnotatedClassResolver.resolve(config, type, r); |
| } |
| |
| /** |
| * @since 2.9 |
| */ |
| protected AnnotatedClass _resolveAnnotatedWithoutSuperTypes(MapperConfig<?> config, |
| JavaType type, MixInResolver r) { |
| return AnnotatedClassResolver.resolveWithoutSuperTypes(config, type, r); |
| } |
| } |