• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.deser;
2 
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.util.Collection;
7 
8 import com.fasterxml.jackson.core.*;
9 import com.fasterxml.jackson.core.format.*;
10 import com.fasterxml.jackson.core.io.MergedStream;
11 
12 import com.fasterxml.jackson.databind.*;
13 
14 /**
15  * Alternative to {@link DataFormatDetector} that needs to be used when
16  * using data-binding.
17  *
18  * @since 2.1
19  */
20 public class DataFormatReaders
21 {
22     /**
23      * By default we will look ahead at most 64 bytes; in most cases,
24      * much less (4 bytes or so) is needed, but we will allow bit more
25      * leniency to support data formats that need more complex heuristics.
26      */
27     public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64;
28 
29     /**
30      * Ordered list of readers which both represent data formats to
31      * detect (in precedence order, starting with highest) and contain
32      * factories used for actual detection.
33      */
34     protected final ObjectReader[] _readers;
35 
36     /**
37      * Strength of match we consider to be good enough to be used
38      * without checking any other formats.
39      * Default value is {@link MatchStrength#SOLID_MATCH},
40      */
41     protected final MatchStrength _optimalMatch;
42 
43     /**
44      * Strength of minimal match we accept as the answer, unless
45      * better matches are found.
46      * Default value is {@link MatchStrength#WEAK_MATCH},
47      */
48     protected final MatchStrength _minimalMatch;
49 
50     /**
51      * Maximum number of leading bytes of the input that we can read
52      * to determine data format.
53      *<p>
54      * Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}.
55      */
56     protected final int _maxInputLookahead;
57 
58     /*
59     /**********************************************************
60     /* Construction
61     /**********************************************************
62      */
63 
DataFormatReaders(ObjectReader... detectors)64     public DataFormatReaders(ObjectReader... detectors) {
65         this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH,
66             DEFAULT_MAX_INPUT_LOOKAHEAD);
67     }
68 
DataFormatReaders(Collection<ObjectReader> detectors)69     public DataFormatReaders(Collection<ObjectReader> detectors) {
70         this(detectors.toArray(new ObjectReader[detectors.size()]));
71     }
72 
DataFormatReaders(ObjectReader[] detectors, MatchStrength optMatch, MatchStrength minMatch, int maxInputLookahead)73     private DataFormatReaders(ObjectReader[] detectors,
74             MatchStrength optMatch, MatchStrength minMatch,
75             int maxInputLookahead)
76     {
77         _readers = detectors;
78         _optimalMatch = optMatch;
79         _minimalMatch = minMatch;
80         _maxInputLookahead = maxInputLookahead;
81     }
82 
83     /*
84     /**********************************************************
85     /* Fluent factories for changing match settings
86     /**********************************************************
87      */
88 
withOptimalMatch(MatchStrength optMatch)89     public DataFormatReaders withOptimalMatch(MatchStrength optMatch) {
90         if (optMatch == _optimalMatch) {
91             return this;
92         }
93         return new DataFormatReaders(_readers, optMatch, _minimalMatch, _maxInputLookahead);
94     }
95 
withMinimalMatch(MatchStrength minMatch)96     public DataFormatReaders withMinimalMatch(MatchStrength minMatch) {
97         if (minMatch == _minimalMatch) {
98             return this;
99         }
100         return new DataFormatReaders(_readers, _optimalMatch, minMatch, _maxInputLookahead);
101     }
102 
with(ObjectReader[] readers)103     public DataFormatReaders with(ObjectReader[] readers) {
104         return new DataFormatReaders(readers, _optimalMatch, _minimalMatch, _maxInputLookahead);
105     }
106 
withMaxInputLookahead(int lookaheadBytes)107     public DataFormatReaders withMaxInputLookahead(int lookaheadBytes)
108     {
109         if (lookaheadBytes == _maxInputLookahead) {
110             return this;
111         }
112         return new DataFormatReaders(_readers, _optimalMatch, _minimalMatch, lookaheadBytes);
113     }
114 
115     /*
116     /**********************************************************
117     /* Fluent factories for changing underlying readers
118     /**********************************************************
119      */
120 
with(DeserializationConfig config)121     public DataFormatReaders with(DeserializationConfig config)
122     {
123         final int len = _readers.length;
124         ObjectReader[] r = new ObjectReader[len];
125         for (int i = 0; i < len; ++i) {
126             r[i] = _readers[i].with(config);
127         }
128         return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
129     }
130 
withType(JavaType type)131     public DataFormatReaders withType(JavaType type)
132     {
133         final int len = _readers.length;
134         ObjectReader[] r = new ObjectReader[len];
135         for (int i = 0; i < len; ++i) {
136             r[i] = _readers[i].forType(type);
137         }
138         return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
139     }
140 
141     /*
142     /**********************************************************
143     /* Public API
144     /**********************************************************
145      */
146 
147     /**
148      * Method to call to find format that content (accessible via given
149      * {@link InputStream}) given has, as per configuration of this detector
150      * instance.
151      *
152      * @return Matcher object which contains result; never null, even in cases
153      *    where no match (with specified minimal match strength) is found.
154      */
findFormat(InputStream in)155     public Match findFormat(InputStream in) throws IOException
156     {
157         return _findFormat(new AccessorForReader(in, new byte[_maxInputLookahead]));
158     }
159 
160     /**
161      * Method to call to find format that given content (full document)
162      * has, as per configuration of this detector instance.
163      *
164      * @return Matcher object which contains result; never null, even in cases
165      *    where no match (with specified minimal match strength) is found.
166      */
findFormat(byte[] fullInputData)167     public Match findFormat(byte[] fullInputData) throws IOException
168     {
169         return _findFormat(new AccessorForReader(fullInputData));
170     }
171 
172     /**
173      * Method to call to find format that given content (full document)
174      * has, as per configuration of this detector instance.
175      *
176      * @return Matcher object which contains result; never null, even in cases
177      *    where no match (with specified minimal match strength) is found.
178      *
179      * @since 2.1
180      */
findFormat(byte[] fullInputData, int offset, int len)181     public Match findFormat(byte[] fullInputData, int offset, int len) throws IOException
182     {
183         return _findFormat(new AccessorForReader(fullInputData, offset, len));
184     }
185 
186     /*
187     /**********************************************************
188     /* Overrides
189     /**********************************************************
190      */
191 
192     @Override
toString()193     public String toString()
194     {
195         StringBuilder sb = new StringBuilder();
196         sb.append('[');
197         final int len = _readers.length;
198         if (len > 0) {
199             sb.append(_readers[0].getFactory().getFormatName());
200             for (int i = 1; i < len; ++i) {
201                 sb.append(", ");
202                 sb.append(_readers[i].getFactory().getFormatName());
203             }
204         }
205         sb.append(']');
206         return sb.toString();
207     }
208 
209     /*
210     /**********************************************************
211     /* Internal methods
212     /**********************************************************
213      */
214 
_findFormat(AccessorForReader acc)215     private Match _findFormat(AccessorForReader acc) throws IOException
216     {
217         ObjectReader bestMatch = null;
218         MatchStrength bestMatchStrength = null;
219         for (ObjectReader f : _readers) {
220             acc.reset();
221             MatchStrength strength = f.getFactory().hasFormat(acc);
222             // if not better than what we have so far (including minimal level limit), skip
223             if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) {
224                 continue;
225             }
226             // also, needs to better match than before
227             if (bestMatch != null) {
228                 if (bestMatchStrength.ordinal() >= strength.ordinal()) {
229                     continue;
230                 }
231             }
232             // finally: if it's good enough match, we are done
233             bestMatch = f;
234             bestMatchStrength = strength;
235             if (strength.ordinal() >= _optimalMatch.ordinal()) {
236                 break;
237             }
238         }
239         return acc.createMatcher(bestMatch, bestMatchStrength);
240     }
241 
242     /*
243     /**********************************************************
244     /* Helper classes
245     /**********************************************************
246      */
247 
248     /**
249      * We need sub-class here as well, to be able to access efficiently.
250      */
251     protected class AccessorForReader extends InputAccessor.Std
252     {
AccessorForReader(InputStream in, byte[] buffer)253         public AccessorForReader(InputStream in, byte[] buffer) {
254             super(in, buffer);
255         }
AccessorForReader(byte[] inputDocument)256         public AccessorForReader(byte[] inputDocument) {
257             super(inputDocument);
258         }
AccessorForReader(byte[] inputDocument, int start, int len)259         public AccessorForReader(byte[] inputDocument, int start, int len) {
260             super(inputDocument, start, len);
261         }
262 
createMatcher(ObjectReader match, MatchStrength matchStrength)263         public Match createMatcher(ObjectReader match, MatchStrength matchStrength)
264         {
265             return new Match(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart),
266                     match, matchStrength);
267         }
268     }
269 
270     /**
271      * Result class, similar to {@link DataFormatMatcher}
272      */
273     public static class Match
274     {
275         protected final InputStream _originalStream;
276 
277         /**
278          * Content read during format matching process
279          */
280         protected final byte[] _bufferedData;
281 
282         /**
283          * Pointer to the first byte in buffer available for reading
284          */
285         protected final int _bufferedStart;
286 
287         /**
288          * Number of bytes available in buffer.
289          */
290         protected final int _bufferedLength;
291 
292         /**
293          * Factory that produced sufficient match (if any)
294          */
295         protected final ObjectReader _match;
296 
297         /**
298          * Strength of match with {@link #_match}
299          */
300         protected final MatchStrength _matchStrength;
301 
Match(InputStream in, byte[] buffered, int bufferedStart, int bufferedLength, ObjectReader match, MatchStrength strength)302         protected Match(InputStream in, byte[] buffered,
303                 int bufferedStart, int bufferedLength,
304                 ObjectReader match, MatchStrength strength)
305         {
306             _originalStream = in;
307             _bufferedData = buffered;
308             _bufferedStart = bufferedStart;
309             _bufferedLength = bufferedLength;
310             _match = match;
311             _matchStrength = strength;
312         }
313 
314         /*
315         /**********************************************************
316         /* Public API, simple accessors
317         /**********************************************************
318          */
319 
320         /**
321          * Accessor to use to see if any formats matched well enough with
322          * the input data.
323          */
hasMatch()324         public boolean hasMatch() { return _match != null; }
325 
326         /**
327          * Method for accessing strength of the match, if any; if no match,
328          * will return {@link MatchStrength#INCONCLUSIVE}.
329          */
getMatchStrength()330         public MatchStrength getMatchStrength() {
331             return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength;
332         }
333 
334         /**
335          * Accessor for {@link JsonFactory} that represents format that data matched.
336          */
getReader()337         public ObjectReader getReader() { return _match; }
338 
339         /**
340          * Accessor for getting brief textual name of matched format if any (null
341          * if none). Equivalent to:
342          *<pre>
343          *   return hasMatch() ? getMatch().getFormatName() : null;
344          *</pre>
345          */
getMatchedFormatName()346         public String getMatchedFormatName() {
347             return _match.getFactory().getFormatName();
348         }
349 
350         /*
351         /**********************************************************
352         /* Public API, factory methods
353         /**********************************************************
354          */
355 
356         /**
357          * Convenience method for trying to construct a {@link JsonParser} for
358          * parsing content which is assumed to be in detected data format.
359          * If no match was found, returns null.
360          */
createParserWithMatch()361         public JsonParser createParserWithMatch() throws IOException
362         {
363             if (_match == null) {
364                 return null;
365             }
366             JsonFactory jf = _match.getFactory();
367             if (_originalStream == null) {
368                 return jf.createParser(_bufferedData, _bufferedStart, _bufferedLength);
369             }
370             return jf.createParser(getDataStream());
371         }
372 
373         /**
374          * Method to use for accessing input for which format detection has been done.
375          * This <b>must</b> be used instead of using stream passed to detector
376          * unless given stream itself can do buffering.
377          * Stream will return all content that was read during matching process, as well
378          * as remaining contents of the underlying stream.
379          */
getDataStream()380         public InputStream getDataStream() {
381             if (_originalStream == null) {
382                 return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength);
383             }
384             return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength);
385         }
386     }
387 
388 }
389