1 /*
2  * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */

25
26 package javax.imageio;
27
28 import java.awt.image.BufferedImage;
29 import java.awt.image.RenderedImage;
30 import java.io.File;
31 import java.io.FilePermission;
32 import java.io.InputStream;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.lang.reflect.Method;
36 import java.net.URL;
37 import java.security.AccessController;
38 import java.util.Arrays;
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();
198
199                         if (cachepath == null || cachepath.isEmpty()) {
200                             getCacheInfo().setHasPermission(Boolean.FALSE);
201                             return false;
202                         }
203                     }
204
205                     // we have to check whether we can read, write,
206                     // and delete cache files.
207                     // So, compose cache file path and check it.
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 trueif 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 falsethis 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 nullif 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
351         boolean usecache = getUseCache() && hasCachePermission();
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
413         boolean usecache = getUseCache() && hasCachePermission();
414
415         while (iter.hasNext()) {
416             ImageOutputStreamSpi spi = iter.next();
417             if (spi.getOutputClass().isInstance(output)) {
418                 try {
419                     return spi.createOutputStreamInstance(output,
420                                                           usecache,
421                                                           getCacheDirectory());
422                 } catch (IOException e) {
423                     throw new IIOException("Can't create cache file!", e);
424                 }
425             }
426         }
427
428         return null;
429     }
430
431     private static enum SpiInfo {
432         FORMAT_NAMES {
433             @Override
434             String[] info(ImageReaderWriterSpi spi) {
435                 return spi.getFormatNames();
436             }
437         },
438         MIME_TYPES {
439             @Override
440             String[] info(ImageReaderWriterSpi spi) {
441                 return spi.getMIMETypes();
442             }
443         },
444         FILE_SUFFIXES {
445             @Override
446             String[] info(ImageReaderWriterSpi spi) {
447                 return spi.getFileSuffixes();
448             }
449         };
450
451         abstract String[] info(ImageReaderWriterSpi spi);
452     }
453
454     private static <S extends ImageReaderWriterSpi>
455         String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo)
456     {
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<>();
466         while (iter.hasNext()) {
467             ImageReaderWriterSpi spi = iter.next();
468             String[] info = spiInfo.info(spi);
469             if (info != null) {
470                 Collections.addAll(s, info);
471             }
472         }
473
474         return s.toArray(new String[s.size()]);
475     }
476
477     // Readers
478
479     /**
480      * Returns an array of {@code String}s listing all of the
481      * informal format names understood by the current set of registered
482      * readers.
483      *
484      * @return an array of {@code String}s.
485      */

486     public static String[] getReaderFormatNames() {
487         return getReaderWriterInfo(ImageReaderSpi.class,
488                                    SpiInfo.FORMAT_NAMES);
489     }
490
491     /**
492      * Returns an array of {@code String}s listing all of the
493      * MIME types understood by the current set of registered
494      * readers.
495      *
496      * @return an array of {@code String}s.
497      */

498     public static String[] getReaderMIMETypes() {
499         return getReaderWriterInfo(ImageReaderSpi.class,
500                                    SpiInfo.MIME_TYPES);
501     }
502
503     /**
504      * Returns an array of {@code String}s listing all of the
505      * file suffixes associated with the formats understood
506      * by the current set of registered readers.
507      *
508      * @return an array of {@code String}s.
509      * @since 1.6
510      */

