KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > imageio > ImageIO


1 /*
2  * @(#)ImageIO.java 1.85 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.imageio;
9
10 import java.awt.image.BufferedImage JavaDoc;
11 import java.awt.image.RenderedImage JavaDoc;
12 import java.io.File JavaDoc;
13 import java.io.InputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.OutputStream JavaDoc;
16 import java.lang.reflect.Method JavaDoc;
17 import java.net.URL JavaDoc;
18 import java.security.AccessController JavaDoc;
19 import java.util.Arrays JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.NoSuchElementException JavaDoc;
24 import java.util.Set JavaDoc;
25 import javax.imageio.spi.IIORegistry JavaDoc;
26 import javax.imageio.spi.ImageReaderSpi JavaDoc;
27 import javax.imageio.spi.ImageWriterSpi JavaDoc;
28 import javax.imageio.spi.ImageInputStreamSpi JavaDoc;
29 import javax.imageio.spi.ImageOutputStreamSpi JavaDoc;
30 import javax.imageio.spi.ImageTranscoderSpi JavaDoc;
31 import javax.imageio.spi.ServiceRegistry JavaDoc;
32 import javax.imageio.stream.ImageInputStream JavaDoc;
33 import javax.imageio.stream.ImageOutputStream JavaDoc;
34 import sun.awt.AppContext;
35 import sun.security.action.GetPropertyAction;
36
37 /**
38  * A class containing static convenience methods for locating
39  * <code>ImageReader</code>s and <code>ImageWriter</code>s, and
40  * performing simple encoding and decoding.
41  *
42  * @version 0.5
43  */

