< prev index next >

src/java.desktop/share/classes/javax/imageio/ImageIO.java

Print this page




  39 import java.util.Collections;
  40 import java.util.HashSet;
  41 import java.util.Iterator;
  42 import java.util.NoSuchElementException;
  43 import java.util.Set;
  44 import javax.imageio.spi.IIORegistry;
  45 import javax.imageio.spi.ImageReaderSpi;
  46 import javax.imageio.spi.ImageReaderWriterSpi;
  47 import javax.imageio.spi.ImageWriterSpi;
  48 import javax.imageio.spi.ImageInputStreamSpi;
  49 import javax.imageio.spi.ImageOutputStreamSpi;
  50 import javax.imageio.spi.ImageTranscoderSpi;
  51 import javax.imageio.spi.ServiceRegistry;
  52 import javax.imageio.stream.ImageInputStream;
  53 import javax.imageio.stream.ImageOutputStream;
  54 import sun.awt.AppContext;
  55 import sun.security.action.GetPropertyAction;
  56 
  57 /**
  58  * A class containing static convenience methods for locating
  59  * <code>ImageReader</code>s and <code>ImageWriter</code>s, and
  60  * performing simple encoding and decoding.
  61  *
  62  */
  63 public final class ImageIO {
  64 
  65     private static final IIORegistry theRegistry =
  66         IIORegistry.getDefaultInstance();
  67 
  68     /**
  69      * Constructor is private to prevent instantiation.
  70      */
  71     private ImageIO() {}
  72 
  73     /**
  74      * Scans for plug-ins on the application class path,
  75      * loads their service provider classes, and registers a service
  76      * provider instance for each one found with the
  77      * <code>IIORegistry</code>.
  78      *
  79      * <p>This method is needed because the application class path can
  80      * theoretically change, or additional plug-ins may become available.
  81      * Rather than re-scanning the classpath on every invocation of the
  82      * API, the class path is scanned automatically only on the first
  83      * invocation. Clients can call this method to prompt a re-scan.
  84      * Thus this method need only be invoked by sophisticated applications
  85      * which dynamically make new plug-ins available at runtime.
  86      *
  87      * <p> The <code>getResources</code> method of the context
  88      * <code>ClassLoader</code> is used locate JAR files containing
  89      * files named
  90      * <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>,
  91      * where <i>classname</i> is one of <code>ImageReaderSpi</code>,
  92      * <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>,
  93      * <code>ImageInputStreamSpi</code>, or
  94      * <code>ImageOutputStreamSpi</code>, along the application class
  95      * path.
  96      *
  97      * <p> The contents of the located files indicate the names of
  98      * actual implementation classes which implement the
  99      * aforementioned service provider interfaces; the default class
 100      * loader is then used to load each of these classes and to
 101      * instantiate an instance of each class, which is then placed
 102      * into the registry for later retrieval.
 103      *
 104      * <p> The exact set of locations searched depends on the
 105      * implementation of the Java runtime environment.
 106      *
 107      * @see ClassLoader#getResources
 108      */
 109     public static void scanForPlugins() {
 110         theRegistry.registerApplicationClasspathSpis();
 111     }
 112 
 113     // ImageInputStreams
 114 
 115     /**
 116      * A class to hold information about caching.  Each
 117      * <code>ThreadGroup</code> will have its own copy
 118      * via the <code>AppContext</code> mechanism.
 119      */
 120     static class CacheInfo {
 121         boolean useCache = true;
 122         File cacheDirectory = null;
 123         Boolean hasPermission = null;
 124 
 125         public CacheInfo() {}
 126 
 127         public boolean getUseCache() {
 128             return useCache;
 129         }
 130 
 131         public void setUseCache(boolean useCache) {
 132             this.useCache = useCache;
 133         }
 134 
 135         public File getCacheDirectory() {
 136             return cacheDirectory;
 137         }
 138 
 139         public void setCacheDirectory(File cacheDirectory) {
 140             this.cacheDirectory = cacheDirectory;
 141         }
 142 
 143         public Boolean getHasPermission() {
 144             return hasPermission;
 145         }
 146 
 147         public void setHasPermission(Boolean hasPermission) {
 148             this.hasPermission = hasPermission;
 149         }
 150     }
 151 
 152     /**
 153      * Returns the <code>CacheInfo</code> object associated with this
 154      * <code>ThreadGroup</code>.
 155      */
 156     private static synchronized CacheInfo getCacheInfo() {
 157         AppContext context = AppContext.getAppContext();
 158         CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
 159         if (info == null) {
 160             info = new CacheInfo();
 161             context.put(CacheInfo.class, info);
 162         }
 163         return info;
 164     }
 165 
 166     /**
 167      * Returns the default temporary (cache) directory as defined by the
 168      * java.io.tmpdir system property.
 169      */
 170     private static String getTempDir() {
 171         GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
 172         return AccessController.doPrivileged(a);
 173     }
 174 
 175     /**
 176      * Determines whether the caller has write access to the cache
 177      * directory, stores the result in the <code>CacheInfo</code> object,
 178      * and returns the decision.  This method helps to prevent mysterious
 179      * SecurityExceptions to be thrown when this convenience class is used
 180      * in an applet, for example.
 181      */
 182     private static boolean hasCachePermission() {
 183         Boolean hasPermission = getCacheInfo().getHasPermission();
 184 
 185         if (hasPermission != null) {
 186             return hasPermission.booleanValue();
 187         } else {
 188             try {
 189                 SecurityManager security = System.getSecurityManager();
 190                 if (security != null) {
 191                     File cachedir = getCacheDirectory();
 192                     String cachepath;
 193 
 194                     if (cachedir != null) {
 195                         cachepath = cachedir.getPath();
 196                     } else {
 197                         cachepath = getTempDir();


 208                     String filepath = cachepath;
 209                     if (!filepath.endsWith(File.separator)) {
 210                         filepath += File.separator;
 211                     }
 212                     filepath += "*";
 213 
 214                     security.checkPermission(new FilePermission(filepath, "read, write, delete"));
 215                 }
 216             } catch (SecurityException e) {
 217                 getCacheInfo().setHasPermission(Boolean.FALSE);
 218                 return false;
 219             }
 220 
 221             getCacheInfo().setHasPermission(Boolean.TRUE);
 222             return true;
 223         }
 224     }
 225 
 226     /**
 227      * Sets a flag indicating whether a disk-based cache file should
 228      * be used when creating <code>ImageInputStream</code>s and
 229      * <code>ImageOutputStream</code>s.
 230      *
 231      * <p> When reading from a standard <code>InputStream</code>, it
 232      * may be necessary to save previously read information in a cache
 233      * since the underlying stream does not allow data to be re-read.
 234      * Similarly, when writing to a standard
 235      * <code>OutputStream</code>, a cache may be used to allow a
 236      * previously written value to be changed before flushing it to
 237      * the final destination.
 238      *
 239      * <p> The cache may reside in main memory or on disk.  Setting
 240      * this flag to <code>false</code> disallows the use of disk for
 241      * future streams, which may be advantageous when working with
 242      * small images, as the overhead of creating and destroying files
 243      * is removed.
 244      *
 245      * <p> On startup, the value is set to <code>true</code>.
 246      *
 247      * @param useCache a <code>boolean</code> indicating whether a
 248      * cache file should be used, in cases where it is optional.
 249      *
 250      * @see #getUseCache
 251      */
 252     public static void setUseCache(boolean useCache) {
 253         getCacheInfo().setUseCache(useCache);
 254     }
 255 
 256     /**
 257      * Returns the current value set by <code>setUseCache</code>, or
 258      * <code>true</code> if no explicit setting has been made.
 259      *
 260      * @return true if a disk-based cache may be used for
 261      * <code>ImageInputStream</code>s and
 262      * <code>ImageOutputStream</code>s.
 263      *
 264      * @see #setUseCache
 265      */
 266     public static boolean getUseCache() {
 267         return getCacheInfo().getUseCache();
 268     }
 269 
 270     /**
 271      * Sets the directory where cache files are to be created.  A
 272      * value of <code>null</code> indicates that the system-dependent
 273      * default temporary-file directory is to be used.  If
 274      * <code>getUseCache</code> returns false, this value is ignored.
 275      *
 276      * @param cacheDirectory a <code>File</code> specifying a directory.
 277      *
 278      * @see File#createTempFile(String, String, File)
 279      *
 280      * @exception SecurityException if the security manager denies
 281      * access to the directory.
 282      * @exception IllegalArgumentException if <code>cacheDir</code> is
 283      * non-<code>null</code> but is not a directory.
 284      *
 285      * @see #getCacheDirectory
 286      */
 287     public static void setCacheDirectory(File cacheDirectory) {
 288         if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {
 289             throw new IllegalArgumentException("Not a directory!");
 290         }
 291         getCacheInfo().setCacheDirectory(cacheDirectory);
 292         getCacheInfo().setHasPermission(null);
 293     }
 294 
 295     /**
 296      * Returns the current value set by
 297      * <code>setCacheDirectory</code>, or <code>null</code> if no
 298      * explicit setting has been made.
 299      *
 300      * @return a <code>File</code> indicating the directory where
 301      * cache files will be created, or <code>null</code> to indicate
 302      * the system-dependent default temporary-file directory.
 303      *
 304      * @see #setCacheDirectory
 305      */
 306     public static File getCacheDirectory() {
 307         return getCacheInfo().getCacheDirectory();
 308     }
 309 
 310     /**
 311      * Returns an <code>ImageInputStream</code> that will take its
 312      * input from the given <code>Object</code>.  The set of
 313      * <code>ImageInputStreamSpi</code>s registered with the
 314      * <code>IIORegistry</code> class is queried and the first one
 315      * that is able to take input from the supplied object is used to
 316      * create the returned <code>ImageInputStream</code>.  If no
 317      * suitable <code>ImageInputStreamSpi</code> exists,
 318      * <code>null</code> is returned.
 319      *
 320      * <p> The current cache settings from <code>getUseCache</code>and
 321      * <code>getCacheDirectory</code> will be used to control caching.
 322      *
 323      * @param input an <code>Object</code> to be used as an input
 324      * source, such as a <code>File</code>, readable
 325      * <code>RandomAccessFile</code>, or <code>InputStream</code>.
 326      *
 327      * @return an <code>ImageInputStream</code>, or <code>null</code>.
 328      *
 329      * @exception IllegalArgumentException if <code>input</code>
 330      * is <code>null</code>.
 331      * @exception IOException if a cache file is needed but cannot be
 332      * created.
 333      *
 334      * @see javax.imageio.spi.ImageInputStreamSpi
 335      */
 336     public static ImageInputStream createImageInputStream(Object input)
 337         throws IOException {
 338         if (input == null) {
 339             throw new IllegalArgumentException("input == null!");
 340         }
 341 
 342         Iterator<ImageInputStreamSpi> iter;
 343         // Ensure category is present
 344         try {
 345             iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,
 346                                                    true);
 347         } catch (IllegalArgumentException e) {
 348             return null;
 349         }
 350 


 352 
 353         while (iter.hasNext()) {
 354             ImageInputStreamSpi spi = iter.next();
 355             if (spi.getInputClass().isInstance(input)) {
 356                 try {
 357                     return spi.createInputStreamInstance(input,
 358                                                          usecache,
 359                                                          getCacheDirectory());
 360                 } catch (IOException e) {
 361                     throw new IIOException("Can't create cache file!", e);
 362                 }
 363             }
 364         }
 365 
 366         return null;
 367     }
 368 
 369     // ImageOutputStreams
 370 
 371     /**
 372      * Returns an <code>ImageOutputStream</code> that will send its
 373      * output to the given <code>Object</code>.  The set of
 374      * <code>ImageOutputStreamSpi</code>s registered with the
 375      * <code>IIORegistry</code> class is queried and the first one
 376      * that is able to send output from the supplied object is used to
 377      * create the returned <code>ImageOutputStream</code>.  If no
 378      * suitable <code>ImageOutputStreamSpi</code> exists,
 379      * <code>null</code> is returned.
 380      *
 381      * <p> The current cache settings from <code>getUseCache</code>and
 382      * <code>getCacheDirectory</code> will be used to control caching.
 383      *
 384      * @param output an <code>Object</code> to be used as an output
 385      * destination, such as a <code>File</code>, writable
 386      * <code>RandomAccessFile</code>, or <code>OutputStream</code>.
 387      *
 388      * @return an <code>ImageOutputStream</code>, or
 389      * <code>null</code>.
 390      *
 391      * @exception IllegalArgumentException if <code>output</code> is
 392      * <code>null</code>.
 393      * @exception IOException if a cache file is needed but cannot be
 394      * created.
 395      *
 396      * @see javax.imageio.spi.ImageOutputStreamSpi
 397      */
 398     public static ImageOutputStream createImageOutputStream(Object output)
 399         throws IOException {
 400         if (output == null) {
 401             throw new IllegalArgumentException("output == null!");
 402         }
 403 
 404         Iterator<ImageOutputStreamSpi> iter;
 405         // Ensure category is present
 406         try {
 407             iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,
 408                                                    true);
 409         } catch (IllegalArgumentException e) {
 410             return null;
 411         }
 412 


 457         // Ensure category is present
 458         Iterator<S> iter;
 459         try {
 460             iter = theRegistry.getServiceProviders(spiClass, true);
 461         } catch (IllegalArgumentException e) {
 462             return new String[0];
 463         }
 464 
 465         HashSet<String> s = new HashSet<String>();
 466         while (iter.hasNext()) {
 467             ImageReaderWriterSpi spi = iter.next();
 468             Collections.addAll(s, spiInfo.info(spi));
 469         }
 470 
 471         return s.toArray(new String[s.size()]);
 472     }
 473 
 474     // Readers
 475 
 476     /**
 477      * Returns an array of <code>String</code>s listing all of the
 478      * informal format names understood by the current set of registered
 479      * readers.
 480      *
 481      * @return an array of <code>String</code>s.
 482      */
 483     public static String[] getReaderFormatNames() {
 484         return getReaderWriterInfo(ImageReaderSpi.class,
 485                                    SpiInfo.FORMAT_NAMES);
 486     }
 487 
 488     /**
 489      * Returns an array of <code>String</code>s listing all of the
 490      * MIME types understood by the current set of registered
 491      * readers.
 492      *
 493      * @return an array of <code>String</code>s.
 494      */
 495     public static String[] getReaderMIMETypes() {
 496         return getReaderWriterInfo(ImageReaderSpi.class,
 497                                    SpiInfo.MIME_TYPES);
 498     }
 499 
 500     /**
 501      * Returns an array of <code>String</code>s listing all of the
 502      * file suffixes associated with the formats understood
 503      * by the current set of registered readers.
 504      *
 505      * @return an array of <code>String</code>s.
 506      * @since 1.6
 507      */
 508     public static String[] getReaderFileSuffixes() {
 509         return getReaderWriterInfo(ImageReaderSpi.class,
 510                                    SpiInfo.FILE_SUFFIXES);
 511     }
 512 
 513     static class ImageReaderIterator implements Iterator<ImageReader> {
 514         // Contains ImageReaderSpis
 515         private Iterator<ImageReaderSpi> iter;
 516 
 517         public ImageReaderIterator(Iterator<ImageReaderSpi> iter) {
 518             this.iter = iter;
 519         }
 520 
 521         public boolean hasNext() {
 522             return iter.hasNext();
 523         }
 524 
 525         public ImageReader next() {


 601         Method method;
 602         String name;
 603 
 604         // method returns an array of Strings
 605         public ContainsFilter(Method method,
 606                               String name) {
 607             this.method = method;
 608             this.name = name;
 609         }
 610 
 611         public boolean filter(Object elt) {
 612             try {
 613                 return contains((String[])method.invoke(elt), name);
 614             } catch (Exception e) {
 615                 return false;
 616             }
 617         }
 618     }
 619 
 620     /**
 621      * Returns an <code>Iterator</code> containing all currently
 622      * registered <code>ImageReader</code>s that claim to be able to
 623      * decode the supplied <code>Object</code>, typically an
 624      * <code>ImageInputStream</code>.
 625      *
 626      * <p> The stream position is left at its prior position upon
 627      * exit from this method.
 628      *
 629      * @param input an <code>ImageInputStream</code> or other
 630      * <code>Object</code> containing encoded image data.
 631      *
 632      * @return an <code>Iterator</code> containing <code>ImageReader</code>s.
 633      *
 634      * @exception IllegalArgumentException if <code>input</code> is
 635      * <code>null</code>.
 636      *
 637      * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
 638      */
 639     public static Iterator<ImageReader> getImageReaders(Object input) {
 640         if (input == null) {
 641             throw new IllegalArgumentException("input == null!");
 642         }
 643         Iterator<ImageReaderSpi> iter;
 644         // Ensure category is present
 645         try {
 646             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 647                                               new CanDecodeInputFilter(input),
 648                                               true);
 649         } catch (IllegalArgumentException e) {
 650             return Collections.emptyIterator();
 651         }
 652 
 653         return new ImageReaderIterator(iter);
 654     }
 655 


 664         try {
 665             readerFormatNamesMethod =
 666                 ImageReaderSpi.class.getMethod("getFormatNames");
 667             readerFileSuffixesMethod =
 668                 ImageReaderSpi.class.getMethod("getFileSuffixes");
 669             readerMIMETypesMethod =
 670                 ImageReaderSpi.class.getMethod("getMIMETypes");
 671 
 672             writerFormatNamesMethod =
 673                 ImageWriterSpi.class.getMethod("getFormatNames");
 674             writerFileSuffixesMethod =
 675                 ImageWriterSpi.class.getMethod("getFileSuffixes");
 676             writerMIMETypesMethod =
 677                 ImageWriterSpi.class.getMethod("getMIMETypes");
 678         } catch (NoSuchMethodException e) {
 679             e.printStackTrace();
 680         }
 681     }
 682 
 683     /**
 684      * Returns an <code>Iterator</code> containing all currently
 685      * registered <code>ImageReader</code>s that claim to be able to
 686      * decode the named format.
 687      *
 688      * @param formatName a <code>String</code> containing the informal
 689      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
 690      *
 691      * @return an <code>Iterator</code> containing
 692      * <code>ImageReader</code>s.
 693      *
 694      * @exception IllegalArgumentException if <code>formatName</code>
 695      * is <code>null</code>.
 696      *
 697      * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
 698      */
 699     public static Iterator<ImageReader>
 700         getImageReadersByFormatName(String formatName)
 701     {
 702         if (formatName == null) {
 703             throw new IllegalArgumentException("formatName == null!");
 704         }
 705         Iterator<ImageReaderSpi> iter;
 706         // Ensure category is present
 707         try {
 708             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 709                                     new ContainsFilter(readerFormatNamesMethod,
 710                                                        formatName),
 711                                                 true);
 712         } catch (IllegalArgumentException e) {
 713             return Collections.emptyIterator();
 714         }
 715         return new ImageReaderIterator(iter);
 716     }
 717 
 718     /**
 719      * Returns an <code>Iterator</code> containing all currently
 720      * registered <code>ImageReader</code>s that claim to be able to
 721      * decode files with the given suffix.
 722      *
 723      * @param fileSuffix a <code>String</code> containing a file
 724      * suffix (<i>e.g.</i>, "jpg" or "tiff").
 725      *
 726      * @return an <code>Iterator</code> containing
 727      * <code>ImageReader</code>s.
 728      *
 729      * @exception IllegalArgumentException if <code>fileSuffix</code>
 730      * is <code>null</code>.
 731      *
 732      * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
 733      */
 734     public static Iterator<ImageReader>
 735         getImageReadersBySuffix(String fileSuffix)
 736     {
 737         if (fileSuffix == null) {
 738             throw new IllegalArgumentException("fileSuffix == null!");
 739         }
 740         // Ensure category is present
 741         Iterator<ImageReaderSpi> iter;
 742         try {
 743             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 744                                    new ContainsFilter(readerFileSuffixesMethod,
 745                                                       fileSuffix),
 746                                               true);
 747         } catch (IllegalArgumentException e) {
 748             return Collections.emptyIterator();
 749         }
 750         return new ImageReaderIterator(iter);
 751     }
 752 
 753     /**
 754      * Returns an <code>Iterator</code> containing all currently
 755      * registered <code>ImageReader</code>s that claim to be able to
 756      * decode files with the given MIME type.
 757      *
 758      * @param MIMEType a <code>String</code> containing a file
 759      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
 760      *
 761      * @return an <code>Iterator</code> containing
 762      * <code>ImageReader</code>s.
 763      *
 764      * @exception IllegalArgumentException if <code>MIMEType</code> is
 765      * <code>null</code>.
 766      *
 767      * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
 768      */
 769     public static Iterator<ImageReader>
 770         getImageReadersByMIMEType(String MIMEType)
 771     {
 772         if (MIMEType == null) {
 773             throw new IllegalArgumentException("MIMEType == null!");
 774         }
 775         // Ensure category is present
 776         Iterator<ImageReaderSpi> iter;
 777         try {
 778             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 779                                       new ContainsFilter(readerMIMETypesMethod,
 780                                                          MIMEType),
 781                                               true);
 782         } catch (IllegalArgumentException e) {
 783             return Collections.emptyIterator();
 784         }
 785         return new ImageReaderIterator(iter);
 786     }
 787 
 788     // Writers
 789 
 790     /**
 791      * Returns an array of <code>String</code>s listing all of the
 792      * informal format names understood by the current set of registered
 793      * writers.
 794      *
 795      * @return an array of <code>String</code>s.
 796      */
 797     public static String[] getWriterFormatNames() {
 798         return getReaderWriterInfo(ImageWriterSpi.class,
 799                                    SpiInfo.FORMAT_NAMES);
 800     }
 801 
 802     /**
 803      * Returns an array of <code>String</code>s listing all of the
 804      * MIME types understood by the current set of registered
 805      * writers.
 806      *
 807      * @return an array of <code>String</code>s.
 808      */
 809     public static String[] getWriterMIMETypes() {
 810         return getReaderWriterInfo(ImageWriterSpi.class,
 811                                    SpiInfo.MIME_TYPES);
 812     }
 813 
 814     /**
 815      * Returns an array of <code>String</code>s listing all of the
 816      * file suffixes associated with the formats understood
 817      * by the current set of registered writers.
 818      *
 819      * @return an array of <code>String</code>s.
 820      * @since 1.6
 821      */
 822     public static String[] getWriterFileSuffixes() {
 823         return getReaderWriterInfo(ImageWriterSpi.class,
 824                                    SpiInfo.FILE_SUFFIXES);
 825     }
 826 
 827     static class ImageWriterIterator implements Iterator<ImageWriter> {
 828         // Contains ImageWriterSpis
 829         private Iterator<ImageWriterSpi> iter;
 830 
 831         public ImageWriterIterator(Iterator<ImageWriterSpi> iter) {
 832             this.iter = iter;
 833         }
 834 
 835         public boolean hasNext() {
 836             return iter.hasNext();
 837         }
 838 
 839         public ImageWriter next() {


 847             }
 848             return null;
 849         }
 850 
 851         public void remove() {
 852             throw new UnsupportedOperationException();
 853         }
 854     }
 855 
 856     private static boolean contains(String[] names, String name) {
 857         for (int i = 0; i < names.length; i++) {
 858             if (name.equalsIgnoreCase(names[i])) {
 859                 return true;
 860             }
 861         }
 862 
 863         return false;
 864     }
 865 
 866     /**
 867      * Returns an <code>Iterator</code> containing all currently
 868      * registered <code>ImageWriter</code>s that claim to be able to
 869      * encode the named format.
 870      *
 871      * @param formatName a <code>String</code> containing the informal
 872      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
 873      *
 874      * @return an <code>Iterator</code> containing
 875      * <code>ImageWriter</code>s.
 876      *
 877      * @exception IllegalArgumentException if <code>formatName</code> is
 878      * <code>null</code>.
 879      *
 880      * @see javax.imageio.spi.ImageWriterSpi#getFormatNames
 881      */
 882     public static Iterator<ImageWriter>
 883         getImageWritersByFormatName(String formatName)
 884     {
 885         if (formatName == null) {
 886             throw new IllegalArgumentException("formatName == null!");
 887         }
 888         Iterator<ImageWriterSpi> iter;
 889         // Ensure category is present
 890         try {
 891             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
 892                                     new ContainsFilter(writerFormatNamesMethod,
 893                                                        formatName),
 894                                             true);
 895         } catch (IllegalArgumentException e) {
 896             return Collections.emptyIterator();
 897         }
 898         return new ImageWriterIterator(iter);
 899     }
 900 
 901     /**
 902      * Returns an <code>Iterator</code> containing all currently
 903      * registered <code>ImageWriter</code>s that claim to be able to
 904      * encode files with the given suffix.
 905      *
 906      * @param fileSuffix a <code>String</code> containing a file
 907      * suffix (<i>e.g.</i>, "jpg" or "tiff").
 908      *
 909      * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
 910      *
 911      * @exception IllegalArgumentException if <code>fileSuffix</code> is
 912      * <code>null</code>.
 913      *
 914      * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
 915      */
 916     public static Iterator<ImageWriter>
 917         getImageWritersBySuffix(String fileSuffix)
 918     {
 919         if (fileSuffix == null) {
 920             throw new IllegalArgumentException("fileSuffix == null!");
 921         }
 922         Iterator<ImageWriterSpi> iter;
 923         // Ensure category is present
 924         try {
 925             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
 926                                    new ContainsFilter(writerFileSuffixesMethod,
 927                                                       fileSuffix),
 928                                             true);
 929         } catch (IllegalArgumentException e) {
 930             return Collections.emptyIterator();
 931         }
 932         return new ImageWriterIterator(iter);
 933     }
 934 
 935     /**
 936      * Returns an <code>Iterator</code> containing all currently
 937      * registered <code>ImageWriter</code>s that claim to be able to
 938      * encode files with the given MIME type.
 939      *
 940      * @param MIMEType a <code>String</code> containing a file
 941      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
 942      *
 943      * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
 944      *
 945      * @exception IllegalArgumentException if <code>MIMEType</code> is
 946      * <code>null</code>.
 947      *
 948      * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
 949      */
 950     public static Iterator<ImageWriter>
 951         getImageWritersByMIMEType(String MIMEType)
 952     {
 953         if (MIMEType == null) {
 954             throw new IllegalArgumentException("MIMEType == null!");
 955         }
 956         Iterator<ImageWriterSpi> iter;
 957         // Ensure category is present
 958         try {
 959             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
 960                                       new ContainsFilter(writerMIMETypesMethod,
 961                                                          MIMEType),
 962                                             true);
 963         } catch (IllegalArgumentException e) {
 964             return Collections.emptyIterator();
 965         }
 966         return new ImageWriterIterator(iter);
 967     }
 968 
 969     /**
 970      * Returns an <code>ImageWriter</code>corresponding to the given
 971      * <code>ImageReader</code>, if there is one, or <code>null</code>
 972      * if the plug-in for this <code>ImageReader</code> does not
 973      * specify a corresponding <code>ImageWriter</code>, or if the
 974      * given <code>ImageReader</code> is not registered.  This
 975      * mechanism may be used to obtain an <code>ImageWriter</code>
 976      * that will understand the internal structure of non-pixel
 977      * metadata (as encoded by <code>IIOMetadata</code> objects)
 978      * generated by the <code>ImageReader</code>.  By obtaining this
 979      * data from the <code>ImageReader</code> and passing it on to the
 980      * <code>ImageWriter</code> obtained with this method, a client
 981      * program can read an image, modify it in some way, and write it
 982      * back out preserving all metadata, without having to understand
 983      * anything about the structure of the metadata, or even about
 984      * the image format.  Note that this method returns the
 985      * "preferred" writer, which is the first in the list returned by
 986      * <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>.
 987      *
 988      * @param reader an instance of a registered <code>ImageReader</code>.
 989      *
 990      * @return an <code>ImageWriter</code>, or null.
 991      *
 992      * @exception IllegalArgumentException if <code>reader</code> is
 993      * <code>null</code>.
 994      *
 995      * @see #getImageReader(ImageWriter)
 996      * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
 997      */
 998     public static ImageWriter getImageWriter(ImageReader reader) {
 999         if (reader == null) {
1000             throw new IllegalArgumentException("reader == null!");
1001         }
1002 
1003         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1004         if (readerSpi == null) {
1005             Iterator<ImageReaderSpi> readerSpiIter;
1006             // Ensure category is present
1007             try {
1008                 readerSpiIter =
1009                     theRegistry.getServiceProviders(ImageReaderSpi.class,
1010                                                     false);
1011             } catch (IllegalArgumentException e) {
1012                 return null;
1013             }


1037             return null;
1038         }
1039 
1040         ImageWriterSpi writerSpi = (ImageWriterSpi)
1041             theRegistry.getServiceProviderByClass(writerSpiClass);
1042         if (writerSpi == null) {
1043             return null;
1044         }
1045 
1046         try {
1047             return writerSpi.createWriterInstance();
1048         } catch (IOException e) {
1049             // Deregister the spi in this case, but only as a writerSpi
1050             theRegistry.deregisterServiceProvider(writerSpi,
1051                                                   ImageWriterSpi.class);
1052             return null;
1053         }
1054     }
1055 
1056     /**
1057      * Returns an <code>ImageReader</code>corresponding to the given
1058      * <code>ImageWriter</code>, if there is one, or <code>null</code>
1059      * if the plug-in for this <code>ImageWriter</code> does not
1060      * specify a corresponding <code>ImageReader</code>, or if the
1061      * given <code>ImageWriter</code> is not registered.  This method
1062      * is provided principally for symmetry with
1063      * <code>getImageWriter(ImageReader)</code>.  Note that this
1064      * method returns the "preferred" reader, which is the first in
1065      * the list returned by
1066      * javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>.
1067      *
1068      * @param writer an instance of a registered <code>ImageWriter</code>.
1069      *
1070      * @return an <code>ImageReader</code>, or null.
1071      *
1072      * @exception IllegalArgumentException if <code>writer</code> is
1073      * <code>null</code>.
1074      *
1075      * @see #getImageWriter(ImageReader)
1076      * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
1077      */
1078     public static ImageReader getImageReader(ImageWriter writer) {
1079         if (writer == null) {
1080             throw new IllegalArgumentException("writer == null!");
1081         }
1082 
1083         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1084         if (writerSpi == null) {
1085             Iterator<ImageWriterSpi> writerSpiIter;
1086             // Ensure category is present
1087             try {
1088                 writerSpiIter =
1089                     theRegistry.getServiceProviders(ImageWriterSpi.class,
1090                                                     false);
1091             } catch (IllegalArgumentException e) {
1092                 return null;
1093             }


1117             return null;
1118         }
1119 
1120         ImageReaderSpi readerSpi = (ImageReaderSpi)
1121             theRegistry.getServiceProviderByClass(readerSpiClass);
1122         if (readerSpi == null) {
1123             return null;
1124         }
1125 
1126         try {
1127             return readerSpi.createReaderInstance();
1128         } catch (IOException e) {
1129             // Deregister the spi in this case, but only as a readerSpi
1130             theRegistry.deregisterServiceProvider(readerSpi,
1131                                                   ImageReaderSpi.class);
1132             return null;
1133         }
1134     }
1135 
1136     /**
1137      * Returns an <code>Iterator</code> containing all currently
1138      * registered <code>ImageWriter</code>s that claim to be able to
1139      * encode images of the given layout (specified using an
1140      * <code>ImageTypeSpecifier</code>) in the given format.
1141      *
1142      * @param type an <code>ImageTypeSpecifier</code> indicating the
1143      * layout of the image to be written.
1144      * @param formatName the informal name of the <code>format</code>.
1145      *
1146      * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
1147      *
1148      * @exception IllegalArgumentException if any parameter is
1149      * <code>null</code>.
1150      *
1151      * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
1152      */
1153     public static Iterator<ImageWriter>
1154         getImageWriters(ImageTypeSpecifier type, String formatName)
1155     {
1156         if (type == null) {
1157             throw new IllegalArgumentException("type == null!");
1158         }
1159         if (formatName == null) {
1160             throw new IllegalArgumentException("formatName == null!");
1161         }
1162 
1163         Iterator<ImageWriterSpi> iter;
1164         // Ensure category is present
1165         try {
1166             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
1167                                  new CanEncodeImageAndFormatFilter(type,
1168                                                                    formatName),
1169                                             true);


1204 
1205         String readerSpiName;
1206         String writerSpiName;
1207 
1208         public TranscoderFilter(ImageReaderSpi readerSpi,
1209                                 ImageWriterSpi writerSpi) {
1210             this.readerSpiName = readerSpi.getClass().getName();
1211             this.writerSpiName = writerSpi.getClass().getName();
1212         }
1213 
1214         public boolean filter(Object elt) {
1215             ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;
1216             String readerName = spi.getReaderServiceProviderName();
1217             String writerName = spi.getWriterServiceProviderName();
1218             return (readerName.equals(readerSpiName) &&
1219                     writerName.equals(writerSpiName));
1220         }
1221     }
1222 
1223     /**
1224      * Returns an <code>Iterator</code> containing all currently
1225      * registered <code>ImageTranscoder</code>s that claim to be
1226      * able to transcode between the metadata of the given
1227      * <code>ImageReader</code> and <code>ImageWriter</code>.
1228      *
1229      * @param reader an <code>ImageReader</code>.
1230      * @param writer an <code>ImageWriter</code>.
1231      *
1232      * @return an <code>Iterator</code> containing
1233      * <code>ImageTranscoder</code>s.
1234      *
1235      * @exception IllegalArgumentException if <code>reader</code> or
1236      * <code>writer</code> is <code>null</code>.
1237      */
1238     public static Iterator<ImageTranscoder>
1239         getImageTranscoders(ImageReader reader, ImageWriter writer)
1240     {
1241         if (reader == null) {
1242             throw new IllegalArgumentException("reader == null!");
1243         }
1244         if (writer == null) {
1245             throw new IllegalArgumentException("writer == null!");
1246         }
1247         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1248         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1249         ServiceRegistry.Filter filter =
1250             new TranscoderFilter(readerSpi, writerSpi);
1251 
1252         Iterator<ImageTranscoderSpi> iter;
1253         // Ensure category is present
1254         try {
1255             iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,
1256                                             filter, true);
1257         } catch (IllegalArgumentException e) {
1258             return Collections.emptyIterator();
1259         }
1260         return new ImageTranscoderIterator(iter);
1261     }
1262 
1263     // All-in-one methods
1264 
1265     /**
1266      * Returns a <code>BufferedImage</code> as the result of decoding
1267      * a supplied <code>File</code> with an <code>ImageReader</code>
1268      * chosen automatically from among those currently registered.
1269      * The <code>File</code> is wrapped in an
1270      * <code>ImageInputStream</code>.  If no registered
1271      * <code>ImageReader</code> claims to be able to read the
1272      * resulting stream, <code>null</code> is returned.
1273      *
1274      * <p> The current cache settings from <code>getUseCache</code>and
1275      * <code>getCacheDirectory</code> will be used to control caching in the
1276      * <code>ImageInputStream</code> that is created.
1277      *
1278      * <p> Note that there is no <code>read</code> method that takes a
1279      * filename as a <code>String</code>; use this method instead after
1280      * creating a <code>File</code> from the filename.
1281      *
1282      * <p> This method does not attempt to locate
1283      * <code>ImageReader</code>s that can read directly from a
1284      * <code>File</code>; that may be accomplished using
1285      * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1286      *
1287      * @param input a <code>File</code> to read from.
1288      *
1289      * @return a <code>BufferedImage</code> containing the decoded
1290      * contents of the input, or <code>null</code>.
1291      *
1292      * @exception IllegalArgumentException if <code>input</code> is
1293      * <code>null</code>.
1294      * @exception IOException if an error occurs during reading.
1295      */
1296     public static BufferedImage read(File input) throws IOException {
1297         if (input == null) {
1298             throw new IllegalArgumentException("input == null!");
1299         }
1300         if (!input.canRead()) {
1301             throw new IIOException("Can't read input file!");
1302         }
1303 
1304         ImageInputStream stream = createImageInputStream(input);
1305         if (stream == null) {
1306             throw new IIOException("Can't create an ImageInputStream!");
1307         }
1308         BufferedImage bi = read(stream);
1309         if (bi == null) {
1310             stream.close();
1311         }
1312         return bi;
1313     }
1314 
1315     /**
1316      * Returns a <code>BufferedImage</code> as the result of decoding
1317      * a supplied <code>InputStream</code> with an <code>ImageReader</code>
1318      * chosen automatically from among those currently registered.
1319      * The <code>InputStream</code> is wrapped in an
1320      * <code>ImageInputStream</code>.  If no registered
1321      * <code>ImageReader</code> claims to be able to read the
1322      * resulting stream, <code>null</code> is returned.
1323      *
1324      * <p> The current cache settings from <code>getUseCache</code>and
1325      * <code>getCacheDirectory</code> will be used to control caching in the
1326      * <code>ImageInputStream</code> that is created.
1327      *
1328      * <p> This method does not attempt to locate
1329      * <code>ImageReader</code>s that can read directly from an
1330      * <code>InputStream</code>; that may be accomplished using
1331      * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1332      *
1333      * <p> This method <em>does not</em> close the provided
1334      * <code>InputStream</code> after the read operation has completed;
1335      * it is the responsibility of the caller to close the stream, if desired.
1336      *
1337      * @param input an <code>InputStream</code> to read from.
1338      *
1339      * @return a <code>BufferedImage</code> containing the decoded
1340      * contents of the input, or <code>null</code>.
1341      *
1342      * @exception IllegalArgumentException if <code>input</code> is
1343      * <code>null</code>.
1344      * @exception IOException if an error occurs during reading.
1345      */
1346     public static BufferedImage read(InputStream input) throws IOException {
1347         if (input == null) {
1348             throw new IllegalArgumentException("input == null!");
1349         }
1350 
1351         ImageInputStream stream = createImageInputStream(input);
1352         BufferedImage bi = read(stream);
1353         if (bi == null) {
1354             stream.close();
1355         }
1356         return bi;
1357     }
1358 
1359     /**
1360      * Returns a <code>BufferedImage</code> as the result of decoding
1361      * a supplied <code>URL</code> with an <code>ImageReader</code>
1362      * chosen automatically from among those currently registered.  An
1363      * <code>InputStream</code> is obtained from the <code>URL</code>,
1364      * which is wrapped in an <code>ImageInputStream</code>.  If no
1365      * registered <code>ImageReader</code> claims to be able to read
1366      * the resulting stream, <code>null</code> is returned.
1367      *
1368      * <p> The current cache settings from <code>getUseCache</code>and
1369      * <code>getCacheDirectory</code> will be used to control caching in the
1370      * <code>ImageInputStream</code> that is created.
1371      *
1372      * <p> This method does not attempt to locate
1373      * <code>ImageReader</code>s that can read directly from a
1374      * <code>URL</code>; that may be accomplished using
1375      * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1376      *
1377      * @param input a <code>URL</code> to read from.
1378      *
1379      * @return a <code>BufferedImage</code> containing the decoded
1380      * contents of the input, or <code>null</code>.
1381      *
1382      * @exception IllegalArgumentException if <code>input</code> is
1383      * <code>null</code>.
1384      * @exception IOException if an error occurs during reading.
1385      */
1386     public static BufferedImage read(URL input) throws IOException {
1387         if (input == null) {
1388             throw new IllegalArgumentException("input == null!");
1389         }
1390 
1391         InputStream istream = null;
1392         try {
1393             istream = input.openStream();
1394         } catch (IOException e) {
1395             throw new IIOException("Can't get input stream from URL!", e);
1396         }
1397         ImageInputStream stream = createImageInputStream(istream);
1398         BufferedImage bi;
1399         try {
1400             bi = read(stream);
1401             if (bi == null) {
1402                 stream.close();
1403             }
1404         } finally {
1405             istream.close();
1406         }
1407         return bi;
1408     }
1409 
1410     /**
1411      * Returns a <code>BufferedImage</code> as the result of decoding
1412      * a supplied <code>ImageInputStream</code> with an
1413      * <code>ImageReader</code> chosen automatically from among those
1414      * currently registered.  If no registered
1415      * <code>ImageReader</code> claims to be able to read the stream,
1416      * <code>null</code> is returned.
1417      *
1418      * <p> Unlike most other methods in this class, this method <em>does</em>
1419      * close the provided <code>ImageInputStream</code> after the read
1420      * operation has completed, unless <code>null</code> is returned,
1421      * in which case this method <em>does not</em> close the stream.
1422      *
1423      * @param stream an <code>ImageInputStream</code> to read from.
1424      *
1425      * @return a <code>BufferedImage</code> containing the decoded
1426      * contents of the input, or <code>null</code>.
1427      *
1428      * @exception IllegalArgumentException if <code>stream</code> is
1429      * <code>null</code>.
1430      * @exception IOException if an error occurs during reading.
1431      */
1432     public static BufferedImage read(ImageInputStream stream)
1433         throws IOException {
1434         if (stream == null) {
1435             throw new IllegalArgumentException("stream == null!");
1436         }
1437 
1438         Iterator<ImageReader> iter = getImageReaders(stream);
1439         if (!iter.hasNext()) {
1440             return null;
1441         }
1442 
1443         ImageReader reader = iter.next();
1444         ImageReadParam param = reader.getDefaultReadParam();
1445         reader.setInput(stream, true, true);
1446         BufferedImage bi;
1447         try {
1448             bi = reader.read(0, param);
1449         } finally {
1450             reader.dispose();
1451             stream.close();
1452         }
1453         return bi;
1454     }
1455 
1456     /**
1457      * Writes an image using the an arbitrary <code>ImageWriter</code>
1458      * that supports the given format to an
1459      * <code>ImageOutputStream</code>.  The image is written to the
1460      * <code>ImageOutputStream</code> starting at the current stream
1461      * pointer, overwriting existing stream data from that point
1462      * forward, if present.
1463      *
1464      * <p> This method <em>does not</em> close the provided
1465      * <code>ImageOutputStream</code> after the write operation has completed;
1466      * it is the responsibility of the caller to close the stream, if desired.
1467      *
1468      * @param im a <code>RenderedImage</code> to be written.
1469      * @param formatName a <code>String</code> containing the informal
1470      * name of the format.
1471      * @param output an <code>ImageOutputStream</code> to be written to.
1472      *
1473      * @return <code>false</code> if no appropriate writer is found.
1474      *
1475      * @exception IllegalArgumentException if any parameter is
1476      * <code>null</code>.
1477      * @exception IOException if an error occurs during writing.
1478      */
1479     public static boolean write(RenderedImage im,
1480                                 String formatName,
1481                                 ImageOutputStream output) throws IOException {
1482         if (im == null) {
1483             throw new IllegalArgumentException("im == null!");
1484         }
1485         if (formatName == null) {
1486             throw new IllegalArgumentException("formatName == null!");
1487         }
1488         if (output == null) {
1489             throw new IllegalArgumentException("output == null!");
1490         }
1491 
1492         return doWrite(im, getWriter(im, formatName), output);
1493     }
1494 
1495     /**
1496      * Writes an image using an arbitrary <code>ImageWriter</code>
1497      * that supports the given format to a <code>File</code>.  If
1498      * there is already a <code>File</code> present, its contents are
1499      * discarded.
1500      *
1501      * @param im a <code>RenderedImage</code> to be written.
1502      * @param formatName a <code>String</code> containing the informal
1503      * name of the format.
1504      * @param output a <code>File</code> to be written to.
1505      *
1506      * @return <code>false</code> if no appropriate writer is found.
1507      *
1508      * @exception IllegalArgumentException if any parameter is
1509      * <code>null</code>.
1510      * @exception IOException if an error occurs during writing.
1511      */
1512     public static boolean write(RenderedImage im,
1513                                 String formatName,
1514                                 File output) throws IOException {
1515         if (output == null) {
1516             throw new IllegalArgumentException("output == null!");
1517         }
1518         ImageOutputStream stream = null;
1519 
1520         ImageWriter writer = getWriter(im, formatName);
1521         if (writer == null) {
1522             /* Do not make changes in the file system if we have
1523              * no appropriate writer.
1524              */
1525             return false;
1526         }
1527 
1528         try {
1529             output.delete();
1530             stream = createImageOutputStream(output);
1531         } catch (IOException e) {
1532             throw new IIOException("Can't create output stream!", e);
1533         }
1534 
1535         try {
1536             return doWrite(im, writer, stream);
1537         } finally {
1538             stream.close();
1539         }
1540     }
1541 
1542     /**
1543      * Writes an image using an arbitrary <code>ImageWriter</code>
1544      * that supports the given format to an <code>OutputStream</code>.
1545      *
1546      * <p> This method <em>does not</em> close the provided
1547      * <code>OutputStream</code> after the write operation has completed;
1548      * it is the responsibility of the caller to close the stream, if desired.
1549      *
1550      * <p> The current cache settings from <code>getUseCache</code>and
1551      * <code>getCacheDirectory</code> will be used to control caching.
1552      *
1553      * @param im a <code>RenderedImage</code> to be written.
1554      * @param formatName a <code>String</code> containing the informal
1555      * name of the format.
1556      * @param output an <code>OutputStream</code> to be written to.
1557      *
1558      * @return <code>false</code> if no appropriate writer is found.
1559      *
1560      * @exception IllegalArgumentException if any parameter is
1561      * <code>null</code>.
1562      * @exception IOException if an error occurs during writing.
1563      */
1564     public static boolean write(RenderedImage im,
1565                                 String formatName,
1566                                 OutputStream output) throws IOException {
1567         if (output == null) {
1568             throw new IllegalArgumentException("output == null!");
1569         }
1570         ImageOutputStream stream = null;
1571         try {
1572             stream = createImageOutputStream(output);
1573         } catch (IOException e) {
1574             throw new IIOException("Can't create output stream!", e);
1575         }
1576 
1577         try {
1578             return doWrite(im, getWriter(im, formatName), stream);
1579         } finally {
1580             stream.close();
1581         }
1582     }
1583 
1584     /**
1585      * Returns <code>ImageWriter</code> instance according to given
1586      * rendered image and image format or <code>null</code> if there
1587      * is no appropriate writer.
1588      */
1589     private static ImageWriter getWriter(RenderedImage im,
1590                                          String formatName) {
1591         ImageTypeSpecifier type =
1592             ImageTypeSpecifier.createFromRenderedImage(im);
1593         Iterator<ImageWriter> iter = getImageWriters(type, formatName);
1594 
1595         if (iter.hasNext()) {
1596             return iter.next();
1597         } else {
1598             return null;
1599         }
1600     }
1601 
1602     /**
1603      * Writes image to output stream  using given image writer.
1604      */
1605     private static boolean doWrite(RenderedImage im, ImageWriter writer,
1606                                  ImageOutputStream output) throws IOException {


  39 import java.util.Collections;
  40 import java.util.HashSet;
  41 import java.util.Iterator;
  42 import java.util.NoSuchElementException;
  43 import java.util.Set;
  44 import javax.imageio.spi.IIORegistry;
  45 import javax.imageio.spi.ImageReaderSpi;
  46 import javax.imageio.spi.ImageReaderWriterSpi;
  47 import javax.imageio.spi.ImageWriterSpi;
  48 import javax.imageio.spi.ImageInputStreamSpi;
  49 import javax.imageio.spi.ImageOutputStreamSpi;
  50 import javax.imageio.spi.ImageTranscoderSpi;
  51 import javax.imageio.spi.ServiceRegistry;
  52 import javax.imageio.stream.ImageInputStream;
  53 import javax.imageio.stream.ImageOutputStream;
  54 import sun.awt.AppContext;
  55 import sun.security.action.GetPropertyAction;
  56 
  57 /**
  58  * A class containing static convenience methods for locating
  59  * {@code ImageReader}s and {@code ImageWriter}s, and
  60  * performing simple encoding and decoding.
  61  *
  62  */
  63 public final class ImageIO {
  64 
  65     private static final IIORegistry theRegistry =
  66         IIORegistry.getDefaultInstance();
  67 
  68     /**
  69      * Constructor is private to prevent instantiation.
  70      */
  71     private ImageIO() {}
  72 
  73     /**
  74      * Scans for plug-ins on the application class path,
  75      * loads their service provider classes, and registers a service
  76      * provider instance for each one found with the
  77      * {@code IIORegistry}.
  78      *
  79      * <p>This method is needed because the application class path can
  80      * theoretically change, or additional plug-ins may become available.
  81      * Rather than re-scanning the classpath on every invocation of the
  82      * API, the class path is scanned automatically only on the first
  83      * invocation. Clients can call this method to prompt a re-scan.
  84      * Thus this method need only be invoked by sophisticated applications
  85      * which dynamically make new plug-ins available at runtime.
  86      *
  87      * <p> The {@code getResources} method of the context
  88      * {@code ClassLoader} is used locate JAR files containing
  89      * files named
  90      * {@code META-INF/services/javax.imageio.spi.}<i>classname</i>,
  91      * where <i>classname</i> is one of {@code ImageReaderSpi},
  92      * {@code ImageWriterSpi}, {@code ImageTranscoderSpi},
  93      * {@code ImageInputStreamSpi}, or
  94      * {@code ImageOutputStreamSpi}, along the application class
  95      * path.
  96      *
  97      * <p> The contents of the located files indicate the names of
  98      * actual implementation classes which implement the
  99      * aforementioned service provider interfaces; the default class
 100      * loader is then used to load each of these classes and to
 101      * instantiate an instance of each class, which is then placed
 102      * into the registry for later retrieval.
 103      *
 104      * <p> The exact set of locations searched depends on the
 105      * implementation of the Java runtime environment.
 106      *
 107      * @see ClassLoader#getResources
 108      */
 109     public static void scanForPlugins() {
 110         theRegistry.registerApplicationClasspathSpis();
 111     }
 112 
 113     // ImageInputStreams
 114 
 115     /**
 116      * A class to hold information about caching.  Each
 117      * {@code ThreadGroup} will have its own copy
 118      * via the {@code AppContext} mechanism.
 119      */
 120     static class CacheInfo {
 121         boolean useCache = true;
 122         File cacheDirectory = null;
 123         Boolean hasPermission = null;
 124 
 125         public CacheInfo() {}
 126 
 127         public boolean getUseCache() {
 128             return useCache;
 129         }
 130 
 131         public void setUseCache(boolean useCache) {
 132             this.useCache = useCache;
 133         }
 134 
 135         public File getCacheDirectory() {
 136             return cacheDirectory;
 137         }
 138 
 139         public void setCacheDirectory(File cacheDirectory) {
 140             this.cacheDirectory = cacheDirectory;
 141         }
 142 
 143         public Boolean getHasPermission() {
 144             return hasPermission;
 145         }
 146 
 147         public void setHasPermission(Boolean hasPermission) {
 148             this.hasPermission = hasPermission;
 149         }
 150     }
 151 
 152     /**
 153      * Returns the {@code CacheInfo} object associated with this
 154      * {@code ThreadGroup}.
 155      */
 156     private static synchronized CacheInfo getCacheInfo() {
 157         AppContext context = AppContext.getAppContext();
 158         CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
 159         if (info == null) {
 160             info = new CacheInfo();
 161             context.put(CacheInfo.class, info);
 162         }
 163         return info;
 164     }
 165 
 166     /**
 167      * Returns the default temporary (cache) directory as defined by the
 168      * java.io.tmpdir system property.
 169      */
 170     private static String getTempDir() {
 171         GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
 172         return AccessController.doPrivileged(a);
 173     }
 174 
 175     /**
 176      * Determines whether the caller has write access to the cache
 177      * directory, stores the result in the {@code CacheInfo} object,
 178      * and returns the decision.  This method helps to prevent mysterious
 179      * SecurityExceptions to be thrown when this convenience class is used
 180      * in an applet, for example.
 181      */
 182     private static boolean hasCachePermission() {
 183         Boolean hasPermission = getCacheInfo().getHasPermission();
 184 
 185         if (hasPermission != null) {
 186             return hasPermission.booleanValue();
 187         } else {
 188             try {
 189                 SecurityManager security = System.getSecurityManager();
 190                 if (security != null) {
 191                     File cachedir = getCacheDirectory();
 192                     String cachepath;
 193 
 194                     if (cachedir != null) {
 195                         cachepath = cachedir.getPath();
 196                     } else {
 197                         cachepath = getTempDir();


 208                     String filepath = cachepath;
 209                     if (!filepath.endsWith(File.separator)) {
 210                         filepath += File.separator;
 211                     }
 212                     filepath += "*";
 213 
 214                     security.checkPermission(new FilePermission(filepath, "read, write, delete"));
 215                 }
 216             } catch (SecurityException e) {
 217                 getCacheInfo().setHasPermission(Boolean.FALSE);
 218                 return false;
 219             }
 220 
 221             getCacheInfo().setHasPermission(Boolean.TRUE);
 222             return true;
 223         }
 224     }
 225 
 226     /**
 227      * Sets a flag indicating whether a disk-based cache file should
 228      * be used when creating {@code ImageInputStream}s and
 229      * {@code ImageOutputStream}s.
 230      *
 231      * <p> When reading from a standard {@code InputStream}, it
 232      * may be necessary to save previously read information in a cache
 233      * since the underlying stream does not allow data to be re-read.
 234      * Similarly, when writing to a standard
 235      * {@code OutputStream}, a cache may be used to allow a
 236      * previously written value to be changed before flushing it to
 237      * the final destination.
 238      *
 239      * <p> The cache may reside in main memory or on disk.  Setting
 240      * this flag to {@code false} disallows the use of disk for
 241      * future streams, which may be advantageous when working with
 242      * small images, as the overhead of creating and destroying files
 243      * is removed.
 244      *
 245      * <p> On startup, the value is set to {@code true}.
 246      *
 247      * @param useCache a {@code boolean} indicating whether a
 248      * cache file should be used, in cases where it is optional.
 249      *
 250      * @see #getUseCache
 251      */
 252     public static void setUseCache(boolean useCache) {
 253         getCacheInfo().setUseCache(useCache);
 254     }
 255 
 256     /**
 257      * Returns the current value set by {@code setUseCache}, or
 258      * {@code true} if no explicit setting has been made.
 259      *
 260      * @return true if a disk-based cache may be used for
 261      * {@code ImageInputStream}s and
 262      * {@code ImageOutputStream}s.
 263      *
 264      * @see #setUseCache
 265      */
 266     public static boolean getUseCache() {
 267         return getCacheInfo().getUseCache();
 268     }
 269 
 270     /**
 271      * Sets the directory where cache files are to be created.  A
 272      * value of {@code null} indicates that the system-dependent
 273      * default temporary-file directory is to be used.  If
 274      * {@code getUseCache} returns false, this value is ignored.
 275      *
 276      * @param cacheDirectory a {@code File} specifying a directory.
 277      *
 278      * @see File#createTempFile(String, String, File)
 279      *
 280      * @exception SecurityException if the security manager denies
 281      * access to the directory.
 282      * @exception IllegalArgumentException if {@code cacheDir} is
 283      * non-{@code null} but is not a directory.
 284      *
 285      * @see #getCacheDirectory
 286      */
 287     public static void setCacheDirectory(File cacheDirectory) {
 288         if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {
 289             throw new IllegalArgumentException("Not a directory!");
 290         }
 291         getCacheInfo().setCacheDirectory(cacheDirectory);
 292         getCacheInfo().setHasPermission(null);
 293     }
 294 
 295     /**
 296      * Returns the current value set by
 297      * {@code setCacheDirectory}, or {@code null} if no
 298      * explicit setting has been made.
 299      *
 300      * @return a {@code File} indicating the directory where
 301      * cache files will be created, or {@code null} to indicate
 302      * the system-dependent default temporary-file directory.
 303      *
 304      * @see #setCacheDirectory
 305      */
 306     public static File getCacheDirectory() {
 307         return getCacheInfo().getCacheDirectory();
 308     }
 309 
 310     /**
 311      * Returns an {@code ImageInputStream} that will take its
 312      * input from the given {@code Object}.  The set of
 313      * {@code ImageInputStreamSpi}s registered with the
 314      * {@code IIORegistry} class is queried and the first one
 315      * that is able to take input from the supplied object is used to
 316      * create the returned {@code ImageInputStream}.  If no
 317      * suitable {@code ImageInputStreamSpi} exists,
 318      * {@code null} is returned.
 319      *
 320      * <p> The current cache settings from {@code getUseCache} and
 321      * {@code getCacheDirectory} will be used to control caching.
 322      *
 323      * @param input an {@code Object} to be used as an input
 324      * source, such as a {@code File}, readable
 325      * {@code RandomAccessFile}, or {@code InputStream}.
 326      *
 327      * @return an {@code ImageInputStream}, or {@code null}.
 328      *
 329      * @exception IllegalArgumentException if {@code input}
 330      * is {@code null}.
 331      * @exception IOException if a cache file is needed but cannot be
 332      * created.
 333      *
 334      * @see javax.imageio.spi.ImageInputStreamSpi
 335      */
 336     public static ImageInputStream createImageInputStream(Object input)
 337         throws IOException {
 338         if (input == null) {
 339             throw new IllegalArgumentException("input == null!");
 340         }
 341 
 342         Iterator<ImageInputStreamSpi> iter;
 343         // Ensure category is present
 344         try {
 345             iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,
 346                                                    true);
 347         } catch (IllegalArgumentException e) {
 348             return null;
 349         }
 350 


 352 
 353         while (iter.hasNext()) {
 354             ImageInputStreamSpi spi = iter.next();
 355             if (spi.getInputClass().isInstance(input)) {
 356                 try {
 357                     return spi.createInputStreamInstance(input,
 358                                                          usecache,
 359                                                          getCacheDirectory());
 360                 } catch (IOException e) {
 361                     throw new IIOException("Can't create cache file!", e);
 362                 }
 363             }
 364         }
 365 
 366         return null;
 367     }
 368 
 369     // ImageOutputStreams
 370 
 371     /**
 372      * Returns an {@code ImageOutputStream} that will send its
 373      * output to the given {@code Object}.  The set of
 374      * {@code ImageOutputStreamSpi}s registered with the
 375      * {@code IIORegistry} class is queried and the first one
 376      * that is able to send output from the supplied object is used to
 377      * create the returned {@code ImageOutputStream}.  If no
 378      * suitable {@code ImageOutputStreamSpi} exists,
 379      * {@code null} is returned.
 380      *
 381      * <p> The current cache settings from {@code getUseCache} and
 382      * {@code getCacheDirectory} will be used to control caching.
 383      *
 384      * @param output an {@code Object} to be used as an output
 385      * destination, such as a {@code File}, writable
 386      * {@code RandomAccessFile}, or {@code OutputStream}.
 387      *
 388      * @return an {@code ImageOutputStream}, or
 389      * {@code null}.
 390      *
 391      * @exception IllegalArgumentException if {@code output} is
 392      * {@code null}.
 393      * @exception IOException if a cache file is needed but cannot be
 394      * created.
 395      *
 396      * @see javax.imageio.spi.ImageOutputStreamSpi
 397      */
 398     public static ImageOutputStream createImageOutputStream(Object output)
 399         throws IOException {
 400         if (output == null) {
 401             throw new IllegalArgumentException("output == null!");
 402         }
 403 
 404         Iterator<ImageOutputStreamSpi> iter;
 405         // Ensure category is present
 406         try {
 407             iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,
 408                                                    true);
 409         } catch (IllegalArgumentException e) {
 410             return null;
 411         }
 412 


 457         // Ensure category is present
 458         Iterator<S> iter;
 459         try {
 460             iter = theRegistry.getServiceProviders(spiClass, true);
 461         } catch (IllegalArgumentException e) {
 462             return new String[0];
 463         }
 464 
 465         HashSet<String> s = new HashSet<String>();
 466         while (iter.hasNext()) {
 467             ImageReaderWriterSpi spi = iter.next();
 468             Collections.addAll(s, spiInfo.info(spi));
 469         }
 470 
 471         return s.toArray(new String[s.size()]);
 472     }
 473 
 474     // Readers
 475 
 476     /**
 477      * Returns an array of {@code String}s listing all of the
 478      * informal format names understood by the current set of registered
 479      * readers.
 480      *
 481      * @return an array of {@code String}s.
 482      */
 483     public static String[] getReaderFormatNames() {
 484         return getReaderWriterInfo(ImageReaderSpi.class,
 485                                    SpiInfo.FORMAT_NAMES);
 486     }
 487 
 488     /**
 489      * Returns an array of {@code String}s listing all of the
 490      * MIME types understood by the current set of registered
 491      * readers.
 492      *
 493      * @return an array of {@code String}s.
 494      */
 495     public static String[] getReaderMIMETypes() {
 496         return getReaderWriterInfo(ImageReaderSpi.class,
 497                                    SpiInfo.MIME_TYPES);
 498     }
 499 
 500     /**
 501      * Returns an array of {@code String}s listing all of the
 502      * file suffixes associated with the formats understood
 503      * by the current set of registered readers.
 504      *
 505      * @return an array of {@code String}s.
 506      * @since 1.6
 507      */
 508     public static String[] getReaderFileSuffixes() {
 509         return getReaderWriterInfo(ImageReaderSpi.class,
 510                                    SpiInfo.FILE_SUFFIXES);
 511     }
 512 
 513     static class ImageReaderIterator implements Iterator<ImageReader> {
 514         // Contains ImageReaderSpis
 515         private Iterator<ImageReaderSpi> iter;
 516 
 517         public ImageReaderIterator(Iterator<ImageReaderSpi> iter) {
 518             this.iter = iter;
 519         }
 520 
 521         public boolean hasNext() {
 522             return iter.hasNext();
 523         }
 524 
 525         public ImageReader next() {


 601         Method method;
 602         String name;
 603 
 604         // method returns an array of Strings
 605         public ContainsFilter(Method method,
 606                               String name) {
 607             this.method = method;
 608             this.name = name;
 609         }
 610 
 611         public boolean filter(Object elt) {
 612             try {
 613                 return contains((String[])method.invoke(elt), name);
 614             } catch (Exception e) {
 615                 return false;
 616             }
 617         }
 618     }
 619 
 620     /**
 621      * Returns an {@code Iterator} containing all currently
 622      * registered {@code ImageReader}s that claim to be able to
 623      * decode the supplied {@code Object}, typically an
 624      * {@code ImageInputStream}.
 625      *
 626      * <p> The stream position is left at its prior position upon
 627      * exit from this method.
 628      *
 629      * @param input an {@code ImageInputStream} or other
 630      * {@code Object} containing encoded image data.
 631      *
 632      * @return an {@code Iterator} containing {@code ImageReader}s.
 633      *
 634      * @exception IllegalArgumentException if {@code input} is
 635      * {@code null}.
 636      *
 637      * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
 638      */
 639     public static Iterator<ImageReader> getImageReaders(Object input) {
 640         if (input == null) {
 641             throw new IllegalArgumentException("input == null!");
 642         }
 643         Iterator<ImageReaderSpi> iter;
 644         // Ensure category is present
 645         try {
 646             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 647                                               new CanDecodeInputFilter(input),
 648                                               true);
 649         } catch (IllegalArgumentException e) {
 650             return Collections.emptyIterator();
 651         }
 652 
 653         return new ImageReaderIterator(iter);
 654     }
 655 


 664         try {
 665             readerFormatNamesMethod =
 666                 ImageReaderSpi.class.getMethod("getFormatNames");
 667             readerFileSuffixesMethod =
 668                 ImageReaderSpi.class.getMethod("getFileSuffixes");
 669             readerMIMETypesMethod =
 670                 ImageReaderSpi.class.getMethod("getMIMETypes");
 671 
 672             writerFormatNamesMethod =
 673                 ImageWriterSpi.class.getMethod("getFormatNames");
 674             writerFileSuffixesMethod =
 675                 ImageWriterSpi.class.getMethod("getFileSuffixes");
 676             writerMIMETypesMethod =
 677                 ImageWriterSpi.class.getMethod("getMIMETypes");
 678         } catch (NoSuchMethodException e) {
 679             e.printStackTrace();
 680         }
 681     }
 682 
 683     /**
 684      * Returns an {@code Iterator} containing all currently
 685      * registered {@code ImageReader}s that claim to be able to
 686      * decode the named format.
 687      *
 688      * @param formatName a {@code String} containing the informal
 689      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
 690      *
 691      * @return an {@code Iterator} containing
 692      * {@code ImageReader}s.
 693      *
 694      * @exception IllegalArgumentException if {@code formatName}
 695      * is {@code null}.
 696      *
 697      * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
 698      */
 699     public static Iterator<ImageReader>
 700         getImageReadersByFormatName(String formatName)
 701     {
 702         if (formatName == null) {
 703             throw new IllegalArgumentException("formatName == null!");
 704         }
 705         Iterator<ImageReaderSpi> iter;
 706         // Ensure category is present
 707         try {
 708             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 709                                     new ContainsFilter(readerFormatNamesMethod,
 710                                                        formatName),
 711                                                 true);
 712         } catch (IllegalArgumentException e) {
 713             return Collections.emptyIterator();
 714         }
 715         return new ImageReaderIterator(iter);
 716     }
 717 
 718     /**
 719      * Returns an {@code Iterator} containing all currently
 720      * registered {@code ImageReader}s that claim to be able to
 721      * decode files with the given suffix.
 722      *
 723      * @param fileSuffix a {@code String} containing a file
 724      * suffix (<i>e.g.</i>, "jpg" or "tiff").
 725      *
 726      * @return an {@code Iterator} containing
 727      * {@code ImageReader}s.
 728      *
 729      * @exception IllegalArgumentException if {@code fileSuffix}
 730      * is {@code null}.
 731      *
 732      * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
 733      */
 734     public static Iterator<ImageReader>
 735         getImageReadersBySuffix(String fileSuffix)
 736     {
 737         if (fileSuffix == null) {
 738             throw new IllegalArgumentException("fileSuffix == null!");
 739         }
 740         // Ensure category is present
 741         Iterator<ImageReaderSpi> iter;
 742         try {
 743             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 744                                    new ContainsFilter(readerFileSuffixesMethod,
 745                                                       fileSuffix),
 746                                               true);
 747         } catch (IllegalArgumentException e) {
 748             return Collections.emptyIterator();
 749         }
 750         return new ImageReaderIterator(iter);
 751     }
 752 
 753     /**
 754      * Returns an {@code Iterator} containing all currently
 755      * registered {@code ImageReader}s that claim to be able to
 756      * decode files with the given MIME type.
 757      *
 758      * @param MIMEType a {@code String} containing a file
 759      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
 760      *
 761      * @return an {@code Iterator} containing
 762      * {@code ImageReader}s.
 763      *
 764      * @exception IllegalArgumentException if {@code MIMEType} is
 765      * {@code null}.
 766      *
 767      * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
 768      */
 769     public static Iterator<ImageReader>
 770         getImageReadersByMIMEType(String MIMEType)
 771     {
 772         if (MIMEType == null) {
 773             throw new IllegalArgumentException("MIMEType == null!");
 774         }
 775         // Ensure category is present
 776         Iterator<ImageReaderSpi> iter;
 777         try {
 778             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
 779                                       new ContainsFilter(readerMIMETypesMethod,
 780                                                          MIMEType),
 781                                               true);
 782         } catch (IllegalArgumentException e) {
 783             return Collections.emptyIterator();
 784         }
 785         return new ImageReaderIterator(iter);
 786     }
 787 
 788     // Writers
 789 
 790     /**
 791      * Returns an array of {@code String}s listing all of the
 792      * informal format names understood by the current set of registered
 793      * writers.
 794      *
 795      * @return an array of {@code String}s.
 796      */
 797     public static String[] getWriterFormatNames() {
 798         return getReaderWriterInfo(ImageWriterSpi.class,
 799                                    SpiInfo.FORMAT_NAMES);
 800     }
 801 
 802     /**
 803      * Returns an array of {@code String}s listing all of the
 804      * MIME types understood by the current set of registered
 805      * writers.
 806      *
 807      * @return an array of {@code String}s.
 808      */
 809     public static String[] getWriterMIMETypes() {
 810         return getReaderWriterInfo(ImageWriterSpi.class,
 811                                    SpiInfo.MIME_TYPES);
 812     }
 813 
 814     /**
 815      * Returns an array of {@code String}s listing all of the
 816      * file suffixes associated with the formats understood
 817      * by the current set of registered writers.
 818      *
 819      * @return an array of {@code String}s.
 820      * @since 1.6
 821      */
 822     public static String[] getWriterFileSuffixes() {
 823         return getReaderWriterInfo(ImageWriterSpi.class,
 824                                    SpiInfo.FILE_SUFFIXES);
 825     }
 826 
 827     static class ImageWriterIterator implements Iterator<ImageWriter> {
 828         // Contains ImageWriterSpis
 829         private Iterator<ImageWriterSpi> iter;
 830 
 831         public ImageWriterIterator(Iterator<ImageWriterSpi> iter) {
 832             this.iter = iter;
 833         }
 834 
 835         public boolean hasNext() {
 836             return iter.hasNext();
 837         }
 838 
 839         public ImageWriter next() {


 847             }
 848             return null;
 849         }
 850 
 851         public void remove() {
 852             throw new UnsupportedOperationException();
 853         }
 854     }
 855 
 856     private static boolean contains(String[] names, String name) {
 857         for (int i = 0; i < names.length; i++) {
 858             if (name.equalsIgnoreCase(names[i])) {
 859                 return true;
 860             }
 861         }
 862 
 863         return false;
 864     }
 865 
 866     /**
 867      * Returns an {@code Iterator} containing all currently
 868      * registered {@code ImageWriter}s that claim to be able to
 869      * encode the named format.
 870      *
 871      * @param formatName a {@code String} containing the informal
 872      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
 873      *
 874      * @return an {@code Iterator} containing
 875      * {@code ImageWriter}s.
 876      *
 877      * @exception IllegalArgumentException if {@code formatName} is
 878      * {@code null}.
 879      *
 880      * @see javax.imageio.spi.ImageWriterSpi#getFormatNames
 881      */
 882     public static Iterator<ImageWriter>
 883         getImageWritersByFormatName(String formatName)
 884     {
 885         if (formatName == null) {
 886             throw new IllegalArgumentException("formatName == null!");
 887         }
 888         Iterator<ImageWriterSpi> iter;
 889         // Ensure category is present
 890         try {
 891             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
 892                                     new ContainsFilter(writerFormatNamesMethod,
 893                                                        formatName),
 894                                             true);
 895         } catch (IllegalArgumentException e) {
 896             return Collections.emptyIterator();
 897         }
 898         return new ImageWriterIterator(iter);
 899     }
 900 
 901     /**
 902      * Returns an {@code Iterator} containing all currently
 903      * registered {@code ImageWriter}s that claim to be able to
 904      * encode files with the given suffix.
 905      *
 906      * @param fileSuffix a {@code String} containing a file
 907      * suffix (<i>e.g.</i>, "jpg" or "tiff").
 908      *
 909      * @return an {@code Iterator} containing {@code ImageWriter}s.
 910      *
 911      * @exception IllegalArgumentException if {@code fileSuffix} is
 912      * {@code null}.
 913      *
 914      * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
 915      */
 916     public static Iterator<ImageWriter>
 917         getImageWritersBySuffix(String fileSuffix)
 918     {
 919         if (fileSuffix == null) {
 920             throw new IllegalArgumentException("fileSuffix == null!");
 921         }
 922         Iterator<ImageWriterSpi> iter;
 923         // Ensure category is present
 924         try {
 925             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
 926                                    new ContainsFilter(writerFileSuffixesMethod,
 927                                                       fileSuffix),
 928                                             true);
 929         } catch (IllegalArgumentException e) {
 930             return Collections.emptyIterator();
 931         }
 932         return new ImageWriterIterator(iter);
 933     }
 934 
 935     /**
 936      * Returns an {@code Iterator} containing all currently
 937      * registered {@code ImageWriter}s that claim to be able to
 938      * encode files with the given MIME type.
 939      *
 940      * @param MIMEType a {@code String} containing a file
 941      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
 942      *
 943      * @return an {@code Iterator} containing {@code ImageWriter}s.
 944      *
 945      * @exception IllegalArgumentException if {@code MIMEType} is
 946      * {@code null}.
 947      *
 948      * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
 949      */
 950     public static Iterator<ImageWriter>
 951         getImageWritersByMIMEType(String MIMEType)
 952     {
 953         if (MIMEType == null) {
 954             throw new IllegalArgumentException("MIMEType == null!");
 955         }
 956         Iterator<ImageWriterSpi> iter;
 957         // Ensure category is present
 958         try {
 959             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
 960                                       new ContainsFilter(writerMIMETypesMethod,
 961                                                          MIMEType),
 962                                             true);
 963         } catch (IllegalArgumentException e) {
 964             return Collections.emptyIterator();
 965         }
 966         return new ImageWriterIterator(iter);
 967     }
 968 
 969     /**
 970      * Returns an {@code ImageWriter} corresponding to the given
 971      * {@code ImageReader}, if there is one, or {@code null}
 972      * if the plug-in for this {@code ImageReader} does not
 973      * specify a corresponding {@code ImageWriter}, or if the
 974      * given {@code ImageReader} is not registered.  This
 975      * mechanism may be used to obtain an {@code ImageWriter}
 976      * that will understand the internal structure of non-pixel
 977      * metadata (as encoded by {@code IIOMetadata} objects)
 978      * generated by the {@code ImageReader}.  By obtaining this
 979      * data from the {@code ImageReader} and passing it on to the
 980      * {@code ImageWriter} obtained with this method, a client
 981      * program can read an image, modify it in some way, and write it
 982      * back out preserving all metadata, without having to understand
 983      * anything about the structure of the metadata, or even about
 984      * the image format.  Note that this method returns the
 985      * "preferred" writer, which is the first in the list returned by
 986      * {@code javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()}.
 987      *
 988      * @param reader an instance of a registered {@code ImageReader}.
 989      *
 990      * @return an {@code ImageWriter}, or null.
 991      *
 992      * @exception IllegalArgumentException if {@code reader} is
 993      * {@code null}.
 994      *
 995      * @see #getImageReader(ImageWriter)
 996      * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
 997      */
 998     public static ImageWriter getImageWriter(ImageReader reader) {
 999         if (reader == null) {
1000             throw new IllegalArgumentException("reader == null!");
1001         }
1002 
1003         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1004         if (readerSpi == null) {
1005             Iterator<ImageReaderSpi> readerSpiIter;
1006             // Ensure category is present
1007             try {
1008                 readerSpiIter =
1009                     theRegistry.getServiceProviders(ImageReaderSpi.class,
1010                                                     false);
1011             } catch (IllegalArgumentException e) {
1012                 return null;
1013             }


1037             return null;
1038         }
1039 
1040         ImageWriterSpi writerSpi = (ImageWriterSpi)
1041             theRegistry.getServiceProviderByClass(writerSpiClass);
1042         if (writerSpi == null) {
1043             return null;
1044         }
1045 
1046         try {
1047             return writerSpi.createWriterInstance();
1048         } catch (IOException e) {
1049             // Deregister the spi in this case, but only as a writerSpi
1050             theRegistry.deregisterServiceProvider(writerSpi,
1051                                                   ImageWriterSpi.class);
1052             return null;
1053         }
1054     }
1055 
1056     /**
1057      * Returns an {@code ImageReader} corresponding to the given
1058      * {@code ImageWriter}, if there is one, or {@code null}
1059      * if the plug-in for this {@code ImageWriter} does not
1060      * specify a corresponding {@code ImageReader}, or if the
1061      * given {@code ImageWriter} is not registered.  This method
1062      * is provided principally for symmetry with
1063      * {@code getImageWriter(ImageReader)}.  Note that this
1064      * method returns the "preferred" reader, which is the first in
1065      * the list returned by
1066      * javax.imageio.spi.ImageWriterSpi.{@code getImageReaderSpiNames()}.
1067      *
1068      * @param writer an instance of a registered {@code ImageWriter}.
1069      *
1070      * @return an {@code ImageReader}, or null.
1071      *
1072      * @exception IllegalArgumentException if {@code writer} is
1073      * {@code null}.
1074      *
1075      * @see #getImageWriter(ImageReader)
1076      * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
1077      */
1078     public static ImageReader getImageReader(ImageWriter writer) {
1079         if (writer == null) {
1080             throw new IllegalArgumentException("writer == null!");
1081         }
1082 
1083         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1084         if (writerSpi == null) {
1085             Iterator<ImageWriterSpi> writerSpiIter;
1086             // Ensure category is present
1087             try {
1088                 writerSpiIter =
1089                     theRegistry.getServiceProviders(ImageWriterSpi.class,
1090                                                     false);
1091             } catch (IllegalArgumentException e) {
1092                 return null;
1093             }


1117             return null;
1118         }
1119 
1120         ImageReaderSpi readerSpi = (ImageReaderSpi)
1121             theRegistry.getServiceProviderByClass(readerSpiClass);
1122         if (readerSpi == null) {
1123             return null;
1124         }
1125 
1126         try {
1127             return readerSpi.createReaderInstance();
1128         } catch (IOException e) {
1129             // Deregister the spi in this case, but only as a readerSpi
1130             theRegistry.deregisterServiceProvider(readerSpi,
1131                                                   ImageReaderSpi.class);
1132             return null;
1133         }
1134     }
1135 
1136     /**
1137      * Returns an {@code Iterator} containing all currently
1138      * registered {@code ImageWriter}s that claim to be able to
1139      * encode images of the given layout (specified using an
1140      * {@code ImageTypeSpecifier}) in the given format.
1141      *
1142      * @param type an {@code ImageTypeSpecifier} indicating the
1143      * layout of the image to be written.
1144      * @param formatName the informal name of the {@code format}.
1145      *
1146      * @return an {@code Iterator} containing {@code ImageWriter}s.
1147      *
1148      * @exception IllegalArgumentException if any parameter is
1149      * {@code null}.
1150      *
1151      * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
1152      */
1153     public static Iterator<ImageWriter>
1154         getImageWriters(ImageTypeSpecifier type, String formatName)
1155     {
1156         if (type == null) {
1157             throw new IllegalArgumentException("type == null!");
1158         }
1159         if (formatName == null) {
1160             throw new IllegalArgumentException("formatName == null!");
1161         }
1162 
1163         Iterator<ImageWriterSpi> iter;
1164         // Ensure category is present
1165         try {
1166             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
1167                                  new CanEncodeImageAndFormatFilter(type,
1168                                                                    formatName),
1169                                             true);


1204 
1205         String readerSpiName;
1206         String writerSpiName;
1207 
1208         public TranscoderFilter(ImageReaderSpi readerSpi,
1209                                 ImageWriterSpi writerSpi) {
1210             this.readerSpiName = readerSpi.getClass().getName();
1211             this.writerSpiName = writerSpi.getClass().getName();
1212         }
1213 
1214         public boolean filter(Object elt) {
1215             ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;
1216             String readerName = spi.getReaderServiceProviderName();
1217             String writerName = spi.getWriterServiceProviderName();
1218             return (readerName.equals(readerSpiName) &&
1219                     writerName.equals(writerSpiName));
1220         }
1221     }
1222 
1223     /**
1224      * Returns an {@code Iterator} containing all currently
1225      * registered {@code ImageTranscoder}s that claim to be
1226      * able to transcode between the metadata of the given
1227      * {@code ImageReader} and {@code ImageWriter}.
1228      *
1229      * @param reader an {@code ImageReader}.
1230      * @param writer an {@code ImageWriter}.
1231      *
1232      * @return an {@code Iterator} containing
1233      * {@code ImageTranscoder}s.
1234      *
1235      * @exception IllegalArgumentException if {@code reader} or
1236      * {@code writer} is {@code null}.
1237      */
1238     public static Iterator<ImageTranscoder>
1239         getImageTranscoders(ImageReader reader, ImageWriter writer)
1240     {
1241         if (reader == null) {
1242             throw new IllegalArgumentException("reader == null!");
1243         }
1244         if (writer == null) {
1245             throw new IllegalArgumentException("writer == null!");
1246         }
1247         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1248         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1249         ServiceRegistry.Filter filter =
1250             new TranscoderFilter(readerSpi, writerSpi);
1251 
1252         Iterator<ImageTranscoderSpi> iter;
1253         // Ensure category is present
1254         try {
1255             iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,
1256                                             filter, true);
1257         } catch (IllegalArgumentException e) {
1258             return Collections.emptyIterator();
1259         }
1260         return new ImageTranscoderIterator(iter);
1261     }
1262 
1263     // All-in-one methods
1264 
1265     /**
1266      * Returns a {@code BufferedImage} as the result of decoding
1267      * a supplied {@code File} with an {@code ImageReader}
1268      * chosen automatically from among those currently registered.
1269      * The {@code File} is wrapped in an
1270      * {@code ImageInputStream}.  If no registered
1271      * {@code ImageReader} claims to be able to read the
1272      * resulting stream, {@code null} is returned.
1273      *
1274      * <p> The current cache settings from {@code getUseCache} and
1275      * {@code getCacheDirectory} will be used to control caching in the
1276      * {@code ImageInputStream} that is created.
1277      *
1278      * <p> Note that there is no {@code read} method that takes a
1279      * filename as a {@code String}; use this method instead after
1280      * creating a {@code File} from the filename.
1281      *
1282      * <p> This method does not attempt to locate
1283      * {@code ImageReader}s that can read directly from a
1284      * {@code File}; that may be accomplished using
1285      * {@code IIORegistry} and {@code ImageReaderSpi}.
1286      *
1287      * @param input a {@code File} to read from.
1288      *
1289      * @return a {@code BufferedImage} containing the decoded
1290      * contents of the input, or {@code null}.
1291      *
1292      * @exception IllegalArgumentException if {@code input} is
1293      * {@code null}.
1294      * @exception IOException if an error occurs during reading.
1295      */
1296     public static BufferedImage read(File input) throws IOException {
1297         if (input == null) {
1298             throw new IllegalArgumentException("input == null!");
1299         }
1300         if (!input.canRead()) {
1301             throw new IIOException("Can't read input file!");
1302         }
1303 
1304         ImageInputStream stream = createImageInputStream(input);
1305         if (stream == null) {
1306             throw new IIOException("Can't create an ImageInputStream!");
1307         }
1308         BufferedImage bi = read(stream);
1309         if (bi == null) {
1310             stream.close();
1311         }
1312         return bi;
1313     }
1314 
1315     /**
1316      * Returns a {@code BufferedImage} as the result of decoding
1317      * a supplied {@code InputStream} with an {@code ImageReader}
1318      * chosen automatically from among those currently registered.
1319      * The {@code InputStream} is wrapped in an
1320      * {@code ImageInputStream}.  If no registered
1321      * {@code ImageReader} claims to be able to read the
1322      * resulting stream, {@code null} is returned.
1323      *
1324      * <p> The current cache settings from {@code getUseCache} and
1325      * {@code getCacheDirectory} will be used to control caching in the
1326      * {@code ImageInputStream} that is created.
1327      *
1328      * <p> This method does not attempt to locate
1329      * {@code ImageReader}s that can read directly from an
1330      * {@code InputStream}; that may be accomplished using
1331      * {@code IIORegistry} and {@code ImageReaderSpi}.
1332      *
1333      * <p> This method <em>does not</em> close the provided
1334      * {@code InputStream} after the read operation has completed;
1335      * it is the responsibility of the caller to close the stream, if desired.
1336      *
1337      * @param input an {@code InputStream} to read from.
1338      *
1339      * @return a {@code BufferedImage} containing the decoded
1340      * contents of the input, or {@code null}.
1341      *
1342      * @exception IllegalArgumentException if {@code input} is
1343      * {@code null}.
1344      * @exception IOException if an error occurs during reading.
1345      */
1346     public static BufferedImage read(InputStream input) throws IOException {
1347         if (input == null) {
1348             throw new IllegalArgumentException("input == null!");
1349         }
1350 
1351         ImageInputStream stream = createImageInputStream(input);
1352         BufferedImage bi = read(stream);
1353         if (bi == null) {
1354             stream.close();
1355         }
1356         return bi;
1357     }
1358 
1359     /**
1360      * Returns a {@code BufferedImage} as the result of decoding
1361      * a supplied {@code URL} with an {@code ImageReader}
1362      * chosen automatically from among those currently registered.  An
1363      * {@code InputStream} is obtained from the {@code URL},
1364      * which is wrapped in an {@code ImageInputStream}.  If no
1365      * registered {@code ImageReader} claims to be able to read
1366      * the resulting stream, {@code null} is returned.
1367      *
1368      * <p> The current cache settings from {@code getUseCache} and
1369      * {@code getCacheDirectory} will be used to control caching in the
1370      * {@code ImageInputStream} that is created.
1371      *
1372      * <p> This method does not attempt to locate
1373      * {@code ImageReader}s that can read directly from a
1374      * {@code URL}; that may be accomplished using
1375      * {@code IIORegistry} and {@code ImageReaderSpi}.
1376      *
1377      * @param input a {@code URL} to read from.
1378      *
1379      * @return a {@code BufferedImage} containing the decoded
1380      * contents of the input, or {@code null}.
1381      *
1382      * @exception IllegalArgumentException if {@code input} is
1383      * {@code null}.
1384      * @exception IOException if an error occurs during reading.
1385      */
1386     public static BufferedImage read(URL input) throws IOException {
1387         if (input == null) {
1388             throw new IllegalArgumentException("input == null!");
1389         }
1390 
1391         InputStream istream = null;
1392         try {
1393             istream = input.openStream();
1394         } catch (IOException e) {
1395             throw new IIOException("Can't get input stream from URL!", e);
1396         }
1397         ImageInputStream stream = createImageInputStream(istream);
1398         BufferedImage bi;
1399         try {
1400             bi = read(stream);
1401             if (bi == null) {
1402                 stream.close();
1403             }
1404         } finally {
1405             istream.close();
1406         }
1407         return bi;
1408     }
1409 
1410     /**
1411      * Returns a {@code BufferedImage} as the result of decoding
1412      * a supplied {@code ImageInputStream} with an
1413      * {@code ImageReader} chosen automatically from among those
1414      * currently registered.  If no registered
1415      * {@code ImageReader} claims to be able to read the stream,
1416      * {@code null} is returned.
1417      *
1418      * <p> Unlike most other methods in this class, this method <em>does</em>
1419      * close the provided {@code ImageInputStream} after the read
1420      * operation has completed, unless {@code null} is returned,
1421      * in which case this method <em>does not</em> close the stream.
1422      *
1423      * @param stream an {@code ImageInputStream} to read from.
1424      *
1425      * @return a {@code BufferedImage} containing the decoded
1426      * contents of the input, or {@code null}.
1427      *
1428      * @exception IllegalArgumentException if {@code stream} is
1429      * {@code null}.
1430      * @exception IOException if an error occurs during reading.
1431      */
1432     public static BufferedImage read(ImageInputStream stream)
1433         throws IOException {
1434         if (stream == null) {
1435             throw new IllegalArgumentException("stream == null!");
1436         }
1437 
1438         Iterator<ImageReader> iter = getImageReaders(stream);
1439         if (!iter.hasNext()) {
1440             return null;
1441         }
1442 
1443         ImageReader reader = iter.next();
1444         ImageReadParam param = reader.getDefaultReadParam();
1445         reader.setInput(stream, true, true);
1446         BufferedImage bi;
1447         try {
1448             bi = reader.read(0, param);
1449         } finally {
1450             reader.dispose();
1451             stream.close();
1452         }
1453         return bi;
1454     }
1455 
1456     /**
1457      * Writes an image using the an arbitrary {@code ImageWriter}
1458      * that supports the given format to an
1459      * {@code ImageOutputStream}.  The image is written to the
1460      * {@code ImageOutputStream} starting at the current stream
1461      * pointer, overwriting existing stream data from that point
1462      * forward, if present.
1463      *
1464      * <p> This method <em>does not</em> close the provided
1465      * {@code ImageOutputStream} after the write operation has completed;
1466      * it is the responsibility of the caller to close the stream, if desired.
1467      *
1468      * @param im a {@code RenderedImage} to be written.
1469      * @param formatName a {@code String} containing the informal
1470      * name of the format.
1471      * @param output an {@code ImageOutputStream} to be written to.
1472      *
1473      * @return {@code false} if no appropriate writer is found.
1474      *
1475      * @exception IllegalArgumentException if any parameter is
1476      * {@code null}.
1477      * @exception IOException if an error occurs during writing.
1478      */
1479     public static boolean write(RenderedImage im,
1480                                 String formatName,
1481                                 ImageOutputStream output) throws IOException {
1482         if (im == null) {
1483             throw new IllegalArgumentException("im == null!");
1484         }
1485         if (formatName == null) {
1486             throw new IllegalArgumentException("formatName == null!");
1487         }
1488         if (output == null) {
1489             throw new IllegalArgumentException("output == null!");
1490         }
1491 
1492         return doWrite(im, getWriter(im, formatName), output);
1493     }
1494 
1495     /**
1496      * Writes an image using an arbitrary {@code ImageWriter}
1497      * that supports the given format to a {@code File}.  If
1498      * there is already a {@code File} present, its contents are
1499      * discarded.
1500      *
1501      * @param im a {@code RenderedImage} to be written.
1502      * @param formatName a {@code String} containing the informal
1503      * name of the format.
1504      * @param output a {@code File} to be written to.
1505      *
1506      * @return {@code false} if no appropriate writer is found.
1507      *
1508      * @exception IllegalArgumentException if any parameter is
1509      * {@code null}.
1510      * @exception IOException if an error occurs during writing.
1511      */
1512     public static boolean write(RenderedImage im,
1513                                 String formatName,
1514                                 File output) throws IOException {
1515         if (output == null) {
1516             throw new IllegalArgumentException("output == null!");
1517         }
1518         ImageOutputStream stream = null;
1519 
1520         ImageWriter writer = getWriter(im, formatName);
1521         if (writer == null) {
1522             /* Do not make changes in the file system if we have
1523              * no appropriate writer.
1524              */
1525             return false;
1526         }
1527 
1528         try {
1529             output.delete();
1530             stream = createImageOutputStream(output);
1531         } catch (IOException e) {
1532             throw new IIOException("Can't create output stream!", e);
1533         }
1534 
1535         try {
1536             return doWrite(im, writer, stream);
1537         } finally {
1538             stream.close();
1539         }
1540     }
1541 
1542     /**
1543      * Writes an image using an arbitrary {@code ImageWriter}
1544      * that supports the given format to an {@code OutputStream}.
1545      *
1546      * <p> This method <em>does not</em> close the provided
1547      * {@code OutputStream} after the write operation has completed;
1548      * it is the responsibility of the caller to close the stream, if desired.
1549      *
1550      * <p> The current cache settings from {@code getUseCache} and
1551      * {@code getCacheDirectory} will be used to control caching.
1552      *
1553      * @param im a {@code RenderedImage} to be written.
1554      * @param formatName a {@code String} containing the informal
1555      * name of the format.
1556      * @param output an {@code OutputStream} to be written to.
1557      *
1558      * @return {@code false} if no appropriate writer is found.
1559      *
1560      * @exception IllegalArgumentException if any parameter is
1561      * {@code null}.
1562      * @exception IOException if an error occurs during writing.
1563      */
1564     public static boolean write(RenderedImage im,
1565                                 String formatName,
1566                                 OutputStream output) throws IOException {
1567         if (output == null) {
1568             throw new IllegalArgumentException("output == null!");
1569         }
1570         ImageOutputStream stream = null;
1571         try {
1572             stream = createImageOutputStream(output);
1573         } catch (IOException e) {
1574             throw new IIOException("Can't create output stream!", e);
1575         }
1576 
1577         try {
1578             return doWrite(im, getWriter(im, formatName), stream);
1579         } finally {
1580             stream.close();
1581         }
1582     }
1583 
1584     /**
1585      * Returns {@code ImageWriter} instance according to given
1586      * rendered image and image format or {@code null} if there
1587      * is no appropriate writer.
1588      */
1589     private static ImageWriter getWriter(RenderedImage im,
1590                                          String formatName) {
1591         ImageTypeSpecifier type =
1592             ImageTypeSpecifier.createFromRenderedImage(im);
1593         Iterator<ImageWriter> iter = getImageWriters(type, formatName);
1594 
1595         if (iter.hasNext()) {
1596             return iter.next();
1597         } else {
1598             return null;
1599         }
1600     }
1601 
1602     /**
1603      * Writes image to output stream  using given image writer.
1604      */
1605     private static boolean doWrite(RenderedImage im, ImageWriter writer,
1606                                  ImageOutputStream output) throws IOException {
< prev index next >