511     public static String[] getReaderFileSuffixes() {
512         return getReaderWriterInfo(ImageReaderSpi.class,
513                                    SpiInfo.FILE_SUFFIXES);
514     }
515
516     static class ImageReaderIterator implements Iterator<ImageReader> {
517         // Contains ImageReaderSpis
518         private Iterator<ImageReaderSpi> iter;
519
520         public ImageReaderIterator(Iterator<ImageReaderSpi> iter) {
521             this.iter = iter;
522         }
523
524         public boolean hasNext() {
525             return iter.hasNext();
526         }
527
528         public ImageReader next() {
529             ImageReaderSpi spi = null;
530             try {
531                 spi = iter.next();
532                 return spi.createReaderInstance();
533             } catch (IOException e) {
534                 // Deregister the spi in this case, but only as
535                 // an ImageReaderSpi
536                 theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class);
537             }
538             return null;
539         }
540
541         public void remove() {
542             throw new UnsupportedOperationException();
543         }
544     }
545
546     static class CanDecodeInputFilter
547         implements ServiceRegistry.Filter {
548
549         Object input;
550
551         public CanDecodeInputFilter(Object input) {
552             this.input = input;
553         }
554
555         public boolean filter(Object elt) {
556             try {
557                 ImageReaderSpi spi = (ImageReaderSpi)elt;
558                 ImageInputStream stream = null;
559                 if (input instanceof ImageInputStream) {
560                     stream = (ImageInputStream)input;
561                 }
562
563                 // Perform mark/reset as a defensive measure
564                 // even though plug-ins are supposed to take
565                 // care of it.
566                 boolean canDecode = false;
567                 if (stream != null) {
568                     stream.mark();
569                 }
570                 try {
571                     canDecode = spi.canDecodeInput(input);
572                 } finally {
573                     if (stream != null) {
574                         stream.reset();
575                     }
576                 }
577
578                 return canDecode;
579             } catch (IOException e) {
580                 return false;
581             }
582         }
583     }
584
585     static class CanEncodeImageAndFormatFilter
586         implements ServiceRegistry.Filter {
587
588         ImageTypeSpecifier type;
589         String formatName;
590
591         public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type,
592                                              String formatName) {
593             this.type = type;
594             this.formatName = formatName;
595         }
596
597         public boolean filter(Object elt) {
598             ImageWriterSpi spi = (ImageWriterSpi)elt;
599             return Arrays.asList(spi.getFormatNames()).contains(formatName) &&
600                 spi.canEncodeImage(type);
601         }
602     }
603
604     static class ContainsFilter
605         implements ServiceRegistry.Filter {
606
607         Method method;
608         String name;
609
610         // method returns an array of Strings
611         public ContainsFilter(Method method,
612                               String name) {
613             this.method = method;
614             this.name = name;
615         }
616
617         public boolean filter(Object elt) {
618             try {
619                 return contains((String[])method.invoke(elt), name);
620             } catch (Exception e) {
621                 return false;
622             }
623         }
624     }
625
626     /**
627      * Returns an {@code Iterator} containing all currently
628      * registered {@code ImageReader}s that claim to be able to
629      * decode the supplied {@code Object}, typically an
630      * {@code ImageInputStream}.
631      *
632      * <p> The stream position is left at its prior position upon
633      * exit from this method.
634      *
635      * @param input an {@code ImageInputStream} or other
636      * {@code Object} containing encoded image data.
637      *
638      * @return an {@code Iterator} containing {@code ImageReader}s.
639      *
640      * @exception IllegalArgumentException if {@code input} is
641      * {@code null}.
642      *
643      * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
644      */

645     public static Iterator<ImageReader> getImageReaders(Object input) {
646         if (input == null) {
647             throw new IllegalArgumentException("input == null!");
648         }
649         Iterator<ImageReaderSpi> iter;
650         // Ensure category is present
651         try {
652             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
653                                               new CanDecodeInputFilter(input),
654                                               true);
655         } catch (IllegalArgumentException e) {
656             return Collections.emptyIterator();
657         }
658
659         return new ImageReaderIterator(iter);
660     }
661
662     private static Method readerFormatNamesMethod;
663     private static Method readerFileSuffixesMethod;
664     private static Method readerMIMETypesMethod;
665     private static Method writerFormatNamesMethod;
666     private static Method writerFileSuffixesMethod;
667     private static Method writerMIMETypesMethod;
668
669     static {
670         try {
671             readerFormatNamesMethod =
672                 ImageReaderSpi.class.getMethod("getFormatNames");
673             readerFileSuffixesMethod =
674                 ImageReaderSpi.class.getMethod("getFileSuffixes");
675             readerMIMETypesMethod =
676                 ImageReaderSpi.class.getMethod("getMIMETypes");
677
678             writerFormatNamesMethod =
679                 ImageWriterSpi.class.getMethod("getFormatNames");
680             writerFileSuffixesMethod =
681                 ImageWriterSpi.class.getMethod("getFileSuffixes");
682             writerMIMETypesMethod =
683                 ImageWriterSpi.class.getMethod("getMIMETypes");
684         } catch (NoSuchMethodException e) {
685             e.printStackTrace();
686         }
687     }
688
689     /**
690      * Returns an {@code Iterator} containing all currently
691      * registered {@code ImageReader}s that claim to be able to
692      * decode the named format.
693      *
694      * @param formatName a {@code String} containing the informal
695      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
696      *
697      * @return an {@code Iterator} containing
698      * {@code ImageReader}s.
699      *
700      * @exception IllegalArgumentException if {@code formatName}
701      * is {@code null}.
702      *
703      * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
704      */

705     public static Iterator<ImageReader>
706         getImageReadersByFormatName(String formatName)
707     {
708         if (formatName == null) {
709             throw new IllegalArgumentException("formatName == null!");
710         }
711         Iterator<ImageReaderSpi> iter;
712         // Ensure category is present
713         try {
714             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
715                                     new ContainsFilter(readerFormatNamesMethod,
716                                                        formatName),
717                                                 true);
718         } catch (IllegalArgumentException e) {
719             return Collections.emptyIterator();
720         }
721         return new ImageReaderIterator(iter);
722     }
723
724     /**
725      * Returns an {@code Iterator} containing all currently
726      * registered {@code ImageReader}s that claim to be able to
727      * decode files with the given suffix.
728      *
729      * @param fileSuffix a {@code String} containing a file
730      * suffix (<i>e.g.</i>, "jpg" or "tiff").
731      *
732      * @return an {@code Iterator} containing
733      * {@code ImageReader}s.
734      *
735      * @exception IllegalArgumentException if {@code fileSuffix}
736      * is {@code null}.
737      *
738      * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
739      */

