1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.io;
7
8 import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration;
9 import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
10 import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
11 import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
12 import gov.nist.secauto.metaschema.core.model.AbstractResourceResolver;
13 import gov.nist.secauto.metaschema.core.model.IBoundObject;
14 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
15 import gov.nist.secauto.metaschema.databind.IBindingContext;
16 import gov.nist.secauto.metaschema.databind.io.ModelDetector.Result;
17
18 import org.eclipse.jdt.annotation.NotOwning;
19 import org.eclipse.jdt.annotation.Owning;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URI;
24 import java.net.URL;
25 import java.util.Map;
26
27 import edu.umd.cs.findbugs.annotations.NonNull;
28
29
30
31
32 public class DefaultBoundLoader
33 extends AbstractResourceResolver
34 implements IBoundLoader {
35 public static final int LOOK_AHEAD_BYTES = 32_768;
36
37
38
39
40
41
42
43 private FormatDetector formatDetector;
44
45 private ModelDetector modelDetector;
46
47 @NonNull
48 private final IBindingContext bindingContext;
49 @NonNull
50 private final IMutableConfiguration<DeserializationFeature<?>> configuration;
51
52
53
54
55
56
57
58 public DefaultBoundLoader(@NonNull IBindingContext bindingContext) {
59 this.bindingContext = bindingContext;
60 this.configuration = new DefaultConfiguration<>();
61 }
62
63 @NonNull
64 private IMutableConfiguration<DeserializationFeature<?>> getConfiguration() {
65 return configuration;
66 }
67
68 @Override
69 public boolean isFeatureEnabled(DeserializationFeature<?> feature) {
70 return getConfiguration().isFeatureEnabled(feature);
71 }
72
73 @Override
74 public Map<DeserializationFeature<?>, Object> getFeatureValues() {
75 return getConfiguration().getFeatureValues();
76 }
77
78 @Override
79 public IBoundLoader applyConfiguration(@NonNull IConfiguration<DeserializationFeature<?>> other) {
80 getConfiguration().applyConfiguration(other);
81 resetDetector();
82 return this;
83 }
84
85 @SuppressWarnings("PMD.NullAssignment")
86 private void resetDetector() {
87
88 formatDetector = null;
89 }
90
91 @Override
92 public IBoundLoader set(DeserializationFeature<?> feature, Object value) {
93 getConfiguration().set(feature, value);
94 resetDetector();
95 return this;
96 }
97
98 @Override
99 public IBindingContext getBindingContext() {
100 return bindingContext;
101 }
102
103 @Override
104 public Format detectFormat(@NonNull URI uri) throws IOException {
105 URI resourceUri = resolve(uri);
106 URL resource = resourceUri.toURL();
107
108 try (InputStream is = ObjectUtils.notNull(resource.openStream())) {
109 return detectFormat(is).getFormat();
110 }
111 }
112
113 @Override
114 public FormatDetector.Result detectFormat(@NonNull InputStream is) throws IOException {
115 return getFormatDetector().detect(is);
116 }
117
118 @NonNull
119 private FormatDetector getFormatDetector() {
120 if (formatDetector == null) {
121 formatDetector = new FormatDetector(getConfiguration());
122 }
123 assert formatDetector != null;
124 return formatDetector;
125 }
126
127 @NonNull
128 private ModelDetector getModelDetector() {
129 if (modelDetector == null) {
130 modelDetector = new ModelDetector(
131 getBindingContext(),
132 getConfiguration());
133 }
134 assert modelDetector != null;
135 return modelDetector;
136 }
137
138 @Override
139 @Owning
140 public Result detectModel(@NotOwning InputStream is, Format format) throws IOException {
141 return getModelDetector().detect(is, format);
142 }
143
144 @Override
145 public <CLASS extends IBoundObject> CLASS load(@NonNull URI uri) throws IOException {
146 URI resourceUri = resolve(uri);
147 URL resource = resourceUri.toURL();
148
149 try (InputStream is = ObjectUtils.notNull(resource.openStream())) {
150 return load(is, uri);
151 }
152 }
153
154 @SuppressWarnings("unchecked")
155 @Override
156 @NonNull
157 public <CLASS extends IBoundObject> CLASS load(
158 @NotOwning @NonNull InputStream is,
159 @NonNull URI documentUri)
160 throws IOException {
161 FormatDetector.Result formatMatch = getFormatDetector().detect(is);
162 Format format = formatMatch.getFormat();
163
164 try (InputStream formatStream = formatMatch.getDataStream()) {
165 try (ModelDetector.Result modelMatch = detectModel(formatStream, format)) {
166
167 IDeserializer<?> deserializer = getDeserializer(
168 modelMatch.getBoundClass(),
169 format,
170 getConfiguration());
171 try (InputStream modelStream = modelMatch.getDataStream()) {
172 return (CLASS) deserializer.deserialize(modelStream, documentUri);
173 }
174 }
175 }
176 }
177
178 @Override
179 public <CLASS extends IBoundObject> CLASS load(Class<CLASS> clazz, URI uri) throws IOException {
180 URI resourceUri = resolve(uri);
181 URL resource = resourceUri.toURL();
182
183 try (InputStream is = ObjectUtils.notNull(resource.openStream())) {
184 return load(clazz, is, resourceUri);
185 }
186 }
187
188 @Override
189 public <CLASS extends IBoundObject> CLASS load(Class<CLASS> clazz, InputStream is, URI documentUri)
190 throws IOException {
191
192
193 FormatDetector.Result match = getFormatDetector().detect(is);
194 Format format = match.getFormat();
195
196 try (InputStream remainingStream = match.getDataStream()) {
197
198 return load(clazz, format, remainingStream, documentUri);
199 }
200 }
201
202 @Override
203 @NonNull
204 public <CLASS extends IBoundObject> CLASS load(
205 @NonNull Class<CLASS> clazz,
206 @NonNull Format format,
207 @NonNull InputStream is,
208 @NonNull URI documentUri) throws IOException {
209
210 IDeserializer<CLASS> deserializer = getDeserializer(clazz, format, getConfiguration());
211 return deserializer.deserialize(is, documentUri);
212 }
213
214 @Override
215 public IDocumentNodeItem loadAsNodeItem(URI uri) throws IOException {
216 URI resourceUri = resolve(uri);
217 URL resource = resourceUri.toURL();
218
219 try (InputStream is = ObjectUtils.notNull(resource.openStream())) {
220 return loadAsNodeItem(is, resourceUri);
221 }
222 }
223
224 @NonNull
225 private IDocumentNodeItem loadAsNodeItem(@NonNull InputStream is, @NonNull URI documentUri) throws IOException {
226 FormatDetector.Result formatMatch = getFormatDetector().detect(is);
227 Format format = formatMatch.getFormat();
228
229 try (InputStream formatStream = formatMatch.getDataStream()) {
230 return loadAsNodeItem(format, formatStream, documentUri);
231 }
232 }
233
234 @Override
235 public IDocumentNodeItem loadAsNodeItem(Format format, URI uri) throws IOException {
236 URI resourceUri = resolve(uri);
237 URL resource = resourceUri.toURL();
238
239 try (InputStream is = ObjectUtils.notNull(resource.openStream())) {
240 return loadAsNodeItem(format, is, resourceUri);
241 }
242 }
243
244 @Override
245 public IDocumentNodeItem loadAsNodeItem(Format format, InputStream is, URI documentUri)
246 throws IOException {
247 try (ModelDetector.Result modelMatch = detectModel(is, format)) {
248
249 IDeserializer<?> deserializer = getDeserializer(
250 modelMatch.getBoundClass(),
251 format,
252 getConfiguration());
253 try (InputStream modelStream = modelMatch.getDataStream()) {
254 return (IDocumentNodeItem) deserializer.deserializeToNodeItem(modelStream, documentUri);
255 }
256 }
257 }
258
259 @NonNull
260 private <CLASS extends IBoundObject> IDeserializer<CLASS> getDeserializer(
261 @NonNull Class<CLASS> clazz,
262 @NonNull Format format,
263 @NonNull IConfiguration<DeserializationFeature<?>> config) {
264 IDeserializer<CLASS> retval = getBindingContext().newDeserializer(format, clazz);
265 retval.applyConfiguration(config);
266 return retval;
267 }
268 }