1 package com.fasterxml.jackson.databind.deser.impl;
2 
3 import java.io.IOException;
4 import java.util.BitSet;
5 
6 import com.fasterxml.jackson.core.JsonParser;
7 import com.fasterxml.jackson.databind.DeserializationContext;
8 import com.fasterxml.jackson.databind.DeserializationFeature;
9 import com.fasterxml.jackson.databind.JsonDeserializer;
10 import com.fasterxml.jackson.databind.JsonMappingException;
11 import com.fasterxml.jackson.databind.deser.SettableAnyProperty;
12 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
13 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
14 
15 /**
16  * Simple container used for temporarily buffering a set of
17  * <code>PropertyValue</code>s.
18  * Using during construction of beans (and Maps) that use Creators,
19  * and hence need buffering before instance (that will have properties
20  * to assign values to) is constructed.
21  */
22 public class PropertyValueBuffer
23 {
24     /*
25     /**********************************************************
26     /* Configuration
27     /**********************************************************
28      */
29 
30     protected final JsonParser _parser;
31     protected final DeserializationContext _context;
32 
33     protected final ObjectIdReader _objectIdReader;
34 
35     /*
36     /**********************************************************
37     /* Accumulated properties, other stuff
38     /**********************************************************
39      */
40 
41     /**
42      * Buffer used for storing creator parameters for constructing
43      * instance.
44      */
45     protected final Object[] _creatorParameters;
46 
47     /**
48      * Number of creator parameters for which we have not yet received
49      * values.
50      */
51     protected int _paramsNeeded;
52 
53     /**
54      * Bitflag used to track parameters found from incoming data
55      * when number of parameters is
56      * less than 32 (fits in int).
57      */
58     protected int _paramsSeen;
59 
60     /**
61      * Bitflag used to track parameters found from incoming data
62      * when number of parameters is
63      * 32 or higher.
64      */
65     protected final BitSet _paramsSeenBig;
66 
67     /**
68      * If we get non-creator parameters before or between
69      * creator parameters, those need to be buffered. Buffer
70      * is just a simple linked list
71      */
72     protected PropertyValue _buffered;
73 
74     /**
75      * In case there is an Object Id property to handle, this is the value
76      * we have for it.
77      */
78     protected Object _idValue;
79 
80     /*
81     /**********************************************************
82     /* Life-cycle
83     /**********************************************************
84      */
85 
PropertyValueBuffer(JsonParser p, DeserializationContext ctxt, int paramCount, ObjectIdReader oir)86     public PropertyValueBuffer(JsonParser p, DeserializationContext ctxt, int paramCount,
87             ObjectIdReader oir)
88     {
89         _parser = p;
90         _context = ctxt;
91         _paramsNeeded = paramCount;
92         _objectIdReader = oir;
93         _creatorParameters = new Object[paramCount];
94         if (paramCount < 32) {
95             _paramsSeenBig = null;
96         } else {
97             _paramsSeenBig = new BitSet();
98         }
99     }
100 
101     /**
102      * Returns {@code true} if the given property was seen in the JSON source by
103      * this buffer.
104      *
105      * @since 2.8
106      */
hasParameter(SettableBeanProperty prop)107     public final boolean hasParameter(SettableBeanProperty prop)
108     {
109         if (_paramsSeenBig == null) {
110             return ((_paramsSeen >> prop.getCreatorIndex()) & 1) == 1;
111         }
112         return _paramsSeenBig.get(prop.getCreatorIndex());
113     }
114 
115     /**
116      * A variation of {@link #getParameters(SettableBeanProperty[])} that
117      * accepts a single property.  Whereas the plural form eagerly fetches and
118      * validates all properties, this method may be used (along with
119      * {@link #hasParameter(SettableBeanProperty)}) to let applications only
120      * fetch the properties defined in the JSON source itself, and to have some
121      * other customized behavior for missing properties.
122      *
123      * @since 2.8
124      */
getParameter(SettableBeanProperty prop)125     public Object getParameter(SettableBeanProperty prop)
126         throws JsonMappingException
127     {
128         Object value;
129         if (hasParameter(prop)) {
130             value = _creatorParameters[prop.getCreatorIndex()];
131         } else {
132             value = _creatorParameters[prop.getCreatorIndex()] = _findMissing(prop);
133         }
134         if (value == null && _context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
135             return _context.reportInputMismatch(prop,
136                 "Null value for creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS` enabled",
137                 prop.getName(), prop.getCreatorIndex());
138         }
139         return value;
140     }
141 
142     /**
143      * Method called to do necessary post-processing such as injection of values
144      * and verification of values for required properties,
145      * after either {@link #assignParameter(SettableBeanProperty, Object)}
146      * returns <code>true</code> (to indicate all creator properties are found), or when
147      * then whole JSON Object has been processed,
148      */
getParameters(SettableBeanProperty[] props)149     public Object[] getParameters(SettableBeanProperty[] props)
150         throws JsonMappingException
151     {
152         // quick check to see if anything else is needed
153         if (_paramsNeeded > 0) {
154             if (_paramsSeenBig == null) {
155                 int mask = _paramsSeen;
156                 // not optimal, could use `Integer.trailingZeroes()`, but for now should not
157                 // really matter for common cases
158                 for (int ix = 0, len = _creatorParameters.length; ix < len; ++ix, mask >>= 1) {
159                     if ((mask & 1) == 0) {
160                         _creatorParameters[ix] = _findMissing(props[ix]);
161                     }
162                 }
163             } else {
164                 final int len = _creatorParameters.length;
165                 for (int ix = 0; (ix = _paramsSeenBig.nextClearBit(ix)) < len; ++ix) {
166                     _creatorParameters[ix] = _findMissing(props[ix]);
167                 }
168             }
169         }
170 
171         if (_context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) {
172             for (int ix = 0; ix < props.length; ++ix) {
173                 if (_creatorParameters[ix] == null) {
174                     SettableBeanProperty prop = props[ix];
175                     _context.reportInputMismatch(prop,
176                             "Null value for creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS` enabled",
177                             prop.getName(), props[ix].getCreatorIndex());
178                 }
179             }
180         }
181         return _creatorParameters;
182     }
183 
_findMissing(SettableBeanProperty prop)184     protected Object _findMissing(SettableBeanProperty prop) throws JsonMappingException
185     {
186         // First: do we have injectable value?
187         Object injectableValueId = prop.getInjectableValueId();
188         if (injectableValueId != null) {
189             return _context.findInjectableValue(prop.getInjectableValueId(),
190                     prop, null);
191         }
192         // Second: required?
193         if (prop.isRequired()) {
194             _context.reportInputMismatch(prop, "Missing required creator property '%s' (index %d)",
195                     prop.getName(), prop.getCreatorIndex());
196         }
197         if (_context.isEnabled(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)) {
198             _context.reportInputMismatch(prop,
199                     "Missing creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES` enabled",
200                     prop.getName(), prop.getCreatorIndex());
201         }
202         try {
203             // Third: NullValueProvider? (22-Sep-2019, [databind#2458])
204             Object nullValue = prop.getNullValueProvider().getNullValue(_context);
205             if (nullValue != null) {
206                 return nullValue;
207             }
208 
209             // Fourth: default value
210             JsonDeserializer<Object> deser = prop.getValueDeserializer();
211             return deser.getNullValue(_context);
212         } catch (JsonMappingException e) {
213             // [databind#2101]: Include property name, if we have it
214             AnnotatedMember member = prop.getMember();
215             if (member != null) {
216                 e.prependPath(member.getDeclaringClass(), prop.getName());
217             }
218             throw e;
219         }
220     }
221 
222     /*
223     /**********************************************************
224     /* Other methods
225     /**********************************************************
226      */
227 
228     /**
229      * Helper method called to see if given non-creator property is the "id property";
230      * and if so, handle appropriately.
231      *
232      * @since 2.1
233      */
readIdProperty(String propName)234     public boolean readIdProperty(String propName) throws IOException
235     {
236         if ((_objectIdReader != null) && propName.equals(_objectIdReader.propertyName.getSimpleName())) {
237             _idValue = _objectIdReader.readObjectReference(_parser, _context);
238             return true;
239         }
240         return false;
241     }
242 
243     /**
244      * Helper method called to handle Object Id value collected earlier, if any
245      */
handleIdValue(final DeserializationContext ctxt, Object bean)246     public Object handleIdValue(final DeserializationContext ctxt, Object bean) throws IOException
247     {
248         if (_objectIdReader != null) {
249             if (_idValue != null) {
250                 ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator, _objectIdReader.resolver);
251                 roid.bindItem(bean);
252                 // also: may need to set a property value as well
253                 SettableBeanProperty idProp = _objectIdReader.idProperty;
254                 if (idProp != null) {
255                     return idProp.setAndReturn(bean, _idValue);
256                 }
257             } else {
258                 // 07-Jun-2016, tatu: Trying to improve error messaging here...
259                 ctxt.reportUnresolvedObjectId(_objectIdReader, bean);
260             }
261         }
262         return bean;
263     }
264 
buffered()265     protected PropertyValue buffered() { return _buffered; }
266 
isComplete()267     public boolean isComplete() { return _paramsNeeded <= 0; }
268 
269     /**
270      * Method called to buffer value for given property, as well as check whether
271      * we now have values for all (creator) properties that we expect to get values for.
272      *
273      * @return True if we have received all creator parameters
274      *
275      * @since 2.6
276      */
assignParameter(SettableBeanProperty prop, Object value)277     public boolean assignParameter(SettableBeanProperty prop, Object value)
278     {
279         final int ix = prop.getCreatorIndex();
280         _creatorParameters[ix] = value;
281         if (_paramsSeenBig == null) {
282             int old = _paramsSeen;
283             int newValue = (old | (1 << ix));
284             if (old != newValue) {
285                 _paramsSeen = newValue;
286                 if (--_paramsNeeded <= 0) {
287                     // 29-Nov-2016, tatu: But! May still require Object Id value
288                     return (_objectIdReader == null) || (_idValue != null);
289                 }
290             }
291         } else {
292             if (!_paramsSeenBig.get(ix)) {
293                 _paramsSeenBig.set(ix);
294                 if (--_paramsNeeded <= 0) {
295                     // 29-Nov-2016, tatu: But! May still require Object Id value
296                 }
297             }
298         }
299         return false;
300     }
301 
bufferProperty(SettableBeanProperty prop, Object value)302     public void bufferProperty(SettableBeanProperty prop, Object value) {
303         _buffered = new PropertyValue.Regular(_buffered, value, prop);
304     }
305 
bufferAnyProperty(SettableAnyProperty prop, String propName, Object value)306     public void bufferAnyProperty(SettableAnyProperty prop, String propName, Object value) {
307         _buffered = new PropertyValue.Any(_buffered, value, prop, propName);
308     }
309 
bufferMapProperty(Object key, Object value)310     public void bufferMapProperty(Object key, Object value) {
311         _buffered = new PropertyValue.Map(_buffered, value, key);
312     }
313 }
314