740     public static Iterator<ImageReader>
741         getImageReadersBySuffix(String fileSuffix)
742     {
743         if (fileSuffix == null) {
744             throw new IllegalArgumentException("fileSuffix == null!");
745         }
746         // Ensure category is present
747         Iterator<ImageReaderSpi> iter;
748         try {
749             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
750                                    new ContainsFilter(readerFileSuffixesMethod,
751                                                       fileSuffix),
752                                               true);
753         } catch (IllegalArgumentException e) {
754             return Collections.emptyIterator();
755         }
756         return new ImageReaderIterator(iter);
757     }
758
759     /**
760      * Returns an {@code Iterator} containing all currently
761      * registered {@code ImageReader}s that claim to be able to
762      * decode files with the given MIME type.
763      *
764      * @param MIMEType a {@code String} containing a file
765      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
766      *
767      * @return an {@code Iterator} containing
768      * {@code ImageReader}s.
769      *
770      * @exception IllegalArgumentException if {@code MIMEType} is
771      * {@code null}.
772      *
773      * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
774      */

775     public static Iterator<ImageReader>
776         getImageReadersByMIMEType(String MIMEType)
777     {
778         if (MIMEType == null) {
779             throw new IllegalArgumentException("MIMEType == null!");
780         }
781         // Ensure category is present
782         Iterator<ImageReaderSpi> iter;
783         try {
784             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
785                                       new ContainsFilter(readerMIMETypesMethod,
786                                                          MIMEType),
787                                               true);
788         } catch (IllegalArgumentException e) {
789             return Collections.emptyIterator();
790         }
791         return new ImageReaderIterator(iter);
792     }
793
794     // Writers
795
796     /**
797      * Returns an array of {@code String}s listing all of the
798      * informal format names understood by the current set of registered
799      * writers.
800      *
801      * @return an array of {@code String}s.
802      */

803     public static String[] getWriterFormatNames() {
804         return getReaderWriterInfo(ImageWriterSpi.class,
805                                    SpiInfo.FORMAT_NAMES);
806     }
807
808     /**
809      * Returns an array of {@code String}s listing all of the
810      * MIME types understood by the current set of registered
811      * writers.
812      *
813      * @return an array of {@code String}s.
814      */

815     public static String[] getWriterMIMETypes() {
816         return getReaderWriterInfo(ImageWriterSpi.class,
817                                    SpiInfo.MIME_TYPES);
818     }
819
820     /**
821      * Returns an array of {@code String}s listing all of the
822      * file suffixes associated with the formats understood
823      * by the current set of registered writers.
824      *
825      * @return an array of {@code String}s.
826      * @since 1.6
827      */

828     public static String[] getWriterFileSuffixes() {
829         return getReaderWriterInfo(ImageWriterSpi.class,
830                                    SpiInfo.FILE_SUFFIXES);
831     }
832
833     static class ImageWriterIterator implements Iterator<ImageWriter> {
834         // Contains ImageWriterSpis
835         private Iterator<ImageWriterSpi> iter;
836
837         public ImageWriterIterator(Iterator<ImageWriterSpi> iter) {
838             this.iter = iter;
839         }
840
841         public boolean hasNext() {
842             return iter.hasNext();
843         }
844
845         public ImageWriter next() {
846             ImageWriterSpi spi = null;
847             try {
848                 spi = iter.next();
849                 return spi.createWriterInstance();
850             } catch (IOException e) {
851                 // Deregister the spi in this case, but only as a writerSpi
852                 theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class);
853             }
854             return null;
855         }
856
857         public void remove() {
858             throw new UnsupportedOperationException();
859         }
860     }
861
862     private static boolean contains(String[] names, String name) {
863         for (int i = 0; i < names.length; i++) {
864             if (name.equalsIgnoreCase(names[i])) {
865                 return true;
866             }
867         }
868
869         return false;
870     }
871
872     /**
873      * Returns an {@code Iterator} containing all currently
874      * registered {@code ImageWriter}s that claim to be able to
875      * encode the named format.
876      *
877      * @param formatName a {@code String} containing the informal
878      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
879      *
880      * @return an {@code Iterator} containing
881      * {@code ImageWriter}s.
882      *
883      * @exception IllegalArgumentException if {@code formatName} is
884      * {@code null}.
885      *
886      * @see javax.imageio.spi.ImageWriterSpi#getFormatNames
887      */

888     public static Iterator<ImageWriter>
889         getImageWritersByFormatName(String formatName)
890     {
891         if (formatName == null) {
892             throw new IllegalArgumentException("formatName == null!");
893         }
894         Iterator<ImageWriterSpi> iter;
895         // Ensure category is present
896         try {
897             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
898                                     new ContainsFilter(writerFormatNamesMethod,
899                                                        formatName),
900                                             true);
901         } catch (IllegalArgumentException e) {
902             return Collections.emptyIterator();
903         }
904         return new ImageWriterIterator(iter);
905     }
906
907     /**
908      * Returns an {@code Iterator} containing all currently
909      * registered {@code ImageWriter}s that claim to be able to
910      * encode files with the given suffix.
911      *
912      * @param fileSuffix a {@code String} containing a file
913      * suffix (<i>e.g.</i>, "jpg" or "tiff").
914      *
915      * @return an {@code Iterator} containing {@code ImageWriter}s.
916      *
917      * @exception IllegalArgumentException if {@code fileSuffix} is
918      * {@code null}.
919      *
920      * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
921      */

