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 }