• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.core.format;
2 
3 import java.io.*;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.core.*;
7 
8 /**
9  * Simple helper class that allows data format (content type) auto-detection,
10  * given an ordered set of {@link JsonFactory} instances to use for actual low-level
11  * detection.
12  */
13 public class DataFormatDetector
14 {
15     /**
16      * By default we will look ahead at most 64 bytes; in most cases,
17      * much less (4 bytes or so) is needed, but we will allow bit more
18      * leniency to support data formats that need more complex heuristics.
19      */
20     public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64;
21 
22     /**
23      * Ordered list of factories which both represent data formats to
24      * detect (in precedence order, starting with highest) and are used
25      * for actual detection.
26      */
27     protected final JsonFactory[] _detectors;
28 
29     /**
30      * Strength of match we consider to be good enough to be used
31      * without checking any other formats.
32      * Default value is {@link MatchStrength#SOLID_MATCH},
33      */
34     protected final MatchStrength _optimalMatch;
35 
36     /**
37      * Strength of minimal match we accept as the answer, unless
38      * better matches are found.
39      * Default value is {@link MatchStrength#WEAK_MATCH},
40      */
41     protected final MatchStrength _minimalMatch;
42 
43     /**
44      * Maximum number of leading bytes of the input that we can read
45      * to determine data format.
46      *<p>
47      * Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}.
48      */
49     protected final int _maxInputLookahead;
50 
51     /*
52     /**********************************************************
53     /* Construction
54     /**********************************************************
55      */
56 
DataFormatDetector(JsonFactory... detectors)57     public DataFormatDetector(JsonFactory... detectors) {
58         this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH,
59             DEFAULT_MAX_INPUT_LOOKAHEAD);
60     }
61 
DataFormatDetector(Collection<JsonFactory> detectors)62     public DataFormatDetector(Collection<JsonFactory> detectors) {
63         this(detectors.toArray(new JsonFactory[0]));
64     }
65 
DataFormatDetector(JsonFactory[] detectors, MatchStrength optMatch, MatchStrength minMatch, int maxInputLookahead)66     private DataFormatDetector(JsonFactory[] detectors,
67             MatchStrength optMatch, MatchStrength minMatch, int maxInputLookahead) {
68         _detectors = detectors;
69         _optimalMatch = optMatch;
70         _minimalMatch = minMatch;
71         _maxInputLookahead = maxInputLookahead;
72     }
73 
74     /**
75      * Method that will return a detector instance that uses given
76      * optimal match level (match that is considered sufficient to return, without
77      * trying to find stronger matches with other formats).
78      */
withOptimalMatch(MatchStrength optMatch)79     public DataFormatDetector withOptimalMatch(MatchStrength optMatch) {
80         if (optMatch == _optimalMatch) {
81             return this;
82         }
83         return new DataFormatDetector(_detectors, optMatch, _minimalMatch, _maxInputLookahead);
84     }
85     /**
86      * Method that will return a detector instance that uses given
87      * minimal match level; match that may be returned unless a stronger match
88      * is found with other format detectors.
89      */
withMinimalMatch(MatchStrength minMatch)90     public DataFormatDetector withMinimalMatch(MatchStrength minMatch) {
91         if (minMatch == _minimalMatch) {
92             return this;
93         }
94         return new DataFormatDetector(_detectors, _optimalMatch, minMatch, _maxInputLookahead);
95     }
96 
97     /**
98      * Method that will return a detector instance that allows detectors to
99      * read up to specified number of bytes when determining format match strength.
100      */
withMaxInputLookahead(int lookaheadBytes)101     public DataFormatDetector withMaxInputLookahead(int lookaheadBytes) {
102         if (lookaheadBytes == _maxInputLookahead) {
103             return this;
104         }
105         return new DataFormatDetector(_detectors, _optimalMatch, _minimalMatch, lookaheadBytes);
106     }
107 
108     /*
109     /**********************************************************
110     /* Public API
111     /**********************************************************
112      */
113 
114     /**
115      * Method to call to find format that content (accessible via given
116      * {@link InputStream}) given has, as per configuration of this detector
117      * instance.
118      *
119      * @return Matcher object which contains result; never null, even in cases
120      *    where no match (with specified minimal match strength) is found.
121      */
findFormat(InputStream in)122     public DataFormatMatcher findFormat(InputStream in) throws IOException {
123         return _findFormat(new InputAccessor.Std(in, new byte[_maxInputLookahead]));
124     }
125 
126     /**
127      * Method to call to find format that given content (full document)
128      * has, as per configuration of this detector instance.
129      *
130      * @return Matcher object which contains result; never null, even in cases
131      *    where no match (with specified minimal match strength) is found.
132      */
findFormat(byte[] fullInputData)133     public DataFormatMatcher findFormat(byte[] fullInputData) throws IOException {
134         return _findFormat(new InputAccessor.Std(fullInputData));
135     }
136 
137     /**
138      * Method to call to find format that given content (full document)
139      * has, as per configuration of this detector instance.
140      *
141      * @return Matcher object which contains result; never null, even in cases
142      *    where no match (with specified minimal match strength) is found.
143      *
144      * @since 2.1
145      */
findFormat(byte[] fullInputData, int offset, int len)146     public DataFormatMatcher findFormat(byte[] fullInputData, int offset, int len) throws IOException {
147         return _findFormat(new InputAccessor.Std(fullInputData, offset, len));
148     }
149 
150     /*
151     /**********************************************************
152     /* Overrides
153     /**********************************************************
154      */
155 
toString()156     @Override public String toString() {
157         StringBuilder sb = new StringBuilder();
158         sb.append('[');
159         final int len = _detectors.length;
160         if (len > 0) {
161             sb.append(_detectors[0].getFormatName());
162             for (int i = 1; i < len; ++i) {
163                 sb.append(", ");
164                 sb.append(_detectors[i].getFormatName());
165             }
166         }
167         sb.append(']');
168         return sb.toString();
169     }
170 
171     /*
172     /**********************************************************
173     /* Internal methods
174     /**********************************************************
175      */
176 
_findFormat(InputAccessor.Std acc)177     private DataFormatMatcher _findFormat(InputAccessor.Std acc) throws IOException {
178         JsonFactory bestMatch = null;
179         MatchStrength bestMatchStrength = null;
180         for (JsonFactory f : _detectors) {
181             acc.reset();
182             MatchStrength strength = f.hasFormat(acc);
183             // if not better than what we have so far (including minimal level limit), skip
184             if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) {
185                 continue;
186             }
187             // also, needs to better match than before
188             if (bestMatch != null) {
189                 if (bestMatchStrength.ordinal() >= strength.ordinal()) {
190                     continue;
191                 }
192             }
193             // finally: if it's good enough match, we are done
194             bestMatch = f;
195             bestMatchStrength = strength;
196             if (strength.ordinal() >= _optimalMatch.ordinal()) {
197                 break;
198             }
199         }
200         return acc.createMatcher(bestMatch, bestMatchStrength);
201     }
202 }
203