922     public static Iterator<ImageWriter>
923         getImageWritersBySuffix(String fileSuffix)
924     {
925         if (fileSuffix == null) {
926             throw new IllegalArgumentException("fileSuffix == null!");
927         }
928         Iterator<ImageWriterSpi> iter;
929         // Ensure category is present
930         try {
931             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
932                                    new ContainsFilter(writerFileSuffixesMethod,
933                                                       fileSuffix),
934                                             true);
935         } catch (IllegalArgumentException e) {
936             return Collections.emptyIterator();
937         }
938         return new ImageWriterIterator(iter);
939     }
940
941     /**
942      * Returns an {@code Iterator} containing all currently
943      * registered {@code ImageWriter}s that claim to be able to
944      * encode files with the given MIME type.
945      *
946      * @param MIMEType a {@code String} containing a file
947      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
948      *
949      * @return an {@code Iterator} containing {@code ImageWriter}s.
950      *
951      * @exception IllegalArgumentException if {@code MIMEType} is
952      * {@code null}.
953      *
954      * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
955      */

956     public static Iterator<ImageWriter>
957         getImageWritersByMIMEType(String MIMEType)
958     {
959         if (MIMEType == null) {
960             throw new IllegalArgumentException("MIMEType == null!");
961         }
962         Iterator<ImageWriterSpi> iter;
963         // Ensure category is present
964         try {
965             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
966                                       new ContainsFilter(writerMIMETypesMethod,
967                                                          MIMEType),
968                                             true);
969         } catch (IllegalArgumentException e) {
970             return Collections.emptyIterator();
971         }
972         return new ImageWriterIterator(iter);
973     }
974
975     /**
976      * Returns an {@code ImageWriter} corresponding to the given
977      * {@code ImageReader}, if there is one, or {@code null}
978      * if the plug-in for this {@code ImageReader} does not
979      * specify a corresponding {@code ImageWriter}, or if the
980      * given {@code ImageReader} is not registered.  This
981      * mechanism may be used to obtain an {@code ImageWriter}
982      * that will understand the internal structure of non-pixel
983      * metadata (as encoded by {@code IIOMetadata} objects)
984      * generated by the {@code ImageReader}.  By obtaining this
985      * data from the {@code ImageReader} and passing it on to the
986      * {@code ImageWriter} obtained with this method, a client
987      * program can read an image, modify it in some way, and write it
988      * back out preserving all metadata, without having to understand
989      * anything about the structure of the metadata, or even about
990      * the image format.  Note that this method returns the
991      * "preferred" writer, which is the first in the list returned by
992      * {@code javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()}.
993      *
994      * @param reader an instance of a registered {@code ImageReader}.
995      *
996      * @return an {@code ImageWriter}, or null.
997      *
998      * @exception IllegalArgumentException if {@code reader} is
999      * {@code null}.
1000      *
1001      * @see #getImageReader(ImageWriter)
1002      * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
1003      */

1004     public static ImageWriter getImageWriter(ImageReader reader) {
1005         if (reader == null) {
1006             throw new IllegalArgumentException("reader == null!");
1007         }
1008
1009         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1010         if (readerSpi == null) {
1011             Iterator<ImageReaderSpi> readerSpiIter;
1012             // Ensure category is present
1013             try {
1014                 readerSpiIter =
1015                     theRegistry.getServiceProviders(ImageReaderSpi.class,
1016                                                     false);
1017             } catch (IllegalArgumentException e) {
1018                 return null;
1019             }
1020
1021             while (readerSpiIter.hasNext()) {
1022                 ImageReaderSpi temp = readerSpiIter.next();
1023                 if (temp.isOwnReader(reader)) {
1024                     readerSpi = temp;
1025                     break;
1026                 }
1027             }
1028             if (readerSpi == null) {
1029                 return null;
1030             }
1031         }
1032
1033         String[] writerNames = readerSpi.getImageWriterSpiNames();
1034         if (writerNames == null) {
1035             return null;
1036         }
1037
1038         Class<?> writerSpiClass = null;
1039         try {
1040             writerSpiClass = Class.forName(writerNames[0], true,
1041                                            ClassLoader.getSystemClassLoader());
1042         } catch (ClassNotFoundException e) {
1043             return null;
1044         }
1045
1046         ImageWriterSpi writerSpi = (ImageWriterSpi)
1047             theRegistry.getServiceProviderByClass(writerSpiClass);
1048         if (writerSpi == null) {
1049             return null;
1050         }
1051
1052         try {
1053             return writerSpi.createWriterInstance();
1054         } catch (IOException e) {
1055             // Deregister the spi in this case, but only as a writerSpi
1056             theRegistry.deregisterServiceProvider(writerSpi,
1057                                                   ImageWriterSpi.class);
1058             return null;
1059         }
1060     }
1061
1062     /**
1063      * Returns an {@code ImageReader} corresponding to the given
1064      * {@code ImageWriter}, if there is one, or {@code null}
1065      * if the plug-in for this {@code ImageWriter} does not
1066      * specify a corresponding {@code ImageReader}, or if the
1067      * given {@code ImageWriter} is not registered.  This method
1068      * is provided principally for symmetry with
1069      * {@code getImageWriter(ImageReader)}.  Note that this
1070      * method returns the "preferred" reader, which is the first in
1071      * the list returned by
1072      * javax.imageio.spi.ImageWriterSpi.{@code getImageReaderSpiNames()}.
1073      *
1074      * @param writer an instance of a registered {@code ImageWriter}.
1075      *
1076      * @return an {@code ImageReader}, or null.
1077      *
1078      * @exception IllegalArgumentException if {@code writer} is
1079      * {@code null}.
1080      *
1081      * @see #getImageWriter(ImageReader)
1082      * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
1083      */

