• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.deser.impl;
2 
3 import java.io.IOException;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.core.JsonParser;
7 
8 import com.fasterxml.jackson.databind.*;
9 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
10 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
11 
12 /**
13  * Object that is used to collect arguments for non-default creator
14  * (non-default-constructor, or argument-taking factory method)
15  * before creator can be called.
16  * Since ordering of JSON properties is not guaranteed, this may
17  * require buffering of values other than ones being passed to
18  * creator.
19  */
20 public final class PropertyBasedCreator
21 {
22     /**
23      * Number of properties: usually same as size of {@link #_propertyLookup},
24      * but not necessarily, when we have unnamed injectable properties.
25      */
26     protected final int _propertyCount;
27 
28     /**
29      * Helper object that knows how to actually construct the instance by
30      * invoking creator method with buffered arguments.
31      */
32     protected final ValueInstantiator _valueInstantiator;
33 
34     /**
35      * Map that contains property objects for either constructor or factory
36      * method (whichever one is null: one property for each
37      * parameter for that one), keyed by logical property name
38      */
39     protected final HashMap<String, SettableBeanProperty> _propertyLookup;
40 
41     /**
42      * Array that contains properties that expect value to inject, if any;
43      * null if no injectable values are expected.
44      */
45     protected final SettableBeanProperty[] _allProperties;
46 
47     /*
48     /**********************************************************
49     /* Construction, initialization
50     /**********************************************************
51      */
52 
PropertyBasedCreator(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps, boolean caseInsensitive, boolean addAliases)53     protected PropertyBasedCreator(DeserializationContext ctxt,
54             ValueInstantiator valueInstantiator,
55             SettableBeanProperty[] creatorProps,
56             boolean caseInsensitive,
57             boolean addAliases)
58     {
59         _valueInstantiator = valueInstantiator;
60         if (caseInsensitive) {
61             _propertyLookup = CaseInsensitiveMap.construct(ctxt.getConfig().getLocale());
62         } else {
63             _propertyLookup = new HashMap<String, SettableBeanProperty>();
64         }
65         final int len = creatorProps.length;
66         _propertyCount = len;
67         _allProperties = new SettableBeanProperty[len];
68 
69         // 26-Feb-2017, tatu: Let's start by aliases, so that there is no
70         //    possibility of accidental override of primary names
71         if (addAliases) {
72             final DeserializationConfig config = ctxt.getConfig();
73             for (SettableBeanProperty prop : creatorProps) {
74                 // 22-Jan-2018, tatu: ignorable entries should be ignored, even if got aliases
75                 if (!prop.isIgnorable()) {
76                     List<PropertyName> aliases = prop.findAliases(config);
77                     if (!aliases.isEmpty()) {
78                         for (PropertyName pn : aliases) {
79                             _propertyLookup.put(pn.getSimpleName(), prop);
80                         }
81                     }
82                 }
83             }
84         }
85         for (int i = 0; i < len; ++i) {
86             SettableBeanProperty prop = creatorProps[i];
87             _allProperties[i] = prop;
88             // 22-Jan-2018, tatu: ignorable entries should be skipped
89             if (!prop.isIgnorable()) {
90                 _propertyLookup.put(prop.getName(), prop);
91             }
92         }
93     }
94 
95     /**
96      * Factory method used for building actual instances to be used with POJOS:
97      * resolves deserializers, checks for "null values".
98      *
99      * @since 2.9
100      */
construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, BeanPropertyMap allProperties)101     public static PropertyBasedCreator construct(DeserializationContext ctxt,
102             ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
103             BeanPropertyMap allProperties)
104         throws JsonMappingException
105     {
106         final int len = srcCreatorProps.length;
107         SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
108         for (int i = 0; i < len; ++i) {
109             SettableBeanProperty prop = srcCreatorProps[i];
110             if (!prop.hasValueDeserializer()) {
111                 // 15-Apr-2020, tatu: [databind#962] Avoid getting deserializer for Inject-only
112                 //     cases
113                 if (!prop.isInjectionOnly()) {
114                     prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
115                 }
116             }
117             creatorProps[i] = prop;
118         }
119         return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
120                 allProperties.isCaseInsensitive(),
121 // 05-Sep-2019, tatu: As per [databind#2378] looks like not all aliases get merged into
122 //    `allProperties` so force lookup anyway.
123 //                allProperties.hasAliases()
124                 true);
125     }
126 
127     /**
128      * Factory method used for building actual instances to be used with types
129      * OTHER than POJOs.
130      * resolves deserializers and checks for "null values".
131      *
132      * @since 2.9
133      */
construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, boolean caseInsensitive)134     public static PropertyBasedCreator construct(DeserializationContext ctxt,
135             ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
136             boolean caseInsensitive)
137         throws JsonMappingException
138     {
139         final int len = srcCreatorProps.length;
140         SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
141         for (int i = 0; i < len; ++i) {
142             SettableBeanProperty prop = srcCreatorProps[i];
143             if (!prop.hasValueDeserializer()) {
144                 prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
145             }
146             creatorProps[i] = prop;
147         }
148         return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
149                 caseInsensitive, false);
150     }
151 
152     @Deprecated // since 2.9
construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps)153     public static PropertyBasedCreator construct(DeserializationContext ctxt,
154             ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps)
155         throws JsonMappingException
156     {
157         return construct(ctxt, valueInstantiator, srcCreatorProps,
158                 ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
159     }
160 
161     /*
162     /**********************************************************
163     /* Accessors
164     /**********************************************************
165      */
166 
properties()167     public Collection<SettableBeanProperty> properties() {
168         return _propertyLookup.values();
169     }
170 
findCreatorProperty(String name)171     public SettableBeanProperty findCreatorProperty(String name) {
172         return _propertyLookup.get(name);
173     }
174 
findCreatorProperty(int propertyIndex)175     public SettableBeanProperty findCreatorProperty(int propertyIndex) {
176         for (SettableBeanProperty prop : _propertyLookup.values()) {
177             if (prop.getPropertyIndex() == propertyIndex) {
178                 return prop;
179             }
180         }
181         return null;
182     }
183 
184     /*
185     /**********************************************************
186     /* Building process
187     /**********************************************************
188      */
189 
190     /**
191      * Method called when starting to build a bean instance.
192      *
193      * @since 2.1 (added ObjectIdReader parameter -- existed in previous versions without)
194      */
startBuilding(JsonParser p, DeserializationContext ctxt, ObjectIdReader oir)195     public PropertyValueBuffer startBuilding(JsonParser p, DeserializationContext ctxt,
196             ObjectIdReader oir) {
197         return new PropertyValueBuffer(p, ctxt, _propertyCount, oir);
198     }
199 
build(DeserializationContext ctxt, PropertyValueBuffer buffer)200     public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException
201     {
202         Object bean = _valueInstantiator.createFromObjectWith(ctxt,
203                 _allProperties, buffer);
204         // returning null isn't quite legal, but let's let caller deal with that
205         if (bean != null) {
206             // Object Id to handle?
207             bean = buffer.handleIdValue(ctxt, bean);
208 
209             // Anything buffered?
210             for (PropertyValue pv = buffer.buffered(); pv != null; pv = pv.next) {
211                 pv.assign(bean);
212             }
213         }
214         return bean;
215     }
216 
217     /*
218     /**********************************************************
219     /* Helper classes
220     /**********************************************************
221      */
222 
223     /**
224      * Simple override of standard {@link java.util.HashMap} to support
225      * case-insensitive access to creator properties
226      *
227      * @since 2.8.5
228      */
229     static class CaseInsensitiveMap extends HashMap<String, SettableBeanProperty>
230     {
231         private static final long serialVersionUID = 1L;
232 
233         /**
234          * Lower-casing can have Locale-specific minor variations.
235          *
236          * @since 2.11
237          */
238         protected final Locale _locale;
239 
240         @Deprecated // since 2.11
CaseInsensitiveMap()241         public CaseInsensitiveMap() {
242             this(Locale.getDefault());
243         }
244 
245         // @since 2.11
CaseInsensitiveMap(Locale l)246         public CaseInsensitiveMap(Locale l) {
247             _locale = l;
248         }
249 
250         // @since 2.11
construct(Locale l)251         public static CaseInsensitiveMap construct(Locale l) {
252             return new CaseInsensitiveMap(l);
253         }
254 
255         @Override
get(Object key0)256         public SettableBeanProperty get(Object key0) {
257             return super.get(((String) key0).toLowerCase(_locale));
258         }
259 
260         @Override
put(String key, SettableBeanProperty value)261         public SettableBeanProperty put(String key, SettableBeanProperty value) {
262             key = key.toLowerCase(_locale);
263             return super.put(key, value);
264         }
265     }
266 }
267