View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package com.addthis.codec.jackson;
15  
16  import java.io.IOException;
17  
18  import com.fasterxml.jackson.core.JsonParser;
19  import com.fasterxml.jackson.core.JsonProcessingException;
20  import com.fasterxml.jackson.core.JsonToken;
21  import com.fasterxml.jackson.databind.DeserializationContext;
22  import com.fasterxml.jackson.databind.JsonDeserializer;
23  import com.fasterxml.jackson.databind.JsonMappingException;
24  import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
25  import com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer;
26  import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
27  import com.fasterxml.jackson.databind.type.ArrayType;
28  import com.fasterxml.jackson.databind.util.ObjectBuffer;
29  
30  /**
31   * Provides missing array index information from error path reporting. Based on the version from master, but
32   * with some additional improvements.
33   */
34  public class IndexReportingObjectArrayDeserializer extends ObjectArrayDeserializer implements ContextualDeserializer {
35  
36      public IndexReportingObjectArrayDeserializer(ArrayType arrayType,
37                                                   JsonDeserializer<Object> elemDeser,
38                                                   TypeDeserializer elemTypeDeser) {
39          super(arrayType, elemDeser, elemTypeDeser);
40      }
41  
42      /** Overridable fluent-factory method used to create contextual instances */
43      @SuppressWarnings("unchecked")
44      public IndexReportingObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser,
45                                                                    JsonDeserializer<?> elemDeser) {
46          if ((elemDeser == _elementDeserializer) && (elemTypeDeser == _elementTypeDeserializer)) {
47              return this;
48          }
49          return new IndexReportingObjectArrayDeserializer(
50                  _arrayType, (JsonDeserializer<Object>) elemDeser, elemTypeDeser);
51      }
52  
53      @Override
54      public Object[] deserialize(JsonParser jp, DeserializationContext ctxt)
55          throws IOException, JsonProcessingException {
56  
57          if (!jp.isExpectedStartArrayToken()) {
58              // punt to super method for edge case handling, we won't need to report any indexes anyway
59              return super.deserialize(jp, ctxt);
60          }
61  
62          final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
63          Object[] chunk = buffer.resetAndStart();
64          int ix = 0;
65          JsonToken t;
66          final TypeDeserializer typeDeser = _elementTypeDeserializer;
67  
68          try {
69              while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
70                  if (ix >= chunk.length) {
71                      chunk = buffer.appendCompletedChunk(chunk);
72                      ix = 0;
73                  }
74  
75                  // Note: must handle null explicitly here; value deserializers won't
76                  Object value;
77                  if (t == JsonToken.VALUE_NULL) {
78                      value = _elementDeserializer.getNullValue();
79                  } else if (typeDeser == null) {
80                      value = _elementDeserializer.deserialize(jp, ctxt);
81                  } else {
82                      value = _elementDeserializer.deserializeWithType(jp, ctxt, typeDeser);
83                  }
84                  chunk[ix] = value;
85                  ix++;
86              }
87          } catch (Exception e) {
88              // note: pass Object.class, not Object[].class, as we need element type for error info
89              // addition: add buffer.bufferedSize() since ix gets periodically reset
90              // addition: report _elementClass instead of hard-coded Object.class
91              throw JsonMappingException.wrapWithPath(e, _elementClass, ix + buffer.bufferedSize());
92          }
93  
94          Object[] result;
95  
96          if (_untyped) {
97              result = buffer.completeAndClearBuffer(chunk, ix);
98          } else {
99              result = buffer.completeAndClearBuffer(chunk, ix, _elementClass);
100         }
101         ctxt.returnObjectBuffer(buffer);
102         return result;
103     }
104 }
105