1084     public static ImageReader getImageReader(ImageWriter writer) {
1085         if (writer == null) {
1086             throw new IllegalArgumentException("writer == null!");
1087         }
1088
1089         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1090         if (writerSpi == null) {
1091             Iterator<ImageWriterSpi> writerSpiIter;
1092             // Ensure category is present
1093             try {
1094                 writerSpiIter =
1095                     theRegistry.getServiceProviders(ImageWriterSpi.class,
1096                                                     false);
1097             } catch (IllegalArgumentException e) {
1098                 return null;
1099             }
1100
1101             while (writerSpiIter.hasNext()) {
1102                 ImageWriterSpi temp = writerSpiIter.next();
1103                 if (temp.isOwnWriter(writer)) {
1104                     writerSpi = temp;
1105                     break;
1106                 }
1107             }
1108             if (writerSpi == null) {
1109                 return null;
1110             }
1111         }
1112
1113         String[] readerNames = writerSpi.getImageReaderSpiNames();
1114         if (readerNames == null) {
1115             return null;
1116         }
1117
1118         Class<?> readerSpiClass = null;
1119         try {
1120             readerSpiClass = Class.forName(readerNames[0], true,
1121                                            ClassLoader.getSystemClassLoader());
1122         } catch (ClassNotFoundException e) {
1123             return null;
1124         }
1125
1126         ImageReaderSpi readerSpi = (ImageReaderSpi)
1127             theRegistry.getServiceProviderByClass(readerSpiClass);
1128         if (readerSpi == null) {
1129             return null;
1130         }
1131
1132         try {
1133             return readerSpi.createReaderInstance();
1134         } catch (IOException e) {
1135             // Deregister the spi in this case, but only as a readerSpi
1136             theRegistry.deregisterServiceProvider(readerSpi,
1137                                                   ImageReaderSpi.class);
1138             return null;
1139         }
1140     }
1141
1142     /**
1143      * Returns an {@code Iterator} containing all currently
1144      * registered {@code ImageWriter}s that claim to be able to
1145      * encode images of the given layout (specified using an
1146      * {@code ImageTypeSpecifier}) in the given format.
1147      *
1148      * @param type an {@code ImageTypeSpecifier} indicating the
1149      * layout of the image to be written.
1150      * @param formatName the informal name of the {@code format}.
1151      *
1152      * @return an {@code Iterator} containing {@code ImageWriter}s.
1153      *
1154      * @exception IllegalArgumentException if any parameter is
1155      * {@code null}.
1156      *
1157      * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
1158      */

1159     public static Iterator<ImageWriter>
1160         getImageWriters(ImageTypeSpecifier type, String formatName)
1161     {
1162         if (type == null) {
1163             throw new IllegalArgumentException("type == null!");
1164         }
1165         if (formatName == null) {
1166             throw new IllegalArgumentException("formatName == null!");
1167         }
1168
1169         Iterator<ImageWriterSpi> iter;
1170         // Ensure category is present
1171         try {
1172             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
1173                                  new CanEncodeImageAndFormatFilter(type,
1174                                                                    formatName),
1175                                             true);
1176         } catch (IllegalArgumentException e) {
1177             return Collections.emptyIterator();
1178         }
1179
1180         return new ImageWriterIterator(iter);
1181     }
1182
1183     static class ImageTranscoderIterator
1184         implements Iterator<ImageTranscoder>
1185     {
1186         // Contains ImageTranscoderSpis
1187         public Iterator<ImageTranscoderSpi> iter;
1188
1189         public ImageTranscoderIterator(Iterator<ImageTranscoderSpi> iter) {
1190             this.iter = iter;
1191         }
1192
1193         public boolean hasNext() {
1194             return iter.hasNext();
1195         }
1196
1197         public ImageTranscoder next() {
1198             ImageTranscoderSpi spi = null;
1199             spi = iter.next();
1200             return spi.createTranscoderInstance();
1201         }
1202
1203         public void remove() {
1204             throw new UnsupportedOperationException();
1205         }
1206     }
1207
1208     static class TranscoderFilter
1209         implements ServiceRegistry.Filter {
1210
1211         String readerSpiName;
1212         String writerSpiName;
1213
1214         public TranscoderFilter(ImageReaderSpi readerSpi,
1215                                 ImageWriterSpi writerSpi) {
1216             this.readerSpiName = readerSpi.getClass().getName();
1217             this.writerSpiName = writerSpi.getClass().getName();
1218         }
1219
1220         public boolean filter(Object elt) {
1221             ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;
1222             String readerName = spi.getReaderServiceProviderName();
1223             String writerName = spi.getWriterServiceProviderName();
1224             return (readerName.equals(readerSpiName) &&
1225                     writerName.equals(writerSpiName));
1226         }
1227     }
1228
1229     /**
1230      * Returns an {@code Iterator} containing all currently
1231      * registered {@code ImageTranscoder}s that claim to be
1232      * able to transcode between the metadata of the given
1233      * {@code ImageReader} and {@code ImageWriter}.
1234      *
1235      * @param reader an {@code ImageReader}.
1236      * @param writer an {@code ImageWriter}.
1237      *
1238      * @return an {@code Iterator} containing
1239      * {@code ImageTranscoder}s.
1240      *
1241      * @exception IllegalArgumentException if {@code reader} or
1242      * {@code writer} is {@code null}.
1243      */

