1 /*
2 * SPDX-FileCopyrightText: none
3 * SPDX-License-Identifier: CC0-1.0
4 */
5
6 package gov.nist.secauto.metaschema.databind.io;
7
8 import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
9 import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
10 import gov.nist.secauto.metaschema.core.metapath.IDocumentLoader;
11 import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
12 import gov.nist.secauto.metaschema.core.model.IBoundObject;
13 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
14 import gov.nist.secauto.metaschema.databind.DefaultBindingContext;
15 import gov.nist.secauto.metaschema.databind.IBindingContext;
16
17 import org.eclipse.jdt.annotation.Owning;
18 import org.xml.sax.InputSource;
19
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.io.Writer;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.net.URL;
29 import java.nio.file.Path;
30
31 import edu.umd.cs.findbugs.annotations.NonNull;
32
33 /**
34 * A common interface for loading Module based instance resources.
35 */
36 public interface IBoundLoader extends IDocumentLoader, IMutableConfiguration<DeserializationFeature<?>> {
37
38 @Override
39 default IBoundLoader enableFeature(DeserializationFeature<?> feature) {
40 return set(feature, true);
41 }
42
43 @Override
44 default IBoundLoader disableFeature(DeserializationFeature<?> feature) {
45 return set(feature, false);
46 }
47
48 @Override
49 IBoundLoader applyConfiguration(IConfiguration<DeserializationFeature<?>> other);
50
51 @Override
52 IBoundLoader set(DeserializationFeature<?> feature, Object value);
53
54 /**
55 * Determine the format of the provided resource.
56 *
57 * @param file
58 * the resource
59 * @return the format information for the provided resource
60 * @throws IOException
61 * if an error occurred while reading the resource
62 */
63 @NonNull
64 default Format detectFormat(@NonNull File file) throws IOException {
65 return detectFormat(ObjectUtils.notNull(file.toPath()));
66 }
67
68 /**
69 * Determine the format of the provided resource.
70 *
71 * @param path
72 * the resource
73 * @return the format information for the provided resource
74 * @throws IOException
75 * if an error occurred while reading the resource
76 */
77 @NonNull
78 default Format detectFormat(@NonNull Path path) throws IOException {
79 return detectFormat(ObjectUtils.notNull(path.toUri()));
80 }
81
82 /**
83 * Determine the format of the provided resource.
84 *
85 * @param url
86 * the resource
87 * @return the format information for the provided resource
88 * @throws IOException
89 * if an error occurred while reading the resource
90 */
91 @NonNull
92 default Format detectFormat(@NonNull URL url) throws IOException {
93 try {
94 return detectFormat(ObjectUtils.notNull(url.toURI()));
95 } catch (URISyntaxException ex) {
96 throw new IOException(ex);
97 }
98 }
99
100 /**
101 * Determine the format of the resource identified by the provided {@code uri}.
102 *
103 * @param uri
104 * the resource
105 * @return the format information for the provided resource
106 * @throws IOException
107 * if an error occurred while reading the resource
108 */
109 @NonNull
110 Format detectFormat(@NonNull URI uri) throws IOException;
111
112 /**
113 * Determine the format of the provided resource.
114 * <p>
115 * This method will consume data from the provided {@link InputStream}. If the
116 * caller of this method intends to read data from the stream after determining
117 * the format, the caller should pass in a stream that can be reset.
118 * <p>
119 * This method will not close the provided {@link InputStream}, since it does
120 * not own the stream.
121 *
122 * @param is
123 * an input stream for the resource
124 * @param resource
125 * the URI of the resource
126 * @return the format information for the provided resource
127 * @throws IOException
128 * if an error occurred while reading the resource
129 */
130 @NonNull
131 FormatDetector.Result detectFormat(@NonNull InputStream is, @NonNull URI resource) throws IOException;
132
133 /**
134 * Determine the model of the provided resource.
135 * <p>
136 * This method will consume data from any {@link InputStream} provided by the
137 * {@link InputSource}. If the caller of this method intends to read data from
138 * the stream after determining the format, the caller should pass in a stream
139 * that can be reset.
140 * <p>
141 * This method will not close any {@link InputStream} provided by the
142 * {@link InputSource}, since it does not own the stream.
143 *
144 * @param is
145 * an input stream for the resource
146 * @param resource
147 * the URI of the resource
148 * @param format
149 * the format of the provided resource
150 * @return the model of the provided resource
151 * @throws IOException
152 * if an error occurred while reading the resource
153 */
154 @NonNull
155 @Owning
156 ModelDetector.Result detectModel(@NonNull InputStream is, @NonNull URI resource, @NonNull Format format)
157 throws IOException;
158
159 /**
160 * Load data from the provided resource into a bound object.
161 * <p>
162 * This method will auto-detect the format of the provided resource.
163 *
164 * @param <CLASS>
165 * the type of the bound object to return
166 * @param file
167 * the resource
168 * @return a bound object containing the loaded data
169 * @throws IOException
170 * if an error occurred while reading the resource
171 * @see #detectFormat(File)
172 */
173 @NonNull
174 default <CLASS extends IBoundObject> CLASS load(@NonNull File file) throws IOException {
175 return load(ObjectUtils.notNull(file.toPath()));
176 }
177
178 /**
179 * Load data from the provided resource into a bound object.
180 * <p>
181 * This method will auto-detect the format of the provided resource.
182 *
183 * @param <CLASS>
184 * the type of the bound object to return
185 * @param path
186 * the resource
187 * @return a bound object containing the loaded data
188 * @throws IOException
189 * if an error occurred while reading the resource
190 * @see #detectFormat(File)
191 */
192 @NonNull
193 default <CLASS extends IBoundObject> CLASS load(@NonNull Path path) throws IOException {
194 return load(ObjectUtils.notNull(path.toUri()));
195 }
196
197 /**
198 * Load data from the provided resource into a bound object.
199 * <p>
200 * This method will auto-detect the format of the provided resource.
201 *
202 * @param <CLASS>
203 * the type of the bound object to return
204 * @param url
205 * the resource
206 * @return a bound object containing the loaded data
207 * @throws IOException
208 * if an error occurred while reading the resource
209 * @throws URISyntaxException
210 * if the provided {@code url} is malformed
211 * @see #detectFormat(URL)
212 */
213 @NonNull
214 default <CLASS extends IBoundObject> CLASS load(@NonNull URL url) throws IOException, URISyntaxException {
215 return load(ObjectUtils.notNull(url.toURI()));
216 }
217
218 /**
219 * Load data from the resource identified by the provided {@code uri} into a
220 * bound object.
221 * <p>
222 * This method will auto-detect the format of the provided resource.
223 *
224 * @param <CLASS>
225 * the type of the bound object to return
226 * @param uri
227 * the resource
228 * @return a bound object containing the loaded data
229 * @throws IOException
230 * if an error occurred while reading the resource
231 * @see #detectFormat(URL)
232 */
233 @NonNull
234 <CLASS extends IBoundObject> CLASS load(@NonNull URI uri) throws IOException;
235
236 /**
237 * Load data from the provided resource into a bound object.
238 * <p>
239 * This method should auto-detect the format of the provided resource.
240 * <p>
241 * This method will not close the provided {@link InputStream}, since it does
242 * not own the stream.
243 *
244 * @param <CLASS>
245 * the type of the bound object to return
246 * @param is
247 * the resource stream
248 * @param resource
249 * the URI of the resource
250 * @return a bound object containing the loaded data
251 * @throws IOException
252 * if an error occurred while reading the resource
253 * @see #detectFormat(InputStream, URI)
254 */
255 @NonNull
256 <CLASS extends IBoundObject> CLASS load(@NonNull InputStream is, @NonNull URI resource) throws IOException;
257
258 /**
259 * Load data from the specified resource into a bound object with the type of
260 * the specified Java class.
261 *
262 * @param <CLASS>
263 * the Java type to load data into
264 * @param clazz
265 * the class for the java type
266 * @param file
267 * the resource to load
268 * @return the loaded instance data
269 * @throws IOException
270 * if an error occurred while loading the data in the specified file
271 */
272 @NonNull
273 default <CLASS extends IBoundObject> CLASS load(
274 @NonNull Class<CLASS> clazz,
275 @NonNull File file) throws IOException {
276 return load(clazz, ObjectUtils.notNull(file.toPath()));
277 }
278
279 /**
280 * Load data from the specified resource into a bound object with the type of
281 * the specified Java class.
282 *
283 * @param <CLASS>
284 * the Java type to load data into
285 * @param clazz
286 * the class for the java type
287 * @param path
288 * the resource to load
289 * @return the loaded instance data
290 * @throws IOException
291 * if an error occurred while loading the data in the specified file
292 */
293 @NonNull
294 default <CLASS extends IBoundObject> CLASS load(
295 @NonNull Class<CLASS> clazz,
296 @NonNull Path path) throws IOException {
297 return load(clazz, ObjectUtils.notNull(path.toUri()));
298 }
299
300 /**
301 * Load data from the specified resource into a bound object with the type of
302 * the specified Java class.
303 *
304 * @param <CLASS>
305 * the Java type to load data into
306 * @param clazz
307 * the class for the java type
308 * @param url
309 * the resource to load
310 * @return the loaded instance data
311 * @throws IOException
312 * if an error occurred while loading the data in the specified file
313 * @throws URISyntaxException
314 * if the provided {@code url} is malformed
315 */
316 @NonNull
317 default <CLASS extends IBoundObject> CLASS load(
318 @NonNull Class<CLASS> clazz,
319 @NonNull URL url) throws IOException, URISyntaxException {
320 return load(clazz, ObjectUtils.notNull(url.toURI()));
321 }
322
323 /**
324 * Load data from the specified resource into a bound object with the type of
325 * the specified Java class.
326 *
327 * @param <CLASS>
328 * the Java type to load data into
329 * @param clazz
330 * the class for the java type
331 * @param uri
332 * the resource to load
333 * @return the loaded instance data
334 * @throws IOException
335 * if an error occurred while loading the data in the specified file
336 */
337 @NonNull
338 <CLASS extends IBoundObject> CLASS load(
339 @NonNull Class<CLASS> clazz,
340 @NonNull URI uri) throws IOException;
341
342 /**
343 * Load data from the specified resource into a bound object with the type of
344 * the specified Java class.
345 * <p>
346 * This method will not close the provided {@link InputStream}, since it does
347 * not own the stream.
348 * <p>
349 * Implementations of this method will do format detection. This process might
350 * leave the provided {@link InputStream} at a position beyond the last parsed
351 * location. If you want to avoid this possibility, use and implementation of
352 * {@link IDeserializer#deserialize(InputStream, URI)} instead, such as what is
353 * provided by {@link DefaultBindingContext#newDeserializer(Format, Class)}.
354 *
355 * @param <CLASS>
356 * the Java type to load data into
357 * @param clazz
358 * the class for the java type
359 * @param is
360 * the resource stream
361 * @param resource
362 * the URI of the resource
363 * @return the loaded data
364 * @throws IOException
365 * if an error occurred while loading the data from the specified
366 * resource
367 */
368 @NonNull
369 <CLASS extends IBoundObject> CLASS load(
370 @NonNull Class<CLASS> clazz,
371 @NonNull InputStream is,
372 @NonNull URI resource) throws IOException;
373
374 /**
375 * Load data from the specified resource into a bound object with the type of
376 * the specified Java class.
377 * <p>
378 * This method will not close the provided {@link InputStream}, since it does
379 * not own the stream.
380 *
381 * @param <CLASS>
382 * the Java type to load data into
383 * @param format
384 * the format to parse
385 * @param clazz
386 * the class for the java type
387 * @param is
388 * the resource stream
389 * @param resource
390 * the URI of the resource
391 * @return the loaded data
392 * @throws IOException
393 * if an error occurred while loading the data from the specified
394 * resource
395 */
396 @NonNull
397 <CLASS extends IBoundObject> CLASS load(
398 @NonNull Class<CLASS> clazz,
399 @NonNull Format format,
400 @NonNull InputStream is,
401 @NonNull URI resource) throws IOException;
402
403 /**
404 * Load data expressed using the provided {@code format} and return that data as
405 * a Metapath node item.
406 * <p>
407 * The specific Module model is auto-detected by analyzing the source. The class
408 * reported is implementation specific.
409 *
410 * @param format
411 * the expected format of the data to parse
412 * @param path
413 * the resource
414 * @return the Metapath node item for the parsed data
415 * @throws IOException
416 * if an error occurred while loading the data from the specified
417 * resource
418 */
419 @NonNull
420 default IDocumentNodeItem loadAsNodeItem(
421 @NonNull Format format,
422 @NonNull Path path) throws IOException {
423 return loadAsNodeItem(format, ObjectUtils.notNull(path.toUri()));
424 }
425
426 /**
427 * Load data expressed using the provided {@code format} and return that data as
428 * a Metapath node item.
429 * <p>
430 * The specific Module model is auto-detected by analyzing the source. The class
431 * reported is implementation specific.
432 *
433 * @param format
434 * the expected format of the data to parse
435 * @param uri
436 * the resource
437 * @return the Metapath node item for the parsed data
438 * @throws IOException
439 * if an error occurred while loading the data from the specified
440 * resource
441 */
442 @NonNull
443 IDocumentNodeItem loadAsNodeItem(
444 @NonNull Format format,
445 @NonNull URI uri) throws IOException;
446
447 /**
448 * Load data expressed using the provided {@code format} and return that data as
449 * a Metapath node item.
450 * <p>
451 * The specific Module model is auto-detected by analyzing the source. The class
452 * reported is implementation specific.
453 *
454 * @param format
455 * the expected format of the data to parse
456 * @param is
457 * the resource stream
458 * @param resource
459 * the URI of the resource
460 * @return the Metapath node item for the parsed data
461 * @throws IOException
462 * if an error occurred while loading the data from the specified
463 * resource
464 */
465 @NonNull
466 IDocumentNodeItem loadAsNodeItem(
467 @NonNull Format format,
468 @NonNull InputStream is,
469 @NonNull URI resource) throws IOException;
470
471 /**
472 * Get the configured Module binding context to use to load Java types.
473 *
474 * @return the binding context
475 */
476 @NonNull
477 IBindingContext getBindingContext();
478
479 /**
480 * Auto convert the provided {@code source} to the provided {@code toFormat}.
481 * Write the converted content to the provided {@code destination}.
482 * <p>
483 * The format of the source is expected to be auto detected using
484 * {@link #detectFormat(Path)}.
485 *
486 * @param <CLASS>
487 * the Java type to load data into
488 * @param source
489 * the resource to convert
490 * @param destination
491 * the resource to write converted content to
492 * @param toFormat
493 * the format to convert to
494 * @param rootClass
495 * the class for the Java type to load data into
496 * @throws FileNotFoundException
497 * the the provided source file was not found
498 * @throws IOException
499 * if an error occurred while loading the data from the specified
500 * resource or writing the converted data to the specified destination
501 */
502 default <CLASS extends IBoundObject> void convert(
503 @NonNull Path source,
504 @NonNull Path destination,
505 @NonNull Format toFormat,
506 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
507 CLASS object = load(rootClass, source);
508
509 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
510 serializer.serialize(object, destination);
511 }
512
513 /**
514 * Auto convert the provided {@code source} to the provided {@code toFormat}.
515 * Write the converted content to the provided {@code destination}.
516 * <p>
517 * The format of the source is expected to be auto detected using
518 * {@link #detectFormat(Path)}.
519 *
520 * @param <CLASS>
521 * the Java type to load data into
522 * @param source
523 * the resource to convert
524 * @param os
525 * the output stream to write converted content to
526 * @param toFormat
527 * the format to convert to
528 * @param rootClass
529 * the class for the Java type to load data into
530 * @throws FileNotFoundException
531 * the the provided source file was not found
532 * @throws IOException
533 * if an error occurred while loading the data from the specified
534 * resource or writing the converted data to the specified destination
535 */
536 default <CLASS extends IBoundObject> void convert(
537 @NonNull Path source,
538 @NonNull OutputStream os,
539 @NonNull Format toFormat,
540 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
541 CLASS object = load(rootClass, source);
542
543 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
544 serializer.serialize(object, os);
545 }
546
547 /**
548 * Auto convert the provided {@code source} to the provided {@code toFormat}.
549 * Write the converted content to the provided {@code destination}.
550 * <p>
551 * The format of the source is expected to be auto detected using
552 * {@link #detectFormat(Path)}.
553 *
554 * @param <CLASS>
555 * the Java type to load data into
556 * @param source
557 * the resource to convert
558 * @param destination
559 * the resource to write converted content to
560 * @param toFormat
561 * the format to convert to
562 * @param rootClass
563 * the class for the Java type to load data into
564 * @throws FileNotFoundException
565 * the the provided source file was not found
566 * @throws IOException
567 * if an error occurred while loading the data from the specified
568 * resource or writing the converted data to the specified destination
569 */
570 default <CLASS extends IBoundObject> void convert(
571 @NonNull URI source,
572 @NonNull Path destination,
573 @NonNull Format toFormat,
574 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
575 CLASS object = load(rootClass, source);
576
577 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
578 serializer.serialize(object, destination);
579 }
580
581 /**
582 * Auto convert the provided {@code source} to the provided {@code toFormat}.
583 * Write the converted content to the provided {@code destination}.
584 * <p>
585 * The format of the source is expected to be auto detected using
586 * {@link #detectFormat(Path)}.
587 *
588 * @param <CLASS>
589 * the Java type to load data into
590 * @param source
591 * the resource to convert
592 * @param os
593 * the output stream to write converted content to
594 * @param toFormat
595 * the format to convert to
596 * @param rootClass
597 * the class for the Java type to load data into
598 * @throws FileNotFoundException
599 * the the provided source file was not found
600 * @throws IOException
601 * if an error occurred while loading the data from the specified
602 * resource or writing the converted data to the specified destination
603 */
604 default <CLASS extends IBoundObject> void convert(
605 @NonNull URI source,
606 @NonNull OutputStream os,
607 @NonNull Format toFormat,
608 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
609 CLASS object = load(rootClass, source);
610
611 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
612 serializer.serialize(object, os);
613 }
614
615 /**
616 * Auto convert the provided {@code source} to the provided {@code toFormat}.
617 * Write the converted content to the provided {@code destination}.
618 * <p>
619 * The format of the source is expected to be auto detected using
620 * {@link #detectFormat(Path)}.
621 *
622 * @param <CLASS>
623 * the Java type to load data into
624 * @param source
625 * the resource to convert
626 * @param writer
627 * the writer to write converted content to
628 * @param toFormat
629 * the format to convert to
630 * @param rootClass
631 * the class for the Java type to load data into
632 * @throws FileNotFoundException
633 * the the provided source file was not found
634 * @throws IOException
635 * if an error occurred while loading the data from the specified
636 * resource or writing the converted data to the specified destination
637 */
638 default <CLASS extends IBoundObject> void convert(
639 @NonNull URI source,
640 @NonNull Writer writer,
641 @NonNull Format toFormat,
642 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
643 CLASS object = load(rootClass, source);
644
645 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
646 serializer.serialize(object, writer);
647 }
648 }