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