1244     public static Iterator<ImageTranscoder>
1245         getImageTranscoders(ImageReader reader, ImageWriter writer)
1246     {
1247         if (reader == null) {
1248             throw new IllegalArgumentException("reader == null!");
1249         }
1250         if (writer == null) {
1251             throw new IllegalArgumentException("writer == null!");
1252         }
1253         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1254         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1255         ServiceRegistry.Filter filter =
1256             new TranscoderFilter(readerSpi, writerSpi);
1257
1258         Iterator<ImageTranscoderSpi> iter;
1259         // Ensure category is present
1260         try {
1261             iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,
1262                                             filter, true);
1263         } catch (IllegalArgumentException e) {
1264             return Collections.emptyIterator();
1265         }
1266         return new ImageTranscoderIterator(iter);
1267     }
1268
1269     // All-in-one methods
1270
1271     /**
1272      * Returns a {@code BufferedImage} as the result of decoding
1273      * a supplied {@code File} with an {@code ImageReader}
1274      * chosen automatically from among those currently registered.
1275      * The {@code File} is wrapped in an
1276      * {@code ImageInputStream}.  If no registered
1277      * {@code ImageReader} claims to be able to read the
1278      * resulting stream, {@code null} is returned.
1279      *
1280      * <p> The current cache settings from {@code getUseCache} and
1281      * {@code getCacheDirectory} will be used to control caching in the
1282      * {@code ImageInputStream} that is created.
1283      *
1284      * <p> Note that there is no {@code read} method that takes a
1285      * filename as a {@code String}; use this method instead after
1286      * creating a {@code File} from the filename.
1287      *
1288      * <p> This method does not attempt to locate
1289      * {@code ImageReader}s that can read directly from a
1290      * {@code File}; that may be accomplished using
1291      * {@code IIORegistry} and {@code ImageReaderSpi}.
1292      *
1293      * @param input a {@code File} to read from.
1294      *
1295      * @return a {@code BufferedImage} containing the decoded
1296      * contents of the input, or {@code null}.
1297      *
1298      * @exception IllegalArgumentException if {@code input} is
1299      * {@code null}.
1300      * @exception IOException if an error occurs during reading or when not
1301      * able to create required ImageInputStream.
1302      */

1303     public static BufferedImage read(File input) throws IOException {
1304         if (input == null) {
1305             throw new IllegalArgumentException("input == null!");
1306         }
1307         if (!input.canRead()) {
1308             throw new IIOException("Can't read input file!");
1309         }
1310
1311         ImageInputStream stream = createImageInputStream(input);
1312         if (stream == null) {
1313             throw new IIOException("Can't create an ImageInputStream!");
1314         }
1315         BufferedImage bi = read(stream);
1316         if (bi == null) {
1317             stream.close();
1318         }
1319         return bi;
1320     }
1321
1322     /**
1323      * Returns a {@code BufferedImage} as the result of decoding
1324      * a supplied {@code InputStream} with an {@code ImageReader}
1325      * chosen automatically from among those currently registered.
1326      * The {@code InputStream} is wrapped in an
1327      * {@code ImageInputStream}.  If no registered
1328      * {@code ImageReader} claims to be able to read the
1329      * resulting stream, {@code null} is returned.
1330      *
1331      * <p> The current cache settings from {@code getUseCache} and
1332      * {@code getCacheDirectory} will be used to control caching in the
1333      * {@code ImageInputStream} that is created.
1334      *
1335      * <p> This method does not attempt to locate
1336      * {@code ImageReader}s that can read directly from an
1337      * {@code InputStream}; that may be accomplished using
1338      * {@code IIORegistry} and {@code ImageReaderSpi}.
1339      *
1340      * <p> This method <em>does not</em> close the provided
1341      * {@code InputStream} after the read operation has completed;
1342      * it is the responsibility of the caller to close the stream, if desired.
1343      *
1344      * @param input an {@code InputStream} to read from.
1345      *
1346      * @return a {@code BufferedImage} containing the decoded
1347      * contents of the input, or {@code null}.
1348      *
1349      * @exception IllegalArgumentException if {@code input} is
1350      * {@code null}.
1351      * @exception IOException if an error occurs during reading or when not
1352      * able to create required ImageInputStream.
1353      */

