1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.databind.io;
28
29 import com.fasterxml.jackson.core.JsonFactory;
30 import com.fasterxml.jackson.core.format.DataFormatDetector;
31 import com.fasterxml.jackson.core.format.DataFormatMatcher;
32 import com.fasterxml.jackson.core.format.MatchStrength;
33 import com.fasterxml.jackson.dataformat.xml.XmlFactory;
34 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
35
36 import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration;
37 import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
38 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
39 import gov.nist.secauto.metaschema.databind.io.json.JsonFactoryFactory;
40 import gov.nist.secauto.metaschema.databind.io.yaml.impl.YamlFactoryFactory;
41
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.net.URL;
45
46 import edu.umd.cs.findbugs.annotations.NonNull;
47
48
49
50
51
52 public class FormatDetector {
53
54 private final DataFormatDetector detector;
55
56
57
58
59 public FormatDetector() {
60 this(new DefaultConfiguration<>());
61 }
62
63
64
65
66
67
68
69 public FormatDetector(
70 @NonNull IConfiguration<DeserializationFeature<?>> configuration) {
71 this(configuration, newDetectorFactory(configuration));
72 }
73
74
75
76
77
78
79
80
81
82 protected FormatDetector(
83 @NonNull IConfiguration<DeserializationFeature<?>> configuration,
84 @NonNull JsonFactory... detectors) {
85 int lookaheadBytes = configuration.get(DeserializationFeature.FORMAT_DETECTION_LOOKAHEAD_LIMIT);
86 this.detector = new DataFormatDetector(detectors)
87 .withMinimalMatch(MatchStrength.INCONCLUSIVE)
88 .withOptimalMatch(MatchStrength.SOLID_MATCH)
89 .withMaxInputLookahead(lookaheadBytes - 1);
90
91 }
92
93 @NonNull
94 private static JsonFactory[] newDetectorFactory(@NonNull IConfiguration<DeserializationFeature<?>> config) {
95 JsonFactory[] detectorFactory = new JsonFactory[3];
96 detectorFactory[0] = YamlFactoryFactory.newParserFactoryInstance(config);
97 detectorFactory[1] = JsonFactoryFactory.instance();
98 detectorFactory[2] = new XmlFactory();
99 return detectorFactory;
100 }
101
102
103
104
105
106
107
108
109
110
111 @NonNull
112 public Result detect(@NonNull URL resource) throws IOException {
113 try (InputStream is = ObjectUtils.notNull(resource.openStream())) {
114 return detect(is);
115 }
116 }
117
118
119
120
121
122
123
124
125
126
127
128 @NonNull
129 public Result detect(@NonNull InputStream inputStream) throws IOException {
130 DataFormatMatcher matcher = detector.findFormat(inputStream);
131 switch (matcher.getMatchStrength()) {
132 case FULL_MATCH:
133 case SOLID_MATCH:
134 case WEAK_MATCH:
135 case INCONCLUSIVE:
136 return new Result(matcher);
137 case NO_MATCH:
138 default:
139 throw new IOException("Unable to identify format");
140 }
141 }
142
143 public static class Result {
144 @NonNull
145 private final DataFormatMatcher matcher;
146
147 private Result(@NonNull DataFormatMatcher matcher) {
148 this.matcher = matcher;
149 }
150
151
152
153
154
155
156 @NonNull
157 public Format getFormat() {
158 Format retval;
159 String formatName = matcher.getMatchedFormatName();
160 if (YAMLFactory.FORMAT_NAME_YAML.equals(formatName)) {
161 retval = Format.YAML;
162 } else if (JsonFactory.FORMAT_NAME_JSON.equals(formatName)) {
163 retval = Format.JSON;
164 } else if (XmlFactory.FORMAT_NAME_XML.equals(formatName)) {
165 retval = Format.XML;
166 } else {
167 throw new UnsupportedOperationException(String.format("The detected format '%s' is not supported", formatName));
168 }
169 return retval;
170 }
171
172
173
174
175
176
177
178 @SuppressWarnings("resource")
179 @NonNull
180 public InputStream getDataStream() {
181 return ObjectUtils.notNull(matcher.getDataStream());
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195 @NonNull
196 public MatchStrength getMatchStrength() {
197 return ObjectUtils.notNull(matcher.getMatchStrength());
198 }
199 }
200 }