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