1354     public static BufferedImage read(InputStream input) throws IOException {
1355         if (input == null) {
1356             throw new IllegalArgumentException("input == null!");
1357         }
1358
1359         ImageInputStream stream = createImageInputStream(input);
1360         if (stream == null) {
1361             throw new IIOException("Can't create an ImageInputStream!");
1362         }
1363         BufferedImage bi = read(stream);
1364         if (bi == null) {
1365             stream.close();
1366         }
1367         return bi;
1368     }
1369
1370     /**
1371      * Returns a {@code BufferedImage} as the result of decoding
1372      * a supplied {@code URL} with an {@code ImageReader}
1373      * chosen automatically from among those currently registered.  An
1374      * {@code InputStream} is obtained from the {@code URL},
1375      * which is wrapped in an {@code ImageInputStream}.  If no
1376      * registered {@code ImageReader} claims to be able to read
1377      * the resulting stream, {@code null} is returned.
1378      *
1379      * <p> The current cache settings from {@code getUseCache} and
1380      * {@code getCacheDirectory} will be used to control caching in the
1381      * {@code ImageInputStream} that is created.
1382      *
1383      * <p> This method does not attempt to locate
1384      * {@code ImageReader}s that can read directly from a
1385      * {@code URL}; that may be accomplished using
1386      * {@code IIORegistry} and {@code ImageReaderSpi}.
1387      *
1388      * @param input a {@code URL} to read from.
1389      *
1390      * @return a {@code BufferedImage} containing the decoded
1391      * contents of the input, or {@code null}.
1392      *
1393      * @exception IllegalArgumentException if {@code input} is
1394      * {@code null}.
1395      * @exception IOException if an error occurs during reading or when not
1396      * able to create required ImageInputStream.
1397      */

1398     public static BufferedImage read(URL input) throws IOException {
1399         if (input == null) {
1400             throw new IllegalArgumentException("input == null!");
1401         }
1402
1403         InputStream istream = null;
1404         try {
1405             istream = input.openStream();
1406         } catch (IOException e) {
1407             throw new IIOException("Can't get input stream from URL!", e);
1408         }
1409         ImageInputStream stream = createImageInputStream(istream);
1410         if (stream == null) {
1411             /* close the istream when stream is null so that if user has
1412              * given filepath as URL he can delete it, otherwise stream will
1413              * be open to that file and he will not be able to delete it.
1414              */

1415             istream.close();
1416             throw new IIOException("Can't create an ImageInputStream!");
1417         }
1418         BufferedImage bi;
1419         try {
1420             bi = read(stream);
1421             if (bi == null) {
1422                 stream.close();
1423             }
1424         } finally {
1425             istream.close();
1426         }
1427         return bi;
1428     }
1429
1430     /**
1431      * Returns a {@code BufferedImage} as the result of decoding
1432      * a supplied {@code ImageInputStream} with an
1433      * {@code ImageReader} chosen automatically from among those
1434      * currently registered.  If no registered
1435      * {@code ImageReader} claims to be able to read the stream,
1436      * {@code null} is returned.
1437      *
1438      * <p> Unlike most other methods in this classthis method <em>does</em>
1439      * close the provided {@code ImageInputStream} after the read
1440      * operation has completed, unless {@code null} is returned,
1441      * in which case this method <em>does not</em> close the stream.
1442      *
1443      * @param stream an {@code ImageInputStream} to read from.
1444      *
1445      * @return a {@code BufferedImage} containing the decoded
1446      * contents of the input, or {@code null}.
1447      *
1448      * @exception IllegalArgumentException if {@code stream} is
1449      * {@code null}.
1450      * @exception IOException if an error occurs during reading.
1451      */

1452     public static BufferedImage read(ImageInputStream stream)
1453         throws IOException {
1454         if (stream == null) {
1455             throw new IllegalArgumentException("stream == null!");
1456         }
1457
1458         Iterator<ImageReader> iter = getImageReaders(stream);
1459         if (!iter.hasNext()) {
1460             return null;
1461         }
1462
1463         ImageReader reader = iter.next();
1464         ImageReadParam param = reader.getDefaultReadParam();
1465         reader.setInput(stream, truetrue);
1466         BufferedImage bi;
1467         try {
1468             bi = reader.read(0, param);
1469         } finally {
1470             reader.dispose();
1471             stream.close();
1472         }
1473         return bi;
1474     }
1475
1476     /**
1477      * Writes an image using the an arbitrary {@code ImageWriter}
1478      * that supports the given format to an
1479      * {@code ImageOutputStream}.  The image is written to the
1480      * {@code ImageOutputStream} starting at the current stream
1481      * pointer, overwriting existing stream data from that point
1482      * forward, if present.
1483      *
1484      * <p> This method <em>does not</em> close the provided
1485      * {@code ImageOutputStream} after the write operation has completed;
1486      * it is the responsibility of the caller to close the stream, if desired.
1487      *
1488      * @param im a {@code RenderedImage} to be written.
1489      * @param formatName a {@code String} containing the informal
1490      * name of the format.
1491      * @param output an {@code ImageOutputStream} to be written to.
1492      *
1493      * @return {@code falseif no appropriate writer is found.
1494      *
1495      * @exception IllegalArgumentException if any parameter is
1496      * {@code null}.
1497      * @exception IOException if an error occurs during writing.
1498      */

