001/*
002 * Portions of this software was developed by employees of the National Institute
003 * of Standards and Technology (NIST), an agency of the Federal Government and is
004 * being made available as a public service. Pursuant to title 17 United States
005 * Code Section 105, works of NIST employees are not subject to copyright
006 * protection in the United States. This software may be subject to foreign
007 * copyright. Permission in the United States and in foreign countries, to the
008 * extent that NIST may hold copyright, to use, copy, modify, create derivative
009 * works, and distribute this software and its documentation without fee is hereby
010 * granted on a non-exclusive basis, provided that this notice and disclaimer
011 * of warranty appears in all copies.
012 *
013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
025 */
026
027package gov.nist.secauto.metaschema.databind.io;
028
029import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
030import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
031import gov.nist.secauto.metaschema.core.metapath.IDocumentLoader;
032import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
033import gov.nist.secauto.metaschema.core.util.ObjectUtils;
034import gov.nist.secauto.metaschema.databind.DefaultBindingContext;
035import gov.nist.secauto.metaschema.databind.IBindingContext;
036
037import java.io.File;
038import java.io.FileNotFoundException;
039import java.io.IOException;
040import java.io.InputStream;
041import java.io.OutputStream;
042import java.net.URI;
043import java.net.URISyntaxException;
044import java.net.URL;
045import java.nio.file.Path;
046
047import edu.umd.cs.findbugs.annotations.NonNull;
048
049/**
050 * A common interface for loading Module based instance resources.
051 */
052public interface IBoundLoader extends IDocumentLoader, IMutableConfiguration<DeserializationFeature<?>> {
053
054  @Override
055  default IBoundLoader enableFeature(DeserializationFeature<?> feature) {
056    return set(feature, true);
057  }
058
059  @Override
060  default IBoundLoader disableFeature(DeserializationFeature<?> feature) {
061    return set(feature, false);
062  }
063
064  @Override
065  IBoundLoader applyConfiguration(IConfiguration<DeserializationFeature<?>> other);
066
067  @Override
068  IBoundLoader set(DeserializationFeature<?> feature, Object value);
069
070  /**
071   * Determine the format of the provided resource.
072   *
073   * @param file
074   *          the resource
075   * @return the format information for the provided resource
076   * @throws IOException
077   *           if an error occurred while reading the resource
078   */
079  @NonNull
080  default FormatDetector.Result detectFormat(@NonNull File file) throws IOException {
081    return detectFormat(ObjectUtils.notNull(file.toPath()));
082  }
083
084  /**
085   * Determine the format of the provided resource.
086   *
087   * @param path
088   *          the resource
089   * @return the format information for the provided resource
090   * @throws IOException
091   *           if an error occurred while reading the resource
092   */
093  @NonNull
094  default FormatDetector.Result detectFormat(@NonNull Path path) throws IOException {
095    return detectFormat(ObjectUtils.notNull(path.toUri()));
096  }
097
098  /**
099   * Determine the format of the provided resource.
100   *
101   * @param url
102   *          the resource
103   * @return the format information for the provided resource
104   * @throws IOException
105   *           if an error occurred while reading the resource
106   */
107  @NonNull
108  default FormatDetector.Result detectFormat(@NonNull URL url) throws IOException {
109    try {
110      return detectFormat(ObjectUtils.notNull(url.toURI()));
111    } catch (URISyntaxException ex) {
112      throw new IOException(ex);
113    }
114  }
115
116  /**
117   * Determine the format of the resource identified by the provided {@code uri}.
118   *
119   * @param uri
120   *          the resource
121   * @return the format information for the provided resource
122   * @throws IOException
123   *           if an error occurred while reading the resource
124   */
125  @NonNull
126  FormatDetector.Result detectFormat(@NonNull URI uri) throws IOException;
127
128  /**
129   * Determine the format of the provided resource.
130   * <p>
131   * This method will consume data from the provided {@link InputStream}. If the
132   * caller of this method intends to read data from the stream after determining
133   * the format, the caller should pass in a stream that can be reset.
134   * <p>
135   * This method will not close the provided {@link InputStream}, since it does
136   * not own the stream.
137   *
138   * @param is
139   *          an input stream for the resource
140   * @return the format information for the provided resource
141   * @throws IOException
142   *           if an error occurred while reading the resource
143   */
144  @NonNull
145  FormatDetector.Result detectFormat(@NonNull InputStream is) throws IOException;
146
147  //
148  // /**
149  // * Determine the format of the provided resource.
150  // * <p>
151  // * This method will consume data from any {@link InputStream} provided by the
152  // * {@link InputSource}. If the caller of this method intends to read data from
153  // * the stream after determining the format, the caller should pass in a stream
154  // * that can be reset.
155  // * <p>
156  // * This method will not close any {@link InputStream} provided by the
157  // * {@link InputSource}, since it does not own the stream.
158  // *
159  // * @param source
160  // * information about how to access the resource
161  // * @return the format of the provided resource
162  // * @throws IOException
163  // * if an error occurred while reading the resource
164  // */
165  // @NonNull
166  // Format detectFormat(@NonNull InputSource source) throws IOException;
167
168  /**
169   * Load data from the provided resource into a bound object.
170   * <p>
171   * This method will auto-detect the format of the provided resource.
172   *
173   * @param <CLASS>
174   *          the type of the bound object to return
175   * @param file
176   *          the resource
177   * @return a bound object containing the loaded data
178   * @throws IOException
179   *           if an error occurred while reading the resource
180   * @see #detectFormat(File)
181   */
182  @NonNull
183  default <CLASS> CLASS load(@NonNull File file) throws IOException {
184    return load(ObjectUtils.notNull(file.toPath()));
185  }
186
187  /**
188   * Load data from the provided resource into a bound object.
189   * <p>
190   * This method will auto-detect the format of the provided resource.
191   *
192   * @param <CLASS>
193   *          the type of the bound object to return
194   * @param path
195   *          the resource
196   * @return a bound object containing the loaded data
197   * @throws IOException
198   *           if an error occurred while reading the resource
199   * @see #detectFormat(File)
200   */
201  @NonNull
202  default <CLASS> CLASS load(@NonNull Path path) throws IOException {
203    return load(ObjectUtils.notNull(path.toUri()));
204  }
205
206  /**
207   * Load data from the provided resource into a bound object.
208   * <p>
209   * This method will auto-detect the format of the provided resource.
210   *
211   * @param <CLASS>
212   *          the type of the bound object to return
213   * @param url
214   *          the resource
215   * @return a bound object containing the loaded data
216   * @throws IOException
217   *           if an error occurred while reading the resource
218   * @throws URISyntaxException
219   *           if the provided {@code url} is malformed
220   * @see #detectFormat(URL)
221   */
222  @NonNull
223  default <CLASS> CLASS load(@NonNull URL url) throws IOException, URISyntaxException {
224    return load(ObjectUtils.notNull(url.toURI()));
225  }
226
227  /**
228   * Load data from the resource identified by the provided {@code uri} into a
229   * bound object.
230   * <p>
231   * This method will auto-detect the format of the provided resource.
232   *
233   * @param <CLASS>
234   *          the type of the bound object to return
235   * @param uri
236   *          the resource
237   * @return a bound object containing the loaded data
238   * @throws IOException
239   *           if an error occurred while reading the resource
240   * @see #detectFormat(URL)
241   */
242  @NonNull
243  <CLASS> CLASS load(@NonNull URI uri) throws IOException;
244
245  /**
246   * Load data from the provided resource into a bound object.
247   * <p>
248   * This method should auto-detect the format of the provided resource.
249   * <p>
250   * This method will not close the provided {@link InputStream}, since it does
251   * not own the stream.
252   *
253   * @param <CLASS>
254   *          the type of the bound object to return
255   * @param is
256   *          the resource stream
257   * @param documentUri
258   *          the URI of the resource
259   * @return a bound object containing the loaded data
260   * @throws IOException
261   *           if an error occurred while reading the resource
262   * @see #detectFormat(InputStream)
263   */
264  @NonNull
265  <CLASS> CLASS load(@NonNull InputStream is, @NonNull URI documentUri) throws IOException;
266
267  /**
268   * Load data from the specified resource into a bound object with the type of
269   * the specified Java class.
270   *
271   * @param <CLASS>
272   *          the Java type to load data into
273   * @param clazz
274   *          the class for the java type
275   * @param file
276   *          the resource to load
277   * @return the loaded instance data
278   * @throws IOException
279   *           if an error occurred while loading the data in the specified file
280   */
281  @NonNull
282  default <CLASS> CLASS load(
283      @NonNull Class<CLASS> clazz,
284      @NonNull File file) throws IOException {
285    return load(clazz, ObjectUtils.notNull(file.toPath()));
286  }
287
288  /**
289   * Load data from the specified resource into a bound object with the type of
290   * the specified Java class.
291   *
292   * @param <CLASS>
293   *          the Java type to load data into
294   * @param clazz
295   *          the class for the java type
296   * @param path
297   *          the resource to load
298   * @return the loaded instance data
299   * @throws IOException
300   *           if an error occurred while loading the data in the specified file
301   */
302  @NonNull
303  default <CLASS> CLASS load(
304      @NonNull Class<CLASS> clazz,
305      @NonNull Path path) throws IOException {
306    return load(clazz, ObjectUtils.notNull(path.toUri()));
307  }
308
309  /**
310   * Load data from the specified resource into a bound object with the type of
311   * the specified Java class.
312   *
313   * @param <CLASS>
314   *          the Java type to load data into
315   * @param clazz
316   *          the class for the java type
317   * @param url
318   *          the resource to load
319   * @return the loaded instance data
320   * @throws IOException
321   *           if an error occurred while loading the data in the specified file
322   * @throws URISyntaxException
323   *           if the provided {@code url} is malformed
324   */
325  @NonNull
326  default <CLASS> CLASS load(
327      @NonNull Class<CLASS> clazz,
328      @NonNull URL url) throws IOException, URISyntaxException {
329    return load(clazz, ObjectUtils.notNull(url.toURI()));
330  }
331
332  /**
333   * Load data from the specified resource into a bound object with the type of
334   * the specified Java class.
335   *
336   * @param <CLASS>
337   *          the Java type to load data into
338   * @param clazz
339   *          the class for the java type
340   * @param uri
341   *          the resource to load
342   * @return the loaded instance data
343   * @throws IOException
344   *           if an error occurred while loading the data in the specified file
345   */
346  @NonNull
347  <CLASS> CLASS load(
348      @NonNull Class<CLASS> clazz,
349      @NonNull URI uri) throws IOException;
350
351  /**
352   * Load data from the specified resource into a bound object with the type of
353   * the specified Java class.
354   * <p>
355   * This method will not close the provided {@link InputStream}, since it does
356   * not own the stream.
357   * <p>
358   * Implementations of this method will do format detection. This process might
359   * leave the provided {@link InputStream} at a position beyond the last parsed
360   * location. If you want to avoid this possibility, use and implementation of
361   * {@link IDeserializer#deserialize(InputStream, URI)} instead, such as what is
362   * provided by {@link DefaultBindingContext#newDeserializer(Format, Class)}.
363   *
364   * @param <CLASS>
365   *          the Java type to load data into
366   * @param clazz
367   *          the class for the java type
368   * @param is
369   *          the resource stream
370   * @param documentUri
371   *          the URI of the resource
372   * @return the loaded data
373   * @throws IOException
374   *           if an error occurred while loading the data from the specified
375   *           resource
376   */
377  @NonNull
378  <CLASS> CLASS load(
379      @NonNull Class<CLASS> clazz,
380      @NonNull InputStream is,
381      @NonNull URI documentUri) throws IOException;
382
383  /**
384   * Load data expressed using the provided {@code format} and return that data as
385   * a Metapath node item.
386   * <p>
387   * The specific Module model is auto-detected by analyzing the source. The class
388   * reported is implementation specific.
389   *
390   * @param format
391   *          the expected format of the data to parse
392   * @param path
393   *          the resource
394   * @return the Metapath node item for the parsed data
395   * @throws IOException
396   *           if an error occurred while loading the data from the specified
397   *           resource
398   */
399  @NonNull
400  default IDocumentNodeItem loadAsNodeItem(
401      @NonNull Format format,
402      @NonNull Path path) throws IOException {
403    return loadAsNodeItem(format, ObjectUtils.notNull(path.toUri()));
404  }
405
406  /**
407   * Load data expressed using the provided {@code format} and return that data as
408   * a Metapath node item.
409   * <p>
410   * The specific Module model is auto-detected by analyzing the source. The class
411   * reported is implementation specific.
412   *
413   * @param format
414   *          the expected format of the data to parse
415   * @param uri
416   *          the resource
417   * @return the Metapath node item for the parsed data
418   * @throws IOException
419   *           if an error occurred while loading the data from the specified
420   *           resource
421   */
422  @NonNull
423  IDocumentNodeItem loadAsNodeItem(
424      @NonNull Format format,
425      @NonNull URI uri) throws IOException;
426
427  /**
428   * Load data expressed using the provided {@code format} and return that data as
429   * a Metapath node item.
430   * <p>
431   * The specific Module model is auto-detected by analyzing the source. The class
432   * reported is implementation specific.
433   *
434   * @param format
435   *          the expected format of the data to parse
436   * @param is
437   *          the resource stream
438   * @param documentUri
439   *          the URI of the resource
440   * @return the Metapath node item for the parsed data
441   * @throws IOException
442   *           if an error occurred while loading the data from the specified
443   *           resource
444   */
445  @NonNull
446  IDocumentNodeItem loadAsNodeItem(
447      @NonNull Format format,
448      @NonNull InputStream is,
449      @NonNull URI documentUri) throws IOException;
450
451  /**
452   * Get the configured Module binding context to use to load Java types.
453   *
454   * @return the binding context
455   */
456  @NonNull
457  IBindingContext getBindingContext();
458
459  /**
460   * Auto convert the provided {@code source} to the provided {@code toFormat}.
461   * Write the converted content to the provided {@code destination}.
462   * <p>
463   * The format of the source is expected to be auto detected using
464   * {@link #detectFormat(Path)}.
465   *
466   * @param <CLASS>
467   *          the Java type to load data into
468   * @param source
469   *          the resource to convert
470   * @param destination
471   *          the resource to write converted content to
472   * @param toFormat
473   *          the format to convert to
474   * @param rootClass
475   *          the class for the Java type to load data into
476   * @throws FileNotFoundException
477   *           the the provided source file was not found
478   * @throws IOException
479   *           if an error occurred while loading the data from the specified
480   *           resource or writing the converted data to the specified destination
481   */
482  default <CLASS> void convert(
483      @NonNull Path source,
484      @NonNull Path destination,
485      @NonNull Format toFormat,
486      @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
487    CLASS object = load(rootClass, source);
488
489    ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
490    serializer.serialize(object, destination);
491  }
492
493  /**
494   * Auto convert the provided {@code source} to the provided {@code toFormat}.
495   * Write the converted content to the provided {@code destination}.
496   * <p>
497   * The format of the source is expected to be auto detected using
498   * {@link #detectFormat(Path)}.
499   *
500   * @param <CLASS>
501   *          the Java type to load data into
502   * @param source
503   *          the resource to convert
504   * @param os
505   *          the output stream to write converted content to
506   * @param toFormat
507   *          the format to convert to
508   * @param rootClass
509   *          the class for the Java type to load data into
510   * @throws FileNotFoundException
511   *           the the provided source file was not found
512   * @throws IOException
513   *           if an error occurred while loading the data from the specified
514   *           resource or writing the converted data to the specified destination
515   */
516  default <CLASS> void convert(
517      @NonNull Path source,
518      @NonNull OutputStream os,
519      @NonNull Format toFormat,
520      @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
521    CLASS object = load(rootClass, source);
522
523    ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
524    serializer.serialize(object, os);
525  }
526}