1 package com.fasterxml.jackson.databind.introspect;
2
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Modifier;
5 import java.util.*;
6
7 import com.fasterxml.jackson.databind.AnnotationIntrospector;
8 import com.fasterxml.jackson.databind.JavaType;
9 import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
10 import com.fasterxml.jackson.databind.type.TypeFactory;
11 import com.fasterxml.jackson.databind.util.ClassUtil;
12
13 public class AnnotatedFieldCollector
14     extends CollectorBase
15 {
16     // // // Configuration
17
18     private final TypeFactory _typeFactory;
19     private final MixInResolver _mixInResolver;
20
21     /**
22      * @since 2.11
23      */

24     private final boolean _collectAnnotations;
25
26     // // // Collected state
27
28     AnnotatedFieldCollector(AnnotationIntrospector intr,
29             TypeFactory types, MixInResolver mixins, boolean collectAnnotations)
30     {
31         super(intr);
32         _typeFactory = types;
33         _mixInResolver = (intr == null) ? null : mixins;
34         _collectAnnotations = collectAnnotations;
35     }
36
37     public static List<AnnotatedField> collectFields(AnnotationIntrospector intr,
38             TypeResolutionContext tc,
39             MixInResolver mixins, TypeFactory types,
40             JavaType type, boolean collectAnnotations)
41     {
42         return new AnnotatedFieldCollector(intr, types, mixins, collectAnnotations)
43                 .collect(tc, type);
44     }
45
46     List<AnnotatedField> collect(TypeResolutionContext tc, JavaType type)
47     {
48         Map<String,FieldBuilder> foundFields = _findFields(tc, type, null);
49         if (foundFields == null) {
50             return Collections.emptyList();
51         }
52         List<AnnotatedField> result = new ArrayList<>(foundFields.size());
53         for (FieldBuilder b : foundFields.values()) {
54             result.add(b.build());
55         }
56         return result;
57     }
58
59     private Map<String,FieldBuilder> _findFields(TypeResolutionContext tc,
60             JavaType type, Map<String,FieldBuilder> fields)
61     {
62         // First, a quick test: we only care for regular classes (not interfaces,
63         //primitive types etc), except for Object.class. A simple check to rule out
64         // other cases is to see if there is a super class or not.
65         JavaType parent = type.getSuperClass();
66         if (parent == null) {
67             return fields;
68         }
69         final Class<?> cls = type.getRawClass();
70         // Let's add super-class' fields first, then ours.
71         fields = _findFields(new TypeResolutionContext.Basic(_typeFactory, parent.getBindings()),
72                 parent, fields);
73         for (Field f : cls.getDeclaredFields()) {
74             // static fields not included (transients are at this point, filtered out later)
75             if (!_isIncludableField(f)) {
76                 continue;
77             }
78             // Ok now: we can (and need) not filter out ignorable fields at this point; partly
79             // because mix-ins haven't been added, and partly because logic can be done
80             // when determining get/settability of the field.
81             if (fields == null) {
82                 fields = new LinkedHashMap<>();
83             }
84             FieldBuilder b = new FieldBuilder(tc, f);
85             if (_collectAnnotations) {
86                 b.annotations = collectAnnotations(b.annotations, f.getDeclaredAnnotations());
87             }
88             fields.put(f.getName(), b);
89         }
90         // And then... any mix-in overrides?
91         if ((fields != null) && (_mixInResolver != null)) {
92             Class<?> mixin = _mixInResolver.findMixInClassFor(cls);
93             if (mixin != null) {
94                 _addFieldMixIns(mixin, cls, fields);
95             }
96         }
97         return fields;
98     }
99
100     /**
101      * Method called to add field mix-ins from given mix-in class (and its fields)
102      * into already collected actual fields (from introspected classes and their
103      * super-classes)
104      */

105     private void _addFieldMixIns(Class<?> mixInCls, Class<?> targetClass,
106             Map<String,FieldBuilder> fields)
107     {
108         List<Class<?>> parents = ClassUtil.findSuperClasses(mixInCls, targetClass, true);
109         for (Class<?> mixin : parents) {
110             for (Field mixinField : mixin.getDeclaredFields()) {
111                 // there are some dummy things (static, synthetic); better ignore
112                 if (!_isIncludableField(mixinField)) {
113                     continue;
114                 }
115                 String name = mixinField.getName();
116                 // anything to mask? (if not, quietly ignore)
117                 FieldBuilder b = fields.get(name);
118                 if (b != null) {
119                     b.annotations = collectAnnotations(b.annotations, mixinField.getDeclaredAnnotations());
120                 }
121             }
122         }
123     }
124
125     private boolean _isIncludableField(Field f)
126     {
127         // Most likely synthetic fields, if any, are to be skipped similar to methods
128         if (f.isSynthetic()) {
129             return false;
130         }
131         // Static fields are never included. Transient are (since 2.6), for
132         // purpose of propagating removal
133         int mods = f.getModifiers();
134         if (Modifier.isStatic(mods)) {
135             return false;
136         }
137         return true;
138     }
139
140     private final static class FieldBuilder {
141         public final TypeResolutionContext typeContext;
142         public final Field field;
143
144         public AnnotationCollector annotations;
145
146         public FieldBuilder(TypeResolutionContext tc, Field f) {
147             typeContext = tc;
148             field = f;
149             annotations = AnnotationCollector.emptyCollector();
150         }
151
152         public AnnotatedField build() {
153             return new AnnotatedField(typeContext, field, annotations.asAnnotationMap());
154         }
155     }
156 }
157