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