44 public final class ImageIO {
45
46     private static final IIORegistry JavaDoc theRegistry =
47         IIORegistry.getDefaultInstance();
48
49     /**
50      * Constructor is private to prevent instantiation.
51      */

52     private ImageIO() {}
53
54     /**
55      * Scans for plug-ins on the application class path,
56      * loads their service provider classes, and registers a service
57      * provider instance for each one found with the
58      * <code>IIORegistry</code>.
59      *
60      * <p>This method is needed because the application class path can
61      * theoretically change, or additional plug-ins may become available.
62      * Rather than re-scanning the classpath on every invocation of the
63      * API, the class path is scanned automatically only on the first
64      * invocation. Clients can call this method to prompt a re-scan.
65      * Thus this method need only be invoked by sophisticated applications
66      * which dynamically make new plug-ins available at runtime.
67      *
68      * <p> The <code>getResources</code> method of the context
69      * <code>ClassLoader</code> is used locate JAR files containing
70      * files named
71      * <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>,
72      * where <i>classname</i> is one of <code>ImageReaderSpi</code>,
73      * <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>,
74      * <code>ImageInputStreamSpi</code>, or
75      * <code>ImageOutputStreamSpi</code>, along the application class
76      * path.
77      *
78      * <p> The contents of the located files indicate the names of
79      * actual implementation classes which implement the
80      * aforementioned service provider interfaces; the default class
81      * loader is then used to load each of these classes and to
82      * instantiate an instance of each class, which is then placed
83      * into the registry for later retrieval.
84      *
85      * <p> The exact set of locations searched depends on the
86      * implementation of the Java runtime enviroment.
87      *
88      * @see ClassLoader#getResources
89      */

90     public static void scanForPlugins() {
91         theRegistry.registerApplicationClasspathSpis();
92     }
93
94     // ImageInputStreams
95

96     /**
97      * A class to hold information about caching. Each
98      * <code>ThreadGroup</code> will have its own copy
99      * via the <code>AppContext</code> mechanism.
100      */

101     static class CacheInfo {
102         boolean useCache = true;
103         File JavaDoc cacheDirectory = null;
104         Boolean JavaDoc hasPermission = null;
105
106         public CacheInfo() {}
107
108         public boolean getUseCache() {
109             return useCache;
110         }
111
112         public void setUseCache(boolean useCache) {
113             this.useCache = useCache;
114         }
115
116         public File JavaDoc getCacheDirectory() {
117             return cacheDirectory;
118         }
119
120         public void setCacheDirectory(File JavaDoc cacheDirectory) {
121             this.cacheDirectory = cacheDirectory;
122         }
123
124         public Boolean JavaDoc getHasPermission() {
125             return hasPermission;
126         }
127
128         public void setHasPermission(Boolean JavaDoc hasPermission) {
129             this.hasPermission = hasPermission;
130         }
131     }
132
133     /**
134      * Returns the <code>CacheInfo</code> object associated with this
135      * <code>ThreadGroup</code>.
136      */

137     private static synchronized CacheInfo getCacheInfo() {
138         AppContext context = AppContext.getAppContext();
139         CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
140         if (info == null) {
141             info = new CacheInfo();
142             context.put(CacheInfo.class, info);
143         }
144         return info;
145     }
146
147     /**
148      * Returns the default temporary (cache) directory as defined by the
149      * java.io.tmpdir system property.
150      */

151     private static String JavaDoc getTempDir() {
152         GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
153         return (String JavaDoc)AccessController.doPrivileged(a);
154     }
155
156     /**
157      * Determines whether the caller has write access to the cache
158      * directory, stores the result in the <code>CacheInfo</code> object,
159      * and returns the decision. This method helps to prevent mysterious
160      * SecurityExceptions to be thrown when this convenience class is used
161      * in an applet, for example.
162      */

163     private static boolean hasCachePermission() {
164         Boolean JavaDoc hasPermission = getCacheInfo().getHasPermission();
165
166         if (hasPermission != null) {
167             return hasPermission.booleanValue();
168         } else {
169             try {
170                 SecurityManager JavaDoc security = System.getSecurityManager();
171                 if (security != null) {
172                     File JavaDoc cachedir = getCacheDirectory();
173                     String JavaDoc cachepath;
174
175                     if (cachedir != null) {
176                         cachepath = cachedir.getPath();
177                     } else {
178                         cachepath = getTempDir();
179
180                         if (cachepath == null) {
181                             getCacheInfo().setHasPermission(Boolean.FALSE);
182                             return false;
183                         }
184                     }
185
186                     security.checkWrite(cachepath);
187                 }
188             } catch (SecurityException JavaDoc e) {
189                 getCacheInfo().setHasPermission(Boolean.FALSE);
190                 return false;
191             }
192
193             getCacheInfo().setHasPermission(Boolean.TRUE);
194             return true;
195         }
196     }
197
198     /**
199      * Sets a flag indicating whether a disk-based cache file should
200      * be used when creating <code>ImageInputStream</code>s and
201      * <code>ImageOutputStream</code>s.
202      *
203      * <p> When reading from a standard <code>InputStream</code>>, it
204      * may be necessary to save previously read information in a cache
205      * since the underlying stream does not allow data to be re-read.
206      * Similarly, when writing to a standard
207      * <code>OutputStream</code>, a cache may be used to allow a
208      * previously written value to be changed before flushing it to
209      * the final destination.
210      *
211      * <p> The cache may reside in main memory or on disk. Setting
212      * this flag to <code>false</code> disallows the use of disk for
213      * future streams, which may be advantageous when working with
214      * small images, as the overhead of creating and destroying files
215      * is removed.
216      *
217      * <p> On startup, the value is set to <code>true</code>.
218      *
219      * @param useCache a <code>boolean</code> indicating whether a
220      * cache file should be used, in cases where it is optional.
221      *
222      * @see #getUseCache
223      */

224     public static void setUseCache(boolean useCache) {
225         getCacheInfo().setUseCache(useCache);
226     }
227
228     /**
229      * Returns the current value set by <code>setUseCache</code>, or
230      * <code>true</code> if no explicit setting has been made.
231      *
232      * @return true if a disk-based cache may be used for
233      * <code>ImageInputStream</code>s and
234      * <code>ImageOutputStream</code>s.
235      *
236      * @see #setUseCache
237      */

238     public static boolean getUseCache() {
239         return getCacheInfo().getUseCache();
240     }
241
242     /**
243      * Sets the directory where cache files are to be created. A
244      * value of <code>null</code> indicates that the system-dependent
245      * default temporary-file directory is to be used. If
246      * <code>getUseCache</code> returns false, this value is ignored.
247      *
248      * @param cacheDirectory a <code>File</code> specifying a directory.
249      *
250      * @see File#createTempFile(String, String, File)
251      *
252      * @exception SecurityException if the security manager denies
253      * access to the directory.
254      * @exception IllegalArgumentException if <code>cacheDir</code> is
255      * non-<code>null</code> but is not a directory.
256      *
257      * @see #getCacheDirectory
258      */

259     public static void setCacheDirectory(File JavaDoc cacheDirectory) {
260         if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {
261             throw new IllegalArgumentException JavaDoc("Not a directory!");
262         }
263         getCacheInfo().setCacheDirectory(cacheDirectory);
264         getCacheInfo().setHasPermission(null);
265     }
266
267     /**
268      * Returns the current value set by
269      * <code>setCacheDirectory</code>, or <code>null</code> if no
270      * explicit setting has been made.
271      *
272      * @return a <code>File</code> indicating the directory where
273      * cache files will be created, or <code>null</code> to indicate
274      * the system-dependent default temporary-file directory.
275      *
276      * @see #setCacheDirectory
277      */

278     public static File JavaDoc getCacheDirectory() {
279         return getCacheInfo().getCacheDirectory();
280     }
281
282     /**
283      * Returns an <code>ImageInputStream</code> that will take its
284      * input from the given <code>Object</code>. The set of
285      * <code>ImageInputStreamSpi</code>s registered with the
286      * <code>IIORegistry</code> class is queried and the first one
287      * that is able to take input from the supplied object is used to
288      * create the returned <code>ImageInputStream</code>. If no
289      * suitable <code>ImageInputStreamSpi</code> exists,
290      * <code>null</code> is returned.
291      *
292      * <p> The current cache settings from <code>getUseCache</code>and
293      * <code>getCacheDirectory</code> will be used to control caching.
294      *
295      * @param input an <code>Object</code> to be used as an input
296      * source, such as a <code>File</code>, readable
297      * <code>RandomAccessFile</code>, or <code>InputStream</code>.
298      *
299      * @return an <code>ImageInputStream</code>, or <code>null</code>.
300      *
301      * @exception IllegalArgumentException if <code>input</code>
302      * is <code>null</code>.
303      * @exception IOException if a cache file is needed but cannot be
304      * created.
305      *
306      * @see javax.imageio.spi.ImageInputStreamSpi
307      */

308     public static ImageInputStream JavaDoc createImageInputStream(Object JavaDoc input)
309         throws IOException JavaDoc {
310         if (input == null) {
311             throw new IllegalArgumentException JavaDoc("input == null!");
312         }
313
314         Iterator JavaDoc iter;
315         // Ensure category is present
316
try {
317             iter = theRegistry.getServiceProviders(ImageInputStreamSpi JavaDoc.class,
318                                                    true);
319         } catch (IllegalArgumentException JavaDoc e) {
320             return null;
321         }
322
323         boolean usecache = getUseCache() && hasCachePermission();
324
325         while (iter.hasNext()) {
326             ImageInputStreamSpi JavaDoc spi = (ImageInputStreamSpi JavaDoc)iter.next();
327             if (spi.getInputClass().isInstance(input)) {
328                 try {
329                     return spi.createInputStreamInstance(input,
330                                                          usecache,
331                                                          getCacheDirectory());
332                 } catch (IOException JavaDoc e) {
333                     throw new IIOException JavaDoc("Can't create cache file!", e);
334                 }
335             }
336         }
337
338         return null;
339     }
340
341     // ImageOutputStreams
342

343     /**
344      * Returns an <code>ImageOutputStream</code> that will send its
345      * output to the given <code>Object</code>. The set of
346      * <code>ImageOutputStreamSpi</code>s registered with the
347      * <code>IIORegistry</code> class is queried and the first one
348      * that is able to send output from the supplied object is used to
349      * create the returned <code>ImageOutputStream</code>. If no
350      * suitable <code>ImageOutputStreamSpi</code> exists,
351      * <code>null</code> is returned.
352      *
353      * <p> The current cache settings from <code>getUseCache</code>and
354      * <code>getCacheDirectory</code> will be used to control caching.
355      *
356      * @param output an <code>Object</code> to be used as an output
357      * destination, such as a <code>File</code>, writable
358      * <code>RandomAccessFile</code>, or <code>OutputStream</code>.
359      *
360      * @return an <code>ImageOutputStream</code>, or
361      * <code>null</code>.
362      *
363      * @exception IllegalArgumentException if <code>output</code> is
364      * <code>null</code>.
365      * @exception IOException if a cache file is needed but cannot be
366      * created.
367      *
368      * @see javax.imageio.spi.ImageOutputStreamSpi
369      */

370     public static ImageOutputStream JavaDoc createImageOutputStream(Object JavaDoc output)
371         throws IOException JavaDoc {
372         if (output == null) {
373             throw new IllegalArgumentException JavaDoc("output == null!");
374         }
375
376         Iterator JavaDoc iter;
377         // Ensure category is present
378
try {
379             iter = theRegistry.getServiceProviders(ImageOutputStreamSpi JavaDoc.class,
380                                                    true);
381         } catch (IllegalArgumentException JavaDoc e) {
382             return null;
383         }
384
385         boolean usecache = getUseCache() && hasCachePermission();
386
387         while (iter.hasNext()) {
388             ImageOutputStreamSpi JavaDoc spi = (ImageOutputStreamSpi JavaDoc)iter.next();
389             if (spi.getOutputClass().isInstance(output)) {
390                 try {
391                     return spi.createOutputStreamInstance(output,
392                                                           usecache,
393                                                           getCacheDirectory());
394                 } catch (IOException JavaDoc e) {
395                     throw new IIOException JavaDoc("Can't create cache file!", e);
396                 }
397             }
398         }
399
400         return null;
401     }
402
403     // Readers
404

405     private static String JavaDoc[] toStringArray(Set JavaDoc s) {
406         String JavaDoc[] val = new String JavaDoc[s.size()];
407         Iterator JavaDoc iter = s.iterator();
408         int index = 0;
409         while (iter.hasNext()) {
410             val[index++] = (String JavaDoc)iter.next();
411         }
412
413         return val;
414     }
415
416     /**
417      * Returns an array of <code>String</code>s listing all of the
418      * informal format names understood by the current set of registered
419      * readers.
420      *
421      * @return an array of <code>String</code>s.
422      */

423     public static String JavaDoc[] getReaderFormatNames() {
424         Iterator JavaDoc iter;
425         // Ensure category is present
426
try {
427             iter = theRegistry.getServiceProviders(ImageReaderSpi JavaDoc.class, true);
428         } catch (IllegalArgumentException JavaDoc e) {
429             return new String JavaDoc[0];
430         }
431
432         Set JavaDoc s = new HashSet JavaDoc();
433         while (iter.hasNext()) {
434             ImageReaderSpi JavaDoc spi = (ImageReaderSpi JavaDoc)iter.next();
435             String JavaDoc[] names = spi.getFormatNames();
436             for (int i = 0; i < names.length; i++) {
437                 s.add(names[i]);
438             }
439         }
440
441         return toStringArray(s);
442    }
443
444     /**
445      * Returns an array of <code>String</code>s listing all of the
446      * MIME types understood by the current set of registered
447      * readers.
448      *
449      * @return an array of <code>String</code>s.
450      */

451     public static String JavaDoc[] getReaderMIMETypes() {
452         Iterator JavaDoc iter;
453         // Ensure category is present
454
try {
455             iter = theRegistry.getServiceProviders(ImageReaderSpi JavaDoc.class, true);
456         } catch (IllegalArgumentException JavaDoc e) {
457             return new String JavaDoc[0];
458         }
459
460         Set JavaDoc s = new HashSet JavaDoc();
461         while (iter.hasNext()) {
462             ImageReaderSpi JavaDoc spi = (ImageReaderSpi JavaDoc)iter.next();
463             String JavaDoc[] names = spi.getMIMETypes();
464             for (int i = 0; i < names.length; i++) {
465                 s.add(names[i]);
466             }
467         }
468
469         return toStringArray(s);
470     }
471
472     static class ImageReaderIterator implements Iterator JavaDoc<ImageReader JavaDoc> {
473         // Contains ImageReaderSpis
474
public Iterator JavaDoc iter;
475
476         public ImageReaderIterator(Iterator JavaDoc iter) {
477             this.iter = iter;
478         }
479         
480         public boolean hasNext() {
481             return iter.hasNext();
482         }
483
484         public ImageReader JavaDoc next() {
485             ImageReaderSpi JavaDoc spi = null;
486             try {
487                 spi = (ImageReaderSpi JavaDoc)iter.next();
488                 return spi.createReaderInstance();
489             } catch (IOException JavaDoc e) {
490                 // Deregister the spi in this case, but only as
491
// an ImageReaderSpi
492
theRegistry.deregisterServiceProvider(spi, ImageReaderSpi JavaDoc.class);
493             }
494             return null;
495         }
496
497         public void remove() {
498             throw new UnsupportedOperationException JavaDoc();
499         }
500     }
501
502     static class CanDecodeInputFilter
503         implements ServiceRegistry.Filter JavaDoc {
504
505         Object JavaDoc input;
506
507         public CanDecodeInputFilter(Object JavaDoc input) {
508             this.input = input;
509         }
510
511         public boolean filter(Object JavaDoc elt) {
512             try {
513                 ImageReaderSpi JavaDoc spi = (ImageReaderSpi JavaDoc)elt;
514                 ImageInputStream JavaDoc stream = null;
515                 if (input instanceof ImageInputStream JavaDoc) {
516                     stream = (ImageInputStream JavaDoc)input;
517                 }
518                 
519                 // Perform mark/reset as a defensive measure
520
// even though plug-ins are supposed to take
521
// care of it.
522
boolean canDecode = false;
523                 if (stream != null) {
524                     stream.mark();
525                 }
526                 canDecode = spi.canDecodeInput(input);
527                 if (stream != null) {
528                     stream.reset();
529                 }
530                 
531                 return canDecode;
532             } catch (IOException JavaDoc e) {
533                 return false;
534             }
535         }
536     }
537
538     static class CanEncodeImageAndFormatFilter
539         implements ServiceRegistry.Filter JavaDoc {
540
541         ImageTypeSpecifier JavaDoc type;
542         String JavaDoc formatName;
543
544         public CanEncodeImageAndFormatFilter(ImageTypeSpecifier JavaDoc type,
545                                              String JavaDoc formatName) {
546             this.type = type;
547             this.formatName = formatName;
548         }
549
550         public boolean filter(Object JavaDoc elt) {
551             ImageWriterSpi JavaDoc spi = (ImageWriterSpi JavaDoc)elt;
552             return Arrays.asList(spi.getFormatNames()).contains(formatName) &&
553                 spi.canEncodeImage(type);
554         }
555     }
556
557     static class ContainsFilter
558         implements ServiceRegistry.Filter JavaDoc {
559
560         Method JavaDoc method;
561         String JavaDoc name;
562
563         // method returns an array of Strings
564
public ContainsFilter(Method JavaDoc method,
565                               String JavaDoc name) {
566             this.method = method;
567             this.name = name;
568         }
569
570         public boolean filter(Object JavaDoc elt) {
571             try {
572                 return contains((String JavaDoc[])method.invoke(elt, null), name);
573             } catch (Exception JavaDoc e) {
574                 return false;
575             }
576         }
577     }
578
579     /**
580      * Returns an <code>Iterator</code> containing all currently
581      * registered <code>ImageReader</code>s that claim to be able to
582      * decode the supplied <code>Object</code>, typically an
583      * <code>ImageInputStream</code>.
584      *
585      * <p> The stream position is left at its prior position upon
586      * exit from this method.
587      *
588      * @param input an <code>ImageInputStream</code> or other
589      * <code>Object</code> containing encoded image data.
590      *
591      * @return an <code>Iterator</code> containing <code>ImageReader</code>s.
592      *
593      * @exception IllegalArgumentException if <code>input</code> is
594      * <code>null</code>.
595      *
596      * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
597      */

598     public static Iterator JavaDoc<ImageReader JavaDoc> getImageReaders(Object JavaDoc input) {
599         if (input == null) {
600             throw new IllegalArgumentException JavaDoc("input == null!");
601         }
602         Iterator JavaDoc iter;
603         // Ensure category is present
604
try {
605             iter = theRegistry.getServiceProviders(ImageReaderSpi JavaDoc.class,
606                                               new CanDecodeInputFilter(input),
607                                               true);
608         } catch (IllegalArgumentException JavaDoc e) {
609             return new HashSet JavaDoc().iterator();
610         }
611
612         return new ImageReaderIterator(iter);
613     }
614
615     private static Method JavaDoc readerFormatNamesMethod;
616     private static Method JavaDoc readerFileSuffixesMethod;
617     private static Method JavaDoc readerMIMETypesMethod;
618     private static Method JavaDoc writerFormatNamesMethod;
619     private static Method JavaDoc writerFileSuffixesMethod;
620     private static Method JavaDoc writerMIMETypesMethod;
621
622     static {
623         try {
624             readerFormatNamesMethod =
625                 ImageReaderSpi JavaDoc.class.getMethod("getFormatNames", null);
626             readerFileSuffixesMethod =
627                 ImageReaderSpi JavaDoc.class.getMethod("getFileSuffixes", null);
628             readerMIMETypesMethod =
629                 ImageReaderSpi JavaDoc.class.getMethod("getMIMETypes", null);
630             
631             writerFormatNamesMethod =
632                 ImageWriterSpi JavaDoc.class.getMethod("getFormatNames", null);
633             writerFileSuffixesMethod =
634                 ImageWriterSpi JavaDoc.class.getMethod("getFileSuffixes", null);
635             writerMIMETypesMethod =
636                 ImageWriterSpi JavaDoc.class.getMethod("getMIMETypes", null);
637         } catch (NoSuchMethodException JavaDoc e) {
638             e.printStackTrace();
639         }
640     }
641
642     /**
643      * Returns an <code>Iterator</code> containing all currently
644      * registered <code>ImageReader</code>s that claim to be able to
645      * decode the named format.
646      *
647      * @param formatName a <code>String</code> containing the informal
648      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
649      *
650      * @return an <code>Iterator</code> containing
651      * <code>ImageReader</code>s.
652      *
653      * @exception IllegalArgumentException if <code>formatName</code>
654      * is <code>null</code>.
655      *
656      * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
657      */

658     public static Iterator JavaDoc<ImageReader JavaDoc>
659     getImageReadersByFormatName(String JavaDoc formatName)
660     {
661         if (formatName == null) {
662             throw new IllegalArgumentException JavaDoc("formatName == null!");
663         }
664         Iterator JavaDoc iter;
665         // Ensure category is present
666
try {
667             iter = theRegistry.getServiceProviders(ImageReaderSpi JavaDoc.class,
668                                     new ContainsFilter(readerFormatNamesMethod,
669                                                        formatName),
670                                                 true);
671         } catch (IllegalArgumentException JavaDoc e) {
672             return new HashSet JavaDoc().iterator();
673         }
674         return new ImageReaderIterator(iter);
675     }
676
677     /**
678      * Returns an <code>Iterator</code> containing all currently
679      * registered <code>ImageReader</code>s that claim to be able to
680      * decode files with the given suffix.
681      *
682      * @param fileSuffix a <code>String</code> containing a file
683      * suffix (<i>e.g.</i>, "jpg" or "tiff").
684      *
685      * @return an <code>Iterator</code> containing
686      * <code>ImageReader</code>s.
687      *
688      * @exception IllegalArgumentException if <code>fileSuffix</code>
689      * is <code>null</code>.
690      *
691      * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
692      */

693     public static Iterator JavaDoc<ImageReader JavaDoc>
694     getImageReadersBySuffix(String JavaDoc fileSuffix)
695     {
696         if (fileSuffix == null) {
697             throw new IllegalArgumentException JavaDoc("fileSuffix == null!");
698         }
699         // Ensure category is present
700
Iterator JavaDoc iter;
701         try {
702             iter = theRegistry.getServiceProviders(ImageReaderSpi JavaDoc.class,
703                                    new ContainsFilter(readerFileSuffixesMethod,
704                                                       fileSuffix),
705                                               true);
706         } catch (IllegalArgumentException JavaDoc e) {
707             return new HashSet JavaDoc().iterator();
708         }
709         return new ImageReaderIterator(iter);
710     }
711
712     /**
713      * Returns an <code>Iterator</code> containing all currently
714      * registered <code>ImageReader</code>s that claim to be able to
715      * decode files with the given MIME type.
716      *
717      * @param MIMEType a <code>String</code> containing a file
718      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
719      *
720      * @return an <code>Iterator</code> containing
721      * <code>ImageReader</code>s.
722      *
723      * @exception IllegalArgumentException if <code>MIMEType</code> is
724      * <code>null</code>.
725      *
726      * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
727      */

728     public static Iterator JavaDoc<ImageReader JavaDoc>
729     getImageReadersByMIMEType(String JavaDoc MIMEType)
730     {
731         if (MIMEType == null) {
732             throw new IllegalArgumentException JavaDoc("MIMEType == null!");
733         }
734         // Ensure category is present
735
Iterator JavaDoc iter;
736         try {
737             iter = theRegistry.getServiceProviders(ImageReaderSpi JavaDoc.class,
738                                       new ContainsFilter(readerMIMETypesMethod,
739                                                          MIMEType),
740                                               true);
741         } catch (IllegalArgumentException JavaDoc e) {
742             return new HashSet JavaDoc().iterator();
743         }
744         return new ImageReaderIterator(iter);
745     }
746
747     // Writers
748

749     /**
750      * Returns an array of <code>String</code>s listing all of the
751      * informal format names understood by the current set of registered
752      * writers.
753      *
754      * @return an array of <code>String</code>s.
755      */

756     public static String JavaDoc[] getWriterFormatNames() {
757         Iterator JavaDoc iter;
758         // Ensure category is present
759
try {
760             iter = theRegistry.getServiceProviders(ImageWriterSpi JavaDoc.class, true);
761         } catch (IllegalArgumentException JavaDoc e) {
762             re