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