1499     public static boolean write(RenderedImage im,
1500                                 String formatName,
1501                                 ImageOutputStream output) throws IOException {
1502         if (im == null) {
1503             throw new IllegalArgumentException("im == null!");
1504         }
1505         if (formatName == null) {
1506             throw new IllegalArgumentException("formatName == null!");
1507         }
1508         if (output == null) {
1509             throw new IllegalArgumentException("output == null!");
1510         }
1511
1512         return doWrite(im, getWriter(im, formatName), output);
1513     }
1514
1515     /**
1516      * Writes an image using an arbitrary {@code ImageWriter}
1517      * that supports the given format to a {@code File}.  If
1518      * there is already a {@code File} present, its contents are
1519      * discarded.
1520      *
1521      * @param im a {@code RenderedImage} to be written.
1522      * @param formatName a {@code String} containing the informal
1523      * name of the format.
1524      * @param output a {@code File} to be written to.
1525      *
1526      * @return {@code falseif no appropriate writer is found.
1527      *
1528      * @exception IllegalArgumentException if any parameter is
1529      * {@code null}.
1530      * @exception IOException if an error occurs during writing or when not
1531      * able to create required ImageOutputStream.
1532      */

1533     public static boolean write(RenderedImage im,
1534                                 String formatName,
1535                                 File output) throws IOException {
1536         if (output == null) {
1537             throw new IllegalArgumentException("output == null!");
1538         }
1539
1540         ImageWriter writer = getWriter(im, formatName);
1541         if (writer == null) {
1542             /* Do not make changes in the file system if we have
1543              * no appropriate writer.
1544              */

1545             return false;
1546         }
1547
1548         output.delete();
1549         ImageOutputStream stream = createImageOutputStream(output);
1550         if (stream == null) {
1551             throw new IIOException("Can't create an ImageOutputStream!");
1552         }
1553         try {
1554             return doWrite(im, writer, stream);
1555         } finally {
1556             stream.close();
1557         }
1558     }
1559
1560     /**
1561      * Writes an image using an arbitrary {@code ImageWriter}
1562      * that supports the given format to an {@code OutputStream}.
1563      *
1564      * <p> This method <em>does not</em> close the provided
1565      * {@code OutputStream} after the write operation has completed;
1566      * it is the responsibility of the caller to close the stream, if desired.
1567      *
1568      * <p> The current cache settings from {@code getUseCache} and
1569      * {@code getCacheDirectory} will be used to control caching.
1570      *
1571      * @param im a {@code RenderedImage} to be written.
1572      * @param formatName a {@code String} containing the informal
1573      * name of the format.
1574      * @param output an {@code OutputStream} to be written to.
1575      *
1576      * @return {@code falseif no appropriate writer is found.
1577      *
1578      * @exception IllegalArgumentException if any parameter is
1579      * {@code null}.
1580      * @exception IOException if an error occurs during writing or when not
1581      * able to create required ImageOutputStream.
1582      */

1583     public static boolean write(RenderedImage im,
1584                                 String formatName,
1585                                 OutputStream output) throws IOException {
1586         if (output == null) {
1587             throw new IllegalArgumentException("output == null!");
1588         }
1589         ImageOutputStream stream = createImageOutputStream(output);
1590         if (stream == null) {
1591             throw new IIOException("Can't create an ImageOutputStream!");
1592         }
1593         try {
1594             return doWrite(im, getWriter(im, formatName), stream);
1595         } finally {
1596             stream.close();
1597         }
1598     }
1599
1600     /**
1601      * Returns {@code ImageWriter} instance according to given
1602      * rendered image and image format or {@code nullif there
1603      * is no appropriate writer.
1604      */

1605     private static ImageWriter getWriter(RenderedImage im,
1606                                          String formatName) {
1607         ImageTypeSpecifier type =
1608             ImageTypeSpecifier.createFromRenderedImage(im);
1609         Iterator<ImageWriter> iter = getImageWriters(type, formatName);
1610
1611         if (iter.hasNext()) {
1612             return iter.next();
1613         } else {
1614             return null;
1615         }
1616     }
1617
1618     /**
1619      * Writes image to output stream  using given image writer.
1620      */

1621     private static boolean doWrite(RenderedImage im, ImageWriter writer,
1622                                  ImageOutputStream output) throws IOException {
1623         if (writer == null) {
1624             return false;
1625         }
1626         writer.setOutput(output);
1627         try {
1628             writer.write(im);
1629         } finally {
1630             writer.dispose();
1631             output.flush();
1632         }
1633         return true;
1634     }
1635 }
1636