1 /* 2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.imageio.spi; 27 28 import java.io.IOException; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Module; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.util.Arrays; 35 import java.util.Iterator; 36 import javax.imageio.ImageReader; 37 import javax.imageio.metadata.IIOMetadata; 38 import javax.imageio.metadata.IIOMetadataFormat; 39 import javax.imageio.metadata.IIOMetadataFormatImpl; 40 import javax.imageio.stream.ImageInputStream; 41 42 /** 43 * A superclass containing instance variables and methods common to 44 * {@code ImageReaderSpi} and {@code ImageWriterSpi}. 45 * 46 * @see IIORegistry 47 * @see ImageReaderSpi 48 * @see ImageWriterSpi 49 * 50 */ 51 public abstract class ImageReaderWriterSpi extends IIOServiceProvider { 52 53 /** 54 * An array of strings to be returned from 55 * {@code getFormatNames}, initially {@code null}. 56 * Constructors should set this to a non-{@code null} value. 57 */ 58 protected String[] names = null; 59 60 /** 61 * An array of strings to be returned from 62 * {@code getFileSuffixes}, initially {@code null}. 63 */ 64 protected String[] suffixes = null; 65 66 /** 67 * An array of strings to be returned from 68 * {@code getMIMETypes}, initially {@code null}. 69 */ 70 protected String[] MIMETypes = null; 71 72 /** 73 * A {@code String} containing the name of the associated 74 * plug-in class, initially {@code null}. 75 */ 76 protected String pluginClassName = null; 77 78 /** 79 * A boolean indicating whether this plug-in supports the 80 * standard metadata format for stream metadata, initially 81 * {@code false}. 82 */ 83 protected boolean supportsStandardStreamMetadataFormat = false; 84 85 /** 86 * A {@code String} containing the name of the native stream 87 * metadata format supported by this plug-in, initially 88 * {@code null}. 89 */ 90 protected String nativeStreamMetadataFormatName = null; 91 92 /** 93 * A {@code String} containing the class name of the native 94 * stream metadata format supported by this plug-in, initially 95 * {@code null}. 96 */ 97 protected String nativeStreamMetadataFormatClassName = null; 98 99 /** 100 * An array of {@code String}s containing the names of any 101 * additional stream metadata formats supported by this plug-in, 102 * initially {@code null}. 103 */ 104 protected String[] extraStreamMetadataFormatNames = null; 105 106 /** 107 * An array of {@code String}s containing the class names of 108 * any additional stream metadata formats supported by this plug-in, 109 * initially {@code null}. 110 */ 111 protected String[] extraStreamMetadataFormatClassNames = null; 112 113 /** 114 * A boolean indicating whether this plug-in supports the 115 * standard metadata format for image metadata, initially 116 * {@code false}. 117 */ 118 protected boolean supportsStandardImageMetadataFormat = false; 119 120 /** 121 * A {@code String} containing the name of the 122 * native stream metadata format supported by this plug-in, 123 * initially {@code null}. 124 */ 125 protected String nativeImageMetadataFormatName = null; 126 127 /** 128 * A {@code String} containing the class name of the 129 * native stream metadata format supported by this plug-in, 130 * initially {@code null}. 131 */ 132 protected String nativeImageMetadataFormatClassName = null; 133 134 /** 135 * An array of {@code String}s containing the names of any 136 * additional image metadata formats supported by this plug-in, 137 * initially {@code null}. 138 */ 139 protected String[] extraImageMetadataFormatNames = null; 140 141 /** 142 * An array of {@code String}s containing the class names of 143 * any additional image metadata formats supported by this 144 * plug-in, initially {@code null}. 145 */ 146 protected String[] extraImageMetadataFormatClassNames = null; 147 148 /** 149 * Constructs an {@code ImageReaderWriterSpi} with a given 150 * set of values. 151 * 152 * @param vendorName the vendor name, as a non-{@code null} 153 * {@code String}. 154 * @param version a version identifier, as a non-{@code null} 155 * {@code String}. 156 * @param names a non-{@code null} array of 157 * {@code String}s indicating the format names. At least one 158 * entry must be present. 159 * @param suffixes an array of {@code String}s indicating the 160 * common file suffixes. If no suffixes are defined, 161 * {@code null} should be supplied. An array of length 0 162 * will be normalized to {@code null}. 163 * @param MIMETypes an array of {@code String}s indicating 164 * the format's MIME types. If no MIME types are defined, 165 * {@code null} should be supplied. An array of length 0 166 * will be normalized to {@code null}. 167 * @param pluginClassName the fully-qualified name of the 168 * associated {@code ImageReader} or {@code ImageWriter} 169 * class, as a non-{@code null String}. 170 * @param supportsStandardStreamMetadataFormat a 171 * {@code boolean} that indicates whether a stream metadata 172 * object can use trees described by the standard metadata format. 173 * @param nativeStreamMetadataFormatName a 174 * {@code String}, or {@code null}, to be returned from 175 * {@code getNativeStreamMetadataFormatName}. 176 * @param nativeStreamMetadataFormatClassName a 177 * {@code String}, or {@code null}, to be used to instantiate 178 * a metadata format object to be returned from 179 * {@code getNativeStreamMetadataFormat}. 180 * @param extraStreamMetadataFormatNames an array of 181 * {@code String}s, or {@code null}, to be returned from 182 * {@code getExtraStreamMetadataFormatNames}. An array of length 183 * 0 is normalized to {@code null}. 184 * @param extraStreamMetadataFormatClassNames an array of 185 * {@code String}s, or {@code null}, to be used to instantiate 186 * a metadata format object to be returned from 187 * {@code getStreamMetadataFormat}. An array of length 188 * 0 is normalized to {@code null}. 189 * @param supportsStandardImageMetadataFormat a 190 * {@code boolean} that indicates whether an image metadata 191 * object can use trees described by the standard metadata format. 192 * @param nativeImageMetadataFormatName a 193 * {@code String}, or {@code null}, to be returned from 194 * {@code getNativeImageMetadataFormatName}. 195 * @param nativeImageMetadataFormatClassName a 196 * {@code String}, or {@code null}, to be used to instantiate 197 * a metadata format object to be returned from 198 * {@code getNativeImageMetadataFormat}. 199 * @param extraImageMetadataFormatNames an array of 200 * {@code String}s to be returned from 201 * {@code getExtraImageMetadataFormatNames}. An array of length 0 202 * is normalized to {@code null}. 203 * @param extraImageMetadataFormatClassNames an array of 204 * {@code String}s, or {@code null}, to be used to instantiate 205 * a metadata format object to be returned from 206 * {@code getImageMetadataFormat}. An array of length 207 * 0 is normalized to {@code null}. 208 * 209 * @exception IllegalArgumentException if {@code vendorName} 210 * is {@code null}. 211 * @exception IllegalArgumentException if {@code version} 212 * is {@code null}. 213 * @exception IllegalArgumentException if {@code names} 214 * is {@code null} or has length 0. 215 * @exception IllegalArgumentException if {@code pluginClassName} 216 * is {@code null}. 217 */ 218 public ImageReaderWriterSpi(String vendorName, 219 String version, 220 String[] names, 221 String[] suffixes, 222 String[] MIMETypes, 223 String pluginClassName, 224 boolean supportsStandardStreamMetadataFormat, 225 String nativeStreamMetadataFormatName, 226 String nativeStreamMetadataFormatClassName, 227 String[] extraStreamMetadataFormatNames, 228 String[] extraStreamMetadataFormatClassNames, 229 boolean supportsStandardImageMetadataFormat, 230 String nativeImageMetadataFormatName, 231 String nativeImageMetadataFormatClassName, 232 String[] extraImageMetadataFormatNames, 233 String[] extraImageMetadataFormatClassNames) { 234 super(vendorName, version); 235 if (names == null) { 236 throw new IllegalArgumentException("names == null!"); 237 } 238 if (names.length == 0) { 239 throw new IllegalArgumentException("names.length == 0!"); 240 } 241 if (pluginClassName == null) { 242 throw new IllegalArgumentException("pluginClassName == null!"); 243 } 244 245 this.names = names.clone(); 246 // If length == 0, leave it null 247 if (suffixes != null && suffixes.length > 0) { 248 this.suffixes = suffixes.clone(); 249 } 250 // If length == 0, leave it null 251 if (MIMETypes != null && MIMETypes.length > 0) { 252 this.MIMETypes = MIMETypes.clone(); 253 } 254 this.pluginClassName = pluginClassName; 255 256 this.supportsStandardStreamMetadataFormat = 257 supportsStandardStreamMetadataFormat; 258 this.nativeStreamMetadataFormatName = nativeStreamMetadataFormatName; 259 this.nativeStreamMetadataFormatClassName = 260 nativeStreamMetadataFormatClassName; 261 // If length == 0, leave it null 262 if (extraStreamMetadataFormatNames != null && 263 extraStreamMetadataFormatNames.length > 0) { 264 this.extraStreamMetadataFormatNames = 265 extraStreamMetadataFormatNames.clone(); 266 } 267 // If length == 0, leave it null 268 if (extraStreamMetadataFormatClassNames != null && 269 extraStreamMetadataFormatClassNames.length > 0) { 270 this.extraStreamMetadataFormatClassNames = 271 extraStreamMetadataFormatClassNames.clone(); 272 } 273 this.supportsStandardImageMetadataFormat = 274 supportsStandardImageMetadataFormat; 275 this.nativeImageMetadataFormatName = nativeImageMetadataFormatName; 276 this.nativeImageMetadataFormatClassName = 277 nativeImageMetadataFormatClassName; 278 // If length == 0, leave it null 279 if (extraImageMetadataFormatNames != null && 280 extraImageMetadataFormatNames.length > 0) { 281 this.extraImageMetadataFormatNames = 282 extraImageMetadataFormatNames.clone(); 283 } 284 // If length == 0, leave it null 285 if (extraImageMetadataFormatClassNames != null && 286 extraImageMetadataFormatClassNames.length > 0) { 287 this.extraImageMetadataFormatClassNames = 288 extraImageMetadataFormatClassNames.clone(); 289 } 290 } 291 292 /** 293 * Constructs a blank {@code ImageReaderWriterSpi}. It is up 294 * to the subclass to initialize instance variables and/or 295 * override method implementations in order to provide working 296 * versions of all methods. 297 */ 298 public ImageReaderWriterSpi() { 299 } 300 301 /** 302 * Returns an array of {@code String}s containing 303 * human-readable names for the formats that are generally usable 304 * by the {@code ImageReader} or {@code ImageWriter} 305 * implementation associated with this service provider. For 306 * example, a single {@code ImageReader} might be able to 307 * process both PBM and PNM files. 308 * 309 * @return a non-{@code null} array of {@code String}s 310 * or length at least 1 containing informal format names 311 * associated with this reader or writer. 312 */ 313 public String[] getFormatNames() { 314 return names.clone(); 315 } 316 317 /** 318 * Returns an array of {@code String}s containing a list of 319 * file suffixes associated with the formats that are generally 320 * usable by the {@code ImageReader} or 321 * {@code ImageWriter} implementation associated with this 322 * service provider. For example, a single 323 * {@code ImageReader} might be able to process files with 324 * '.pbm' and '.pnm' suffixes, or both '.jpg' and '.jpeg' 325 * suffixes. If there are no known file suffixes, 326 * {@code null} will be returned. 327 * 328 * <p> Returning a particular suffix does not guarantee that files 329 * with that suffix can be processed; it merely indicates that it 330 * may be worthwhile attempting to decode or encode such files 331 * using this service provider. 332 * 333 * @return an array of {@code String}s or length at least 1 334 * containing common file suffixes associated with this reader or 335 * writer, or {@code null}. 336 */ 337 public String[] getFileSuffixes() { 338 return suffixes == null ? null : suffixes.clone(); 339 } 340 341 /** 342 * Returns an array of {@code String}s containing a list of 343 * MIME types associated with the formats that are generally 344 * usable by the {@code ImageReader} or 345 * {@code ImageWriter} implementation associated with this 346 * service provider. 347 * 348 * <p> Ideally, only a single MIME type would be required in order 349 * to describe a particular format. However, for several reasons 350 * it is necessary to associate a list of types with each service 351 * provider. First, many common image file formats do not have 352 * standard MIME types, so a list of commonly used unofficial 353 * names will be required, such as {@code image/x-pbm} and 354 * {@code image/x-portable-bitmap}. Some file formats have 355 * official MIME types but may sometimes be referred to using 356 * their previous unofficial designations, such as 357 * {@code image/x-png} instead of the official 358 * {@code image/png}. Finally, a single service provider may 359 * be capable of parsing multiple distinct types from the MIME 360 * point of view, for example {@code image/x-xbitmap} and 361 * {@code image/x-xpixmap}. 362 * 363 * <p> Returning a particular MIME type does not guarantee that 364 * files claiming to be of that type can be processed; it merely 365 * indicates that it may be worthwhile attempting to decode or 366 * encode such files using this service provider. 367 * 368 * @return an array of {@code String}s or length at least 1 369 * containing MIME types associated with this reader or writer, or 370 * {@code null}. 371 */ 372 public String[] getMIMETypes() { 373 return MIMETypes == null ? null : MIMETypes.clone(); 374 } 375 376 /** 377 * Returns the fully-qualified class name of the 378 * {@code ImageReader} or {@code ImageWriter} plug-in 379 * associated with this service provider. 380 * 381 * @return the class name, as a non-{@code null} 382 * {@code String}. 383 */ 384 public String getPluginClassName() { 385 return pluginClassName; 386 } 387 388 /** 389 * Returns {@code true} if the standard metadata format is 390 * among the document formats recognized by the 391 * {@code getAsTree} and {@code setFromTree} methods on 392 * the stream metadata objects produced or consumed by this 393 * plug-in. 394 * 395 * @return {@code true} if the standard format is supported 396 * for stream metadata. 397 */ 398 public boolean isStandardStreamMetadataFormatSupported() { 399 return supportsStandardStreamMetadataFormat; 400 } 401 402 /** 403 * Returns the name of the "native" stream metadata format for 404 * this plug-in, which typically allows for lossless encoding and 405 * transmission of the stream metadata stored in the format handled by 406 * this plug-in. If no such format is supported, 407 * {@code null} will be returned. 408 * 409 * <p> The default implementation returns the 410 * {@code nativeStreamMetadataFormatName} instance variable, 411 * which is typically set by the constructor. 412 * 413 * @return the name of the native stream metadata format, or 414 * {@code null}. 415 * 416 */ 417 public String getNativeStreamMetadataFormatName() { 418 return nativeStreamMetadataFormatName; 419 } 420 421 /** 422 * Returns an array of {@code String}s containing the names 423 * of additional document formats, other than the native and 424 * standard formats, recognized by the 425 * {@code getAsTree} and {@code setFromTree} methods on 426 * the stream metadata objects produced or consumed by this 427 * plug-in. 428 * 429 * <p> If the plug-in does not handle metadata, null should be 430 * returned. 431 * 432 * <p> The set of formats may differ according to the particular 433 * images being read or written; this method should indicate all 434 * the additional formats supported by the plug-in under any 435 * circumstances. 436 * 437 * <p> The default implementation returns a clone of the 438 * {@code extraStreamMetadataFormatNames} instance variable, 439 * which is typically set by the constructor. 440 * 441 * @return an array of {@code String}s, or null. 442 * 443 * @see IIOMetadata#getMetadataFormatNames 444 * @see #getExtraImageMetadataFormatNames 445 * @see #getNativeStreamMetadataFormatName 446 */ 447 public String[] getExtraStreamMetadataFormatNames() { 448 return extraStreamMetadataFormatNames == null ? 449 null : extraStreamMetadataFormatNames.clone(); 450 } 451 452 /** 453 * Returns {@code true} if the standard metadata format is 454 * among the document formats recognized by the 455 * {@code getAsTree} and {@code setFromTree} methods on 456 * the image metadata objects produced or consumed by this 457 * plug-in. 458 * 459 * @return {@code true} if the standard format is supported 460 * for image metadata. 461 */ 462 public boolean isStandardImageMetadataFormatSupported() { 463 return supportsStandardImageMetadataFormat; 464 } 465 466 /** 467 * Returns the name of the "native" image metadata format for 468 * this plug-in, which typically allows for lossless encoding and 469 * transmission of the image metadata stored in the format handled by 470 * this plug-in. If no such format is supported, 471 * {@code null} will be returned. 472 * 473 * <p> The default implementation returns the 474 * {@code nativeImageMetadataFormatName} instance variable, 475 * which is typically set by the constructor. 476 * 477 * @return the name of the native image metadata format, or 478 * {@code null}. 479 * 480 * @see #getExtraImageMetadataFormatNames 481 */ 482 public String getNativeImageMetadataFormatName() { 483 return nativeImageMetadataFormatName; 484 } 485 486 /** 487 * Returns an array of {@code String}s containing the names 488 * of additional document formats, other than the native and 489 * standard formats, recognized by the 490 * {@code getAsTree} and {@code setFromTree} methods on 491 * the image metadata objects produced or consumed by this 492 * plug-in. 493 * 494 * <p> If the plug-in does not handle image metadata, null should 495 * be returned. 496 * 497 * <p> The set of formats may differ according to the particular 498 * images being read or written; this method should indicate all 499 * the additional formats supported by the plug-in under any circumstances. 500 * 501 * <p> The default implementation returns a clone of the 502 * {@code extraImageMetadataFormatNames} instance variable, 503 * which is typically set by the constructor. 504 * 505 * @return an array of {@code String}s, or null. 506 * 507 * @see IIOMetadata#getMetadataFormatNames 508 * @see #getExtraStreamMetadataFormatNames 509 * @see #getNativeImageMetadataFormatName 510 */ 511 public String[] getExtraImageMetadataFormatNames() { 512 return extraImageMetadataFormatNames == null ? 513 null : extraImageMetadataFormatNames.clone(); 514 } 515 516 /** 517 * Returns an {@code IIOMetadataFormat} object describing the 518 * given stream metadata format, or {@code null} if no 519 * description is available. The supplied name must be the native 520 * stream metadata format name, the standard metadata format name, 521 * or one of those returned by 522 * {@code getExtraStreamMetadataFormatNames}. 523 * 524 * @param formatName the desired stream metadata format. 525 * 526 * @return an {@code IIOMetadataFormat} object. 527 * 528 * @exception IllegalArgumentException if {@code formatName} 529 * is {@code null} or is not a supported name. 530 */ 531 public IIOMetadataFormat getStreamMetadataFormat(String formatName) { 532 return getMetadataFormat(formatName, 533 supportsStandardStreamMetadataFormat, 534 nativeStreamMetadataFormatName, 535 nativeStreamMetadataFormatClassName, 536 extraStreamMetadataFormatNames, 537 extraStreamMetadataFormatClassNames); 538 } 539 540 /** 541 * Returns an {@code IIOMetadataFormat} object describing the 542 * given image metadata format, or {@code null} if no 543 * description is available. The supplied name must be the native 544 * image metadata format name, the standard metadata format name, 545 * or one of those returned by 546 * {@code getExtraImageMetadataFormatNames}. 547 * 548 * @param formatName the desired image metadata format. 549 * 550 * @return an {@code IIOMetadataFormat} object. 551 * 552 * @exception IllegalArgumentException if {@code formatName} 553 * is {@code null} or is not a supported name. 554 */ 555 public IIOMetadataFormat getImageMetadataFormat(String formatName) { 556 return getMetadataFormat(formatName, 557 supportsStandardImageMetadataFormat, 558 nativeImageMetadataFormatName, 559 nativeImageMetadataFormatClassName, 560 extraImageMetadataFormatNames, 561 extraImageMetadataFormatClassNames); 562 } 563 564 private IIOMetadataFormat getMetadataFormat(String formatName, 565 boolean supportsStandard, 566 String nativeName, 567 String nativeClassName, 568 String [] extraNames, 569 String [] extraClassNames) { 570 if (formatName == null) { 571 throw new IllegalArgumentException("formatName == null!"); 572 } 573 if (supportsStandard && formatName.equals 574 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 575 576 return IIOMetadataFormatImpl.getStandardFormatInstance(); 577 } 578 String formatClassName = null; 579 if (formatName.equals(nativeName)) { 580 formatClassName = nativeClassName; 581 } else if (extraNames != null) { 582 for (int i = 0; i < extraNames.length; i++) { 583 if (formatName.equals(extraNames[i])) { 584 formatClassName = extraClassNames[i]; 585 break; // out of for 586 } 587 } 588 } 589 if (formatClassName == null) { 590 throw new IllegalArgumentException("Unsupported format name"); 591 } 592 try { 593 // Try to load from the same location as the module of the SPI 594 final String className = formatClassName; 595 PrivilegedAction<Class<?>> pa = () -> { return getMetadataFormatClass(className); }; 596 Class<?> cls = AccessController.doPrivileged(pa); 597 Method meth = cls.getMethod("getInstance"); 598 return (IIOMetadataFormat) meth.invoke(null); 599 } catch (Exception e) { 600 RuntimeException ex = 601 new IllegalStateException ("Can't obtain format"); 602 ex.initCause(e); 603 throw ex; 604 } 605 } 606 607 // If updating this method also see the same in IIOMetadata.java 608 private Class<?> getMetadataFormatClass(String formatClassName) { 609 Module thisModule = ImageReaderWriterSpi.class.getModule(); 610 Module targetModule = this.getClass().getModule(); 611 Class<?> c = null; 612 try { 613 ClassLoader cl = this.getClass().getClassLoader(); 614 c = Class.forName(formatClassName, false, cl); 615 if (!IIOMetadataFormat.class.isAssignableFrom(c)) { 616 return null; 617 } 618 } catch (ClassNotFoundException e) { 619 } 620 if (thisModule.equals(targetModule) || c == null) { 621 return c; 622 } 623 if (targetModule.isNamed()) { 624 int i = formatClassName.lastIndexOf("."); 625 String pn = i > 0 ? formatClassName.substring(0, i) : ""; 626 if (!targetModule.isExported(pn, thisModule)) { 627 throw new IllegalStateException("Class " + formatClassName + 628 " in named module must be exported to java.desktop module."); 629 } 630 } 631 return c; 632 } 633 }