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 * @return the format information for the provided resource 125 * @throws IOException 126 * if an error occurred while reading the resource 127 */ 128 @NonNull 129 FormatDetector.Result detectFormat(@NonNull InputStream is) throws IOException; 130 131 /** 132 * Determine the model of the provided resource. 133 * <p> 134 * This method will consume data from any {@link InputStream} provided by the 135 * {@link InputSource}. If the caller of this method intends to read data from 136 * the stream after determining the format, the caller should pass in a stream 137 * that can be reset. 138 * <p> 139 * This method will not close any {@link InputStream} provided by the 140 * {@link InputSource}, since it does not own the stream. 141 * 142 * @param is 143 * an input stream for the resource 144 * @param format 145 * the format of the provided resource 146 * @return the model of the provided resource 147 * @throws IOException 148 * if an error occurred while reading the resource 149 */ 150 @NonNull 151 @Owning 152 ModelDetector.Result detectModel(@NonNull InputStream is, @NonNull Format format) throws IOException; 153 154 /** 155 * Load data from the provided resource into a bound object. 156 * <p> 157 * This method will auto-detect the format of the provided resource. 158 * 159 * @param <CLASS> 160 * the type of the bound object to return 161 * @param file 162 * the resource 163 * @return a bound object containing the loaded data 164 * @throws IOException 165 * if an error occurred while reading the resource 166 * @see #detectFormat(File) 167 */ 168 @NonNull 169 default <CLASS extends IBoundObject> CLASS load(@NonNull File file) throws IOException { 170 return load(ObjectUtils.notNull(file.toPath())); 171 } 172 173 /** 174 * Load data from the provided resource into a bound object. 175 * <p> 176 * This method will auto-detect the format of the provided resource. 177 * 178 * @param <CLASS> 179 * the type of the bound object to return 180 * @param path 181 * the resource 182 * @return a bound object containing the loaded data 183 * @throws IOException 184 * if an error occurred while reading the resource 185 * @see #detectFormat(File) 186 */ 187 @NonNull 188 default <CLASS extends IBoundObject> CLASS load(@NonNull Path path) throws IOException { 189 return load(ObjectUtils.notNull(path.toUri())); 190 } 191 192 /** 193 * Load data from the provided resource into a bound object. 194 * <p> 195 * This method will auto-detect the format of the provided resource. 196 * 197 * @param <CLASS> 198 * the type of the bound object to return 199 * @param url 200 * the resource 201 * @return a bound object containing the loaded data 202 * @throws IOException 203 * if an error occurred while reading the resource 204 * @throws URISyntaxException 205 * if the provided {@code url} is malformed 206 * @see #detectFormat(URL) 207 */ 208 @NonNull 209 default <CLASS extends IBoundObject> CLASS load(@NonNull URL url) throws IOException, URISyntaxException { 210 return load(ObjectUtils.notNull(url.toURI())); 211 } 212 213 /** 214 * Load data from the resource identified by the provided {@code uri} into a 215 * bound object. 216 * <p> 217 * This method will auto-detect the format of the provided resource. 218 * 219 * @param <CLASS> 220 * the type of the bound object to return 221 * @param uri 222 * the resource 223 * @return a bound object containing the loaded data 224 * @throws IOException 225 * if an error occurred while reading the resource 226 * @see #detectFormat(URL) 227 */ 228 @NonNull 229 <CLASS extends IBoundObject> CLASS load(@NonNull URI uri) throws IOException; 230 231 /** 232 * Load data from the provided resource into a bound object. 233 * <p> 234 * This method should auto-detect the format of the provided resource. 235 * <p> 236 * This method will not close the provided {@link InputStream}, since it does 237 * not own the stream. 238 * 239 * @param <CLASS> 240 * the type of the bound object to return 241 * @param is 242 * the resource stream 243 * @param documentUri 244 * the URI of the resource 245 * @return a bound object containing the loaded data 246 * @throws IOException 247 * if an error occurred while reading the resource 248 * @see #detectFormat(InputStream) 249 */ 250 @NonNull 251 <CLASS extends IBoundObject> CLASS load(@NonNull InputStream is, @NonNull URI documentUri) throws IOException; 252 253 /** 254 * Load data from the specified resource into a bound object with the type of 255 * the specified Java class. 256 * 257 * @param <CLASS> 258 * the Java type to load data into 259 * @param clazz 260 * the class for the java type 261 * @param file 262 * the resource to load 263 * @return the loaded instance data 264 * @throws IOException 265 * if an error occurred while loading the data in the specified file 266 */ 267 @NonNull 268 default <CLASS extends IBoundObject> CLASS load( 269 @NonNull Class<CLASS> clazz, 270 @NonNull File file) throws IOException { 271 return load(clazz, ObjectUtils.notNull(file.toPath())); 272 } 273 274 /** 275 * Load data from the specified resource into a bound object with the type of 276 * the specified Java class. 277 * 278 * @param <CLASS> 279 * the Java type to load data into 280 * @param clazz 281 * the class for the java type 282 * @param path 283 * the resource to load 284 * @return the loaded instance data 285 * @throws IOException 286 * if an error occurred while loading the data in the specified file 287 */ 288 @NonNull 289 default <CLASS extends IBoundObject> CLASS load( 290 @NonNull Class<CLASS> clazz, 291 @NonNull Path path) throws IOException { 292 return load(clazz, ObjectUtils.notNull(path.toUri())); 293 } 294 295 /** 296 * Load data from the specified resource into a bound object with the type of 297 * the specified Java class. 298 * 299 * @param <CLASS> 300 * the Java type to load data into 301 * @param clazz 302 * the class for the java type 303 * @param url 304 * the resource to load 305 * @return the loaded instance data 306 * @throws IOException 307 * if an error occurred while loading the data in the specified file 308 * @throws URISyntaxException 309 * if the provided {@code url} is malformed 310 */ 311 @NonNull 312 default <CLASS extends IBoundObject> CLASS load( 313 @NonNull Class<CLASS> clazz, 314 @NonNull URL url) throws IOException, URISyntaxException { 315 return load(clazz, ObjectUtils.notNull(url.toURI())); 316 } 317 318 /** 319 * Load data from the specified resource into a bound object with the type of 320 * the specified Java class. 321 * 322 * @param <CLASS> 323 * the Java type to load data into 324 * @param clazz 325 * the class for the java type 326 * @param uri 327 * the resource to load 328 * @return the loaded instance data 329 * @throws IOException 330 * if an error occurred while loading the data in the specified file 331 */ 332 @NonNull 333 <CLASS extends IBoundObject> CLASS load( 334 @NonNull Class<CLASS> clazz, 335 @NonNull URI uri) throws IOException; 336 337 /** 338 * Load data from the specified resource into a bound object with the type of 339 * the specified Java class. 340 * <p> 341 * This method will not close the provided {@link InputStream}, since it does 342 * not own the stream. 343 * <p> 344 * Implementations of this method will do format detection. This process might 345 * leave the provided {@link InputStream} at a position beyond the last parsed 346 * location. If you want to avoid this possibility, use and implementation of 347 * {@link IDeserializer#deserialize(InputStream, URI)} instead, such as what is 348 * provided by {@link DefaultBindingContext#newDeserializer(Format, Class)}. 349 * 350 * @param <CLASS> 351 * the Java type to load data into 352 * @param clazz 353 * the class for the java type 354 * @param is 355 * the resource stream 356 * @param documentUri 357 * the URI of the resource 358 * @return the loaded data 359 * @throws IOException 360 * if an error occurred while loading the data from the specified 361 * resource 362 */ 363 @NonNull 364 <CLASS extends IBoundObject> CLASS load( 365 @NonNull Class<CLASS> clazz, 366 @NonNull InputStream is, 367 @NonNull URI documentUri) throws IOException; 368 369 /** 370 * Load data from the specified resource into a bound object with the type of 371 * the specified Java class. 372 * <p> 373 * This method will not close the provided {@link InputStream}, since it does 374 * not own the stream. 375 * 376 * @param <CLASS> 377 * the Java type to load data into 378 * @param format 379 * the format to parse 380 * @param clazz 381 * the class for the java type 382 * @param is 383 * the resource stream 384 * @param documentUri 385 * the URI of the resource 386 * @return the loaded data 387 * @throws IOException 388 * if an error occurred while loading the data from the specified 389 * resource 390 */ 391 @NonNull 392 <CLASS extends IBoundObject> CLASS load( 393 @NonNull Class<CLASS> clazz, 394 @NonNull Format format, 395 @NonNull InputStream is, 396 @NonNull URI documentUri) throws IOException; 397 398 /** 399 * Load data expressed using the provided {@code format} and return that data as 400 * a Metapath node item. 401 * <p> 402 * The specific Module model is auto-detected by analyzing the source. The class 403 * reported is implementation specific. 404 * 405 * @param format 406 * the expected format of the data to parse 407 * @param path 408 * the resource 409 * @return the Metapath node item for the parsed data 410 * @throws IOException 411 * if an error occurred while loading the data from the specified 412 * resource 413 */ 414 @NonNull 415 default IDocumentNodeItem loadAsNodeItem( 416 @NonNull Format format, 417 @NonNull Path path) throws IOException { 418 return loadAsNodeItem(format, ObjectUtils.notNull(path.toUri())); 419 } 420 421 /** 422 * Load data expressed using the provided {@code format} and return that data as 423 * a Metapath node item. 424 * <p> 425 * The specific Module model is auto-detected by analyzing the source. The class 426 * reported is implementation specific. 427 * 428 * @param format 429 * the expected format of the data to parse 430 * @param uri 431 * the resource 432 * @return the Metapath node item for the parsed data 433 * @throws IOException 434 * if an error occurred while loading the data from the specified 435 * resource 436 */ 437 @NonNull 438 IDocumentNodeItem loadAsNodeItem( 439 @NonNull Format format, 440 @NonNull URI uri) throws IOException; 441 442 /** 443 * Load data expressed using the provided {@code format} and return that data as 444 * a Metapath node item. 445 * <p> 446 * The specific Module model is auto-detected by analyzing the source. The class 447 * reported is implementation specific. 448 * 449 * @param format 450 * the expected format of the data to parse 451 * @param is 452 * the resource stream 453 * @param documentUri 454 * the URI of the resource 455 * @return the Metapath node item for the parsed data 456 * @throws IOException 457 * if an error occurred while loading the data from the specified 458 * resource 459 */ 460 @NonNull 461 IDocumentNodeItem loadAsNodeItem( 462 @NonNull Format format, 463 @NonNull InputStream is, 464 @NonNull URI documentUri) throws IOException; 465 466 /** 467 * Get the configured Module binding context to use to load Java types. 468 * 469 * @return the binding context 470 */ 471 @NonNull 472 IBindingContext getBindingContext(); 473 474 /** 475 * Auto convert the provided {@code source} to the provided {@code toFormat}. 476 * Write the converted content to the provided {@code destination}. 477 * <p> 478 * The format of the source is expected to be auto detected using 479 * {@link #detectFormat(Path)}. 480 * 481 * @param <CLASS> 482 * the Java type to load data into 483 * @param source 484 * the resource to convert 485 * @param destination 486 * the resource to write converted content to 487 * @param toFormat 488 * the format to convert to 489 * @param rootClass 490 * the class for the Java type to load data into 491 * @throws FileNotFoundException 492 * the the provided source file was not found 493 * @throws IOException 494 * if an error occurred while loading the data from the specified 495 * resource or writing the converted data to the specified destination 496 */ 497 default <CLASS extends IBoundObject> void convert( 498 @NonNull Path source, 499 @NonNull Path destination, 500 @NonNull Format toFormat, 501 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException { 502 CLASS object = load(rootClass, source); 503 504 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass); 505 serializer.serialize(object, destination); 506 } 507 508 /** 509 * Auto convert the provided {@code source} to the provided {@code toFormat}. 510 * Write the converted content to the provided {@code destination}. 511 * <p> 512 * The format of the source is expected to be auto detected using 513 * {@link #detectFormat(Path)}. 514 * 515 * @param <CLASS> 516 * the Java type to load data into 517 * @param source 518 * the resource to convert 519 * @param os 520 * the output stream to write converted content to 521 * @param toFormat 522 * the format to convert to 523 * @param rootClass 524 * the class for the Java type to load data into 525 * @throws FileNotFoundException 526 * the the provided source file was not found 527 * @throws IOException 528 * if an error occurred while loading the data from the specified 529 * resource or writing the converted data to the specified destination 530 */ 531 default <CLASS extends IBoundObject> void convert( 532 @NonNull Path source, 533 @NonNull OutputStream os, 534 @NonNull Format toFormat, 535 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException { 536 CLASS object = load(rootClass, source); 537 538 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass); 539 serializer.serialize(object, os); 540 } 541 542 /** 543 * Auto convert the provided {@code source} to the provided {@code toFormat}. 544 * Write the converted content to the provided {@code destination}. 545 * <p> 546 * The format of the source is expected to be auto detected using 547 * {@link #detectFormat(Path)}. 548 * 549 * @param <CLASS> 550 * the Java type to load data into 551 * @param source 552 * the resource to convert 553 * @param destination 554 * the resource to write converted content to 555 * @param toFormat 556 * the format to convert to 557 * @param rootClass 558 * the class for the Java type to load data into 559 * @throws FileNotFoundException 560 * the the provided source file was not found 561 * @throws IOException 562 * if an error occurred while loading the data from the specified 563 * resource or writing the converted data to the specified destination 564 */ 565 default <CLASS extends IBoundObject> void convert( 566 @NonNull URI source, 567 @NonNull Path destination, 568 @NonNull Format toFormat, 569 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException { 570 CLASS object = load(rootClass, source); 571 572 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass); 573 serializer.serialize(object, destination); 574 } 575 576 /** 577 * Auto convert the provided {@code source} to the provided {@code toFormat}. 578 * Write the converted content to the provided {@code destination}. 579 * <p> 580 * The format of the source is expected to be auto detected using 581 * {@link #detectFormat(Path)}. 582 * 583 * @param <CLASS> 584 * the Java type to load data into 585 * @param source 586 * the resource to convert 587 * @param os 588 * the output stream to write converted content to 589 * @param toFormat 590 * the format to convert to 591 * @param rootClass 592 * the class for the Java type to load data into 593 * @throws FileNotFoundException 594 * the the provided source file was not found 595 * @throws IOException 596 * if an error occurred while loading the data from the specified 597 * resource or writing the converted data to the specified destination 598 */ 599 default <CLASS extends IBoundObject> void convert( 600 @NonNull URI source, 601 @NonNull OutputStream os, 602 @NonNull Format toFormat, 603 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException { 604 CLASS object = load(rootClass, source); 605 606 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass); 607 serializer.serialize(object, os); 608 } 609 610 /** 611 * Auto convert the provided {@code source} to the provided {@code toFormat}. 612 * Write the converted content to the provided {@code destination}. 613 * <p> 614 * The format of the source is expected to be auto detected using 615 * {@link #detectFormat(Path)}. 616 * 617 * @param <CLASS> 618 * the Java type to load data into 619 * @param source 620 * the resource to convert 621 * @param writer 622 * the writer to write converted content to 623 * @param toFormat 624 * the format to convert to 625 * @param rootClass 626 * the class for the Java type to load data into 627 * @throws FileNotFoundException 628 * the the provided source file was not found 629 * @throws IOException 630 * if an error occurred while loading the data from the specified 631 * resource or writing the converted data to the specified destination 632 */ 633 default <CLASS extends IBoundObject> void convert( 634 @NonNull URI source, 635 @NonNull Writer writer, 636 @NonNull Format toFormat, 637 @NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException { 638 CLASS object = load(rootClass, source); 639 640 ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass); 641 serializer.serialize(object, writer); 642 } 643 }