001/* ImageIO.java --
002   Copyright (C) 2004, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.imageio;
040
041import java.awt.image.BufferedImage;
042import java.awt.image.RenderedImage;
043import java.io.File;
044import java.io.FileInputStream;
045import java.io.FileOutputStream;
046import java.io.IOException;
047import java.io.InputStream;
048import java.io.OutputStream;
049import java.net.URL;
050import java.util.ArrayList;
051import java.util.Collections;
052import java.util.Iterator;
053
054import javax.imageio.spi.IIORegistry;
055import javax.imageio.spi.ImageInputStreamSpi;
056import javax.imageio.spi.ImageOutputStreamSpi;
057import javax.imageio.spi.ImageReaderSpi;
058import javax.imageio.spi.ImageTranscoderSpi;
059import javax.imageio.spi.ImageWriterSpi;
060import javax.imageio.spi.ServiceRegistry;
061import javax.imageio.stream.ImageInputStream;
062import javax.imageio.stream.ImageOutputStream;
063import javax.imageio.stream.MemoryCacheImageInputStream;
064import javax.imageio.stream.MemoryCacheImageOutputStream;
065
066/**
067 * An uninstantiable class that provides static methods for locating
068 * and using image readers and writers.
069 */
070public final class ImageIO
071{
072  /**
073   * Construct an ImageIO.  Private since ImageIO is not instantiable.
074   */
075  private ImageIO()
076  {
077  }
078
079  private static final class ReaderFormatFilter implements ServiceRegistry.Filter
080  {
081    private String formatName;
082
083    public ReaderFormatFilter(String formatName)
084    {
085      this.formatName = formatName;
086    }
087
088    public boolean filter (Object provider)
089    {
090      if (provider instanceof ImageReaderSpi)
091        {
092          ImageReaderSpi spi = (ImageReaderSpi) provider;
093          String[] formatNames = spi.getFormatNames();
094
095          for (int i = formatNames.length - 1; i >= 0; --i)
096            if (formatName.equals(formatNames[i]))
097              return true;
098        }
099
100      return false;
101    }
102  }
103
104  private static final class ReaderMIMETypeFilter implements ServiceRegistry.Filter
105  {
106    private String MIMEType;
107
108    public ReaderMIMETypeFilter(String MIMEType)
109    {
110      this.MIMEType = MIMEType;
111    }
112
113    public boolean filter(Object provider)
114    {
115      if (provider instanceof ImageReaderSpi)
116        {
117          ImageReaderSpi spi = (ImageReaderSpi) provider;
118          String[] mimetypes = spi.getMIMETypes();
119
120          for (int i = mimetypes.length - 1; i >= 0; --i)
121            if (MIMEType.equals(mimetypes[i]))
122              return true;
123        }
124
125      return false;
126    }
127  }
128  
129  private static final class ReaderObjectFilter implements ServiceRegistry.Filter
130  {
131    private Object object;
132
133    public ReaderObjectFilter(Object object)
134    {
135      this.object = object;
136    }
137
138    public boolean filter(Object provider)
139    {
140      if (provider instanceof ImageReaderSpi)
141        {
142          ImageReaderSpi spi = (ImageReaderSpi) provider;
143
144          try
145            {
146              if (spi.canDecodeInput(object))
147                return true;
148            }
149          catch (IOException e)
150            {
151              // Return false in this case
152            }
153        }
154      return false;
155    }
156  }
157
158  private static final class ReaderSuffixFilter implements ServiceRegistry.Filter
159  {
160    private String fileSuffix;
161
162    public ReaderSuffixFilter(String fileSuffix)
163    {
164      this.fileSuffix = fileSuffix;
165    }
166
167    public boolean filter(Object provider)
168    {
169      if (provider instanceof ImageReaderSpi)
170        {
171          ImageReaderSpi spi = (ImageReaderSpi) provider;
172          String[] suffixes = spi.getFileSuffixes();
173
174          for (int i = suffixes.length - 1; i >= 0; --i)
175            if (fileSuffix.equals(suffixes[i]))
176              return true;
177        }
178
179      return false;
180    }
181  }
182  
183  private static final class WriterFormatFilter implements ServiceRegistry.Filter
184  {
185    private String formatName;
186
187    public WriterFormatFilter(String formatName)
188    {
189      this.formatName = formatName;
190    }
191
192    public boolean filter(Object provider)
193    {
194      if (provider instanceof ImageWriterSpi)
195        {
196          ImageWriterSpi spi = (ImageWriterSpi) provider;
197          String[] formatNames = spi.getFormatNames();
198          
199          for (int i = formatNames.length - 1; i >= 0; --i)
200            if (formatName.equals(formatNames[i]))
201              return true;
202        }
203
204      return false;
205    }
206  }
207
208  private static final class WriterMIMETypeFilter implements ServiceRegistry.Filter
209  {
210    private String MIMEType;
211
212    public WriterMIMETypeFilter(String MIMEType)
213    {
214      this.MIMEType = MIMEType;
215    }
216
217    public boolean filter(Object provider)
218    {
219      if (provider instanceof ImageWriterSpi)
220        {
221          ImageWriterSpi spi = (ImageWriterSpi) provider;
222          String[] mimetypes = spi.getMIMETypes();
223
224          for (int i = mimetypes.length - 1; i >= 0; --i)
225            if (MIMEType.equals(mimetypes[i]))
226              return true;
227        }
228
229      return false;
230    }
231  }
232  
233  private static final class WriterSuffixFilter implements ServiceRegistry.Filter
234  {
235    private String fileSuffix;
236
237    public WriterSuffixFilter(String fileSuffix)
238    {
239      this.fileSuffix = fileSuffix;
240    }
241
242    public boolean filter(Object provider)
243    {
244      if (provider instanceof ImageWriterSpi)
245        {
246          ImageWriterSpi spi = (ImageWriterSpi) provider;
247          String[] suffixes = spi.getFileSuffixes();
248
249          for (int i = suffixes.length - 1; i >= 0; --i)
250            if (fileSuffix.equals(suffixes[i]))
251              return true;
252        }
253
254      return false;
255    }
256  }
257
258  private static final class WriterObjectFilter implements ServiceRegistry.Filter
259  {
260    private ImageTypeSpecifier type;
261    private String formatName;
262
263    public WriterObjectFilter(ImageTypeSpecifier type,
264                              String formatName)
265    {
266      this.type = type;
267      this.formatName = formatName;
268    }
269
270    public boolean filter(Object provider)
271    {
272      if (provider instanceof ImageWriterSpi)
273        {
274          ImageWriterSpi spi = (ImageWriterSpi) provider;
275
276          if (spi.canEncodeImage(type))
277            {
278              String[] formatNames = spi.getFormatNames();
279              for (int i = formatNames.length - 1; i >= 0; --i)
280                if (formatName.equals(formatNames[i]))
281                  return true;
282            }
283        }
284
285      return false;
286    }
287  }
288
289  private static final class TranscoderFilter implements ServiceRegistry.Filter
290  {
291    private ImageReader reader;
292    private ImageWriter writer;
293
294    public TranscoderFilter(ImageReader reader,
295                            ImageWriter writer)
296    {
297      this.reader = reader;
298      this.writer = writer;
299    }
300
301    public boolean filter(Object provider)
302    {
303      if (provider instanceof ImageTranscoderSpi)
304        {
305          ImageTranscoderSpi spi = (ImageTranscoderSpi) provider;
306
307          if (spi.getReaderServiceProviderName().equals
308              (reader.getOriginatingProvider().getClass().getName())
309              && spi.getWriterServiceProviderName().equals
310              (writer.getOriginatingProvider().getClass().getName()))
311            return true;
312        }
313
314      return false;
315    }
316  }
317
318  private static final class ImageReaderIterator
319    implements Iterator<ImageReader>
320  {
321    Iterator<ImageReaderSpi> it;
322    Object readerExtension;
323    
324    public ImageReaderIterator(Iterator<ImageReaderSpi> it,
325                               Object readerExtension)
326    {
327      this.it = it;
328      this.readerExtension = readerExtension;
329    }
330    
331    public ImageReaderIterator(Iterator<ImageReaderSpi> it)
332    {
333      this.it = it;
334    }
335
336    public boolean hasNext()
337    {
338      return it.hasNext();
339    }
340
341    public ImageReader next()
342    {
343      try
344        {
345          ImageReaderSpi spi = it.next();
346          return (readerExtension == null
347              ? spi.createReaderInstance()
348              : spi.createReaderInstance(readerExtension));
349        }
350      catch (IOException e)
351        {
352          return null;
353        }
354    }
355
356    public void remove()
357    {
358      throw new UnsupportedOperationException();
359    }
360  }
361
362  private static final class ImageWriterIterator
363    implements Iterator<ImageWriter>
364  {
365    Iterator<ImageWriterSpi> it;
366    Object writerExtension;
367    
368    public ImageWriterIterator(Iterator<ImageWriterSpi> it,
369                               Object writerExtension)
370    {
371      this.it = it;
372      this.writerExtension = writerExtension;
373    }
374    
375    public ImageWriterIterator(Iterator<ImageWriterSpi> it)
376    {
377      this.it = it;
378    }
379
380    public boolean hasNext()
381    {
382      return it.hasNext();
383    }
384
385    public ImageWriter next()
386    {
387      try
388        {
389          ImageWriterSpi spi = it.next();
390          return (writerExtension == null
391              ? spi.createWriterInstance()
392              : spi.createWriterInstance(writerExtension));
393        }
394      catch (IOException e)
395        {
396          return null;
397        }
398    }
399
400    public void remove()
401    {
402      throw new UnsupportedOperationException();
403    }
404  }
405  
406  private static File cacheDirectory;
407  private static boolean useCache = true;
408
409  private static Iterator<ImageReader> getReadersByFilter(Class<ImageReaderSpi> type,
410                                                          ServiceRegistry.Filter filter,
411                                                          Object readerExtension)
412  {
413    try
414      {
415        Iterator<ImageReaderSpi> it
416          = getRegistry().getServiceProviders(type, filter, true);
417        return new ImageReaderIterator(it, readerExtension);
418      }
419    catch (IllegalArgumentException e)
420      {
421        return Collections.EMPTY_SET.iterator();
422      }
423  }
424  
425  private static Iterator<ImageWriter> getWritersByFilter(Class<ImageWriterSpi> type,
426                                                          ServiceRegistry.Filter filter,
427                                                          Object writerExtension)
428  {
429    try
430      {
431        Iterator<ImageWriterSpi> it
432          = getRegistry().getServiceProviders(type, filter, true);
433        return new ImageWriterIterator(it, writerExtension);
434      }
435    catch (IllegalArgumentException e)
436      {
437        return Collections.EMPTY_SET.iterator();
438      }
439  }
440
441  /**
442   * Retrieve the current cache directory.
443   *
444   * @return the current cache directory or null if none is set.
445   */
446  public static File getCacheDirectory()
447  {
448    return cacheDirectory;
449  }
450
451  /**
452   * Retrieve an iterator over all registered readers for the given
453   * format.
454   *
455   * @param formatName an infomal format name (e.g. "jpeg" or "bmp")
456   *
457   * @return an iterator over a collection of image readers
458   *
459   * @exception IllegalArgumentException if formatName is null
460   */
461  public static Iterator<ImageReader> getImageReadersByFormatName(String formatName)
462  {
463    if (formatName == null)
464      throw new IllegalArgumentException("formatName may not be null");
465
466    return getReadersByFilter(ImageReaderSpi.class,
467                              new ReaderFormatFilter(formatName),
468                              formatName);
469  }
470
471  /**
472   * Retrieve an iterator over all registered readers for the given
473   * MIME type.
474   *
475   * @param MIMEType a MIME specification for an image type
476   * (e.g. "image/jpeg" or "image/x-bmp")
477   *
478   * @return an iterator over a collection of image readers
479   *
480   * @exception IllegalArgumentException if MIMEType is null
481   */
482  public static Iterator<ImageReader> getImageReadersByMIMEType(String MIMEType)
483  {
484    if (MIMEType == null)
485      throw new IllegalArgumentException("MIMEType may not be null");
486
487    return getReadersByFilter(ImageReaderSpi.class,
488                              new ReaderMIMETypeFilter(MIMEType),
489                              MIMEType);
490  }
491
492  /**
493   * Retrieve an iterator over all registered readers for the given
494   * file suffix.
495   *
496   * @param fileSuffix an image file suffix (e.g. "jpg" or "bmp")
497   *
498   * @return an iterator over a collection of image readers
499   *
500   * @exception IllegalArgumentException if fileSuffix is null
501   */
502  public static Iterator<ImageReader> getImageReadersBySuffix(String fileSuffix)
503  {
504    if (fileSuffix == null)
505      throw new IllegalArgumentException("formatName may not be null");
506    
507    return getReadersByFilter(ImageReaderSpi.class,
508                              new ReaderSuffixFilter(fileSuffix),
509                              fileSuffix);
510  }
511
512  /**
513   * Retrieve an iterator over all registered writers for the given
514   * format.
515   *
516   * @param formatName an infomal format name (e.g. "jpeg" or "bmp")
517   *
518   * @return an iterator over a collection of image writers
519   *
520   * @exception IllegalArgumentException if formatName is null
521   */
522  public static Iterator<ImageWriter> getImageWritersByFormatName(String formatName)
523  {
524    if (formatName == null)
525      throw new IllegalArgumentException("formatName may not be null");
526    
527    return getWritersByFilter(ImageWriterSpi.class,
528                              new WriterFormatFilter(formatName),
529                              formatName);
530  }
531
532  /**
533   * Retrieve an iterator over all registered writers for the given
534   * MIME type.
535   *
536   * @param MIMEType a MIME specification for an image type
537   * (e.g. "image/jpeg" or "image/x-bmp")
538   *
539   * @return an iterator over a collection of image writers
540   *
541   * @exception IllegalArgumentException if MIMEType is null
542   */
543  public static Iterator<ImageWriter> getImageWritersByMIMEType(String MIMEType)
544  {
545    if (MIMEType == null)
546      throw new IllegalArgumentException("MIMEType may not be null");
547    
548    return getWritersByFilter(ImageWriterSpi.class,
549                              new WriterMIMETypeFilter(MIMEType),
550                              MIMEType);
551  }
552
553  /**
554   * Retrieve an iterator over all registered writers for the given
555   * file suffix.
556   *
557   * @param fileSuffix an image file suffix (e.g. "jpg" or "bmp")
558   *
559   * @return an iterator over a collection of image writers
560   *
561   * @exception IllegalArgumentException if fileSuffix is null
562   */
563  public static Iterator<ImageWriter> getImageWritersBySuffix(String fileSuffix)
564  {
565    if (fileSuffix == null)
566      throw new IllegalArgumentException("fileSuffix may not be null");
567    
568    return getWritersByFilter(ImageWriterSpi.class,
569                              new WriterSuffixFilter(fileSuffix),
570                              fileSuffix);
571  }
572
573  /**
574   * Retrieve all the informal format names supported by the
575   * collection of registered image readers.
576   *
577   * @return an array of format names
578   */
579  public static String[] getReaderFormatNames()
580  {
581    try
582      {
583        Iterator it =
584          getRegistry().getServiceProviders(ImageReaderSpi.class, true);
585        ArrayList result = new ArrayList();
586
587        while (it.hasNext())
588          {
589            ImageReaderSpi spi = (ImageReaderSpi) it.next();
590            String[] names = spi.getFormatNames();
591
592            for (int i = names.length - 1; i >= 0; --i)
593              result.add(names[i]);
594          }
595
596        return (String[]) result.toArray(new String[result.size()]);
597      }
598    catch (IllegalArgumentException e)
599      {
600        return new String[0];
601      }
602  }
603
604  /**
605   * Retrieve all the MIME types supported by the collection of
606   * registered image readers.
607   *
608   * @return an array of MIME types
609   */
610  public static String[] getReaderMIMETypes()
611  {
612    try
613      {
614        Iterator it =
615          getRegistry().getServiceProviders(ImageReaderSpi.class, true);
616        ArrayList result = new ArrayList();
617
618        while (it.hasNext())
619          {
620            ImageReaderSpi spi = (ImageReaderSpi) it.next();
621            String[] names = spi.getMIMETypes();
622
623            for (int i = names.length - 1; i >= 0; --i)
624              result.add(names[i]);
625          }
626
627        return (String[]) result.toArray(new String[result.size()]);
628      }
629    catch (IllegalArgumentException e)
630      {
631        return new String[0];
632      }
633  }
634
635  private static IIORegistry getRegistry()
636  {
637    return IIORegistry.getDefaultInstance();
638  }
639
640  /**
641   * Check whether or not an on-disk cache is used for image input and
642   * output streams.
643   *
644   * @return true if an on-disk cache is available, false otherwise
645   */
646  public static boolean getUseCache()
647  {
648    return useCache;
649  }
650
651  /**
652   * Retrieve all the informal format names supported by the
653   * collection of registered image writers.
654   *
655   * @return an array of format names
656   */
657  public static String[] getWriterFormatNames()
658  {
659    try
660      {
661        Iterator it =
662          getRegistry().getServiceProviders(ImageWriterSpi.class, true);
663        ArrayList result = new ArrayList();
664
665        while (it.hasNext())
666          {
667            ImageWriterSpi spi = (ImageWriterSpi) it.next();
668            String[] names = spi.getFormatNames();
669
670            for (int i = names.length - 1; i >= 0; --i)
671              result.add(names[i]);
672          }
673
674        return (String[]) result.toArray(new String[result.size()]);
675      }
676    catch (IllegalArgumentException e)
677      {
678        return new String[0];
679      }
680  }
681
682  /**
683   * Retrieve all the MIME types supported by the collection of
684   * registered image writers.
685   *
686   * @return an array of MIME types
687   */
688  public static String[] getWriterMIMETypes()
689  {
690    try
691      {
692        Iterator it =
693          getRegistry().getServiceProviders(ImageWriterSpi.class, true);
694        ArrayList result = new ArrayList();
695
696        while (it.hasNext())
697          {
698            ImageWriterSpi spi = (ImageWriterSpi) it.next();
699            String[] names = spi.getMIMETypes();
700
701            for (int i = names.length - 1; i >= 0; --i)
702              result.add(names[i]);
703          }
704
705        return (String[]) result.toArray(new String[result.size()]);
706      }
707    catch (IllegalArgumentException e)
708      {
709        return new String[0];
710      }
711  }
712  
713  /**
714   * Rescans the application classpath for ImageIO service providers
715   * and registers them.
716   */
717  public static void scanForPlugins()
718  {
719    IIORegistry.getDefaultInstance().registerApplicationClasspathSpis();
720  }
721
722  /**
723   * Set the directory to be used for caching image data.  A null
724   * argument means to use the default system temporary directory.
725   * This cache directory is only used if getUseCache returns true.
726   *
727   * @param cacheDirectory the directory where image data should be
728   * cached
729   *
730   * @exception IllegalArgumentException if cacheDirectory is not a
731   * directory
732   */
733  public static void setCacheDirectory(File cacheDirectory)
734  {
735    // FIXME: add SecurityManager call
736    if (cacheDirectory != null)
737      {
738        if (!cacheDirectory.isDirectory())
739          throw new IllegalArgumentException("cacheDirectory must be a directory");
740
741        cacheDirectory.canWrite();
742      }
743    
744    ImageIO.cacheDirectory = cacheDirectory;
745  }
746
747  /**
748   * Control whether or not an on-disk cache is used.  This cache is
749   * used to store input or output data from an image data stream when
750   * data in the stream needs to be re-processed.
751   *
752   * If useCache is false the cache will be stored in memory.  Doing
753   * so eliminates file creation and deletion overhead.  The default
754   * is to use an on-disk cache.
755   *
756   * @param useCache true to use an on-disk cache, false otherwise
757   */
758  public static void setUseCache(boolean useCache)
759  {
760    ImageIO.useCache = useCache;
761  }
762
763  /**
764   * Write an image to a file using a registered writer that supports
765   * the given format, overwriting the file if it already exists.
766   *
767   * @param im the image data to write
768   * @param formatName an informal description of the output format
769   * @param output the file to which the image will be written
770   *
771   * @return false if no registered writer supports the given format,
772   * true otherwise
773   *
774   * @exception IllegalArgumentException if any argument is null
775   * @exception IOException if a writing error occurs
776   */
777  public static boolean write(RenderedImage im,
778                              String formatName,
779                              File output)
780    throws IOException
781  {
782    if (im == null || formatName == null || output == null)
783      throw new IllegalArgumentException ("null argument");
784
785    return write(im, formatName, new FileOutputStream(output));
786  }
787
788  /**
789   * Write an image to an output stream using a registered writer that
790   * supports the given format.
791   *
792   * @param im the image data to write
793   * @param formatName an informal description of the output format
794   * @param output the output stream to which the image will be
795   * written
796   *
797   * @return false if no registered writer supports the given format,
798   * true otherwise
799   *
800   * @exception IllegalArgumentException if any argument is null
801   * @exception IOException if a writing error occurs
802   */
803  public static boolean write(RenderedImage im,
804                              String formatName,
805                              OutputStream output)
806    throws IOException
807  {
808    if (im == null || formatName == null || output == null)
809      throw new IllegalArgumentException ("null argument");
810
811    return write(im, formatName, new MemoryCacheImageOutputStream(output));
812  }
813
814  /**
815   * Write an image to an ImageOutputStream using a registered writer
816   * that supports the given format.  Image data is written starting
817   * at the ImageOutputStream's current stream pointer, overwriting
818   * any existing data.
819   *
820   * @param im the image data to write
821   * @param formatName an informal description of the output format
822   * @param output the image output stream to which the image will be
823   * written
824   *
825   * @return false if no registered writer supports the given format,
826   * true otherwise
827   *
828   * @exception IllegalArgumentException if any argument is null
829   * @exception IOException if a writing error occurs
830   */
831  public static boolean write(RenderedImage im,
832                              String formatName,
833                              ImageOutputStream output)
834    throws IOException
835  {
836    if (im == null || formatName == null || output == null)
837      throw new IllegalArgumentException ("null argument");
838
839    Iterator writers = getImageWritersByFormatName(formatName);
840    IIOImage img = new IIOImage(im, null, null);
841    while (writers.hasNext())
842      {
843        ImageWriter w = (ImageWriter) writers.next();
844        try 
845          {
846            w.setOutput(output);
847          }
848        catch (IllegalArgumentException e)
849          {
850            continue;
851          }
852        
853        w.write(null, img, null);
854        w.dispose();
855        output.close();
856        return true;
857      }
858    return false;
859  }
860
861  /**
862   * Create a buffered image from an image input stream.  An image
863   * reader that supports the given image data is automatically
864   * selected from the collection of registered readers.  If no
865   * registered reader can handle the input format, null is returned.
866   *
867   * @param stream the image input stream from which to read image
868   * data
869   *
870   * @return a new buffered image created from the given image data,
871   * or null
872   *
873   * @exception IllegalArgumentException if stream is null
874   * @exception IOException if a reading error occurs
875   */
876  public static BufferedImage read(ImageInputStream stream)
877    throws IOException
878  {
879    if (stream == null)
880      throw new IllegalArgumentException("null argument");
881
882    Iterator providers = getRegistry().getServiceProviders(ImageReaderSpi.class, true);
883    while (providers.hasNext())
884      {
885        ImageReaderSpi spi = (ImageReaderSpi) providers.next();
886        if (spi.canDecodeInput(stream))
887          {
888            ImageReader reader = spi.createReaderInstance();
889            reader.setInput(stream);
890            return reader.read(0, null);
891          }
892      }
893    return null;
894  }
895
896  /**
897   * Create a buffered image from a URL.  An image reader that
898   * supports the given image data is automatically selected from the
899   * collection of registered readers.  If no registered reader can
900   * handle the input format, null is returned.
901   *
902   * The image data will be cached in the current cache directory if
903   * caching is enabled.
904   *
905   * This method does not locate readers that read data directly from
906   * a URL.  To locate such readers manually, use IIORegistry and
907   * ImageReaderSpi.
908   *
909   * @param input the URL from which to retrieve the image file
910   *
911   * @return a new buffered image created from the given image URL, or
912   * null
913   *
914   * @exception IllegalArgumentException if input is null
915   * @exception IOException if a reading error occurs
916   */
917  public static BufferedImage read(URL input)
918    throws IOException
919  {
920    if (input == null)
921      throw new IllegalArgumentException("null argument");
922
923    return read(input.openStream());
924  }
925
926  /**
927   * Create a buffered image from an input stream.  An image reader
928   * that supports the given image data is automatically selected from
929   * the collection of registered readers.  If no registered reader
930   * can handle the input format, null is returned.
931   *
932   * The image data will be cached in the current cache directory if
933   * caching is enabled.
934   *
935   * This method does not locate readers that read data directly from
936   * an input stream.  To locate such readers manually, use
937   * IIORegistry and ImageReaderSpi.
938   *
939   * @param input the input stream from which to read the image data
940   *
941   * @return a new buffered image created from the given input stream,
942   * or null
943   *
944   * @exception IllegalArgumentException if input is null
945   * @exception IOException if a reading error occurs
946   */
947  public static BufferedImage read(InputStream input)
948    throws IOException
949  {
950    if (input == null)
951      throw new IllegalArgumentException("null argument");
952
953    return read(new MemoryCacheImageInputStream(input));
954  }
955
956  /**
957   * Create a buffered image from a file.  An image reader that
958   * supports the given image data is automatically selected from the
959   * collection of registered readers.  If no registered reader can
960   * handle the input format, null is returned.
961   *
962   * The image data will be cached in the current cache directory if
963   * caching is enabled.
964   *
965   * This method does not locate readers that read data directly from
966   * a file.  To locate such readers manually, use IIORegistry and
967   * ImageReaderSpi.
968   *
969   * @param input the file from which to read image data
970   *
971   * @return a new buffered image created from the given image file,
972   * or null
973   *
974   * @exception IllegalArgumentException if input is null
975   * @exception IOException if a reading error occurs
976   */
977  public static BufferedImage read(File input)
978    throws IOException
979  {
980    if (input == null)
981      throw new IllegalArgumentException("null argument");
982
983    return read(new FileInputStream(input));
984  }
985
986  /**
987   * Create an image input stream from the given object.  The
988   * collection of ImageInputStreamSpis registered with the
989   * IIORegistry is searched for an image input stream that can take
990   * input from the given object.  null is returned if no such SPI is
991   * registered.
992   *
993   * The image data will be cached in the current cache directory if
994   * caching is enabled.
995   *
996   * @param input an object from which to read image data
997   *
998   * @return an ImageInputStream that can read data from input, or
999   * null
1000   *
1001   * @exception IllegalArgumentException if input is null
1002   * @exception IOException if caching is required but not enabled
1003   */
1004  public static ImageInputStream createImageInputStream (Object input)
1005    throws IOException
1006  {
1007    if (input == null)
1008      throw new IllegalArgumentException ("null argument");
1009
1010    Iterator spis = getRegistry().getServiceProviders
1011      (ImageInputStreamSpi.class, true);
1012
1013    ImageInputStreamSpi foundSpi = null;
1014
1015    while(spis.hasNext())
1016      {
1017        ImageInputStreamSpi spi = (ImageInputStreamSpi) spis.next();
1018
1019        if (input.getClass().equals(spi.getInputClass()))
1020          {
1021            foundSpi = spi;
1022            break;
1023          }
1024      }
1025
1026    return foundSpi == null ? null :
1027      foundSpi.createInputStreamInstance (input,
1028                                          getUseCache(),
1029                                          getCacheDirectory());
1030  }
1031
1032  /**
1033   * Create an image output stream from the given object.  The
1034   * collection of ImageOutputStreamSpis registered with the
1035   * IIORegistry is searched for an image output stream that can send
1036   * output to the given object.  null is returned if no such SPI is
1037   * registered.
1038   *
1039   * The image data will be cached in the current cache directory if
1040   * caching is enabled.
1041   *
1042   * @param output an object to which to write image data
1043   *
1044   * @return an ImageOutputStream that can send data to output, or
1045   * null
1046   *
1047   * @exception IllegalArgumentException if output is null
1048   * @exception IOException if caching is required but not enabled
1049   */
1050  public static ImageOutputStream createImageOutputStream (Object output)
1051    throws IOException
1052  {
1053    if (output == null)
1054      throw new IllegalArgumentException ("null argument");
1055
1056    Iterator spis = getRegistry().getServiceProviders
1057      (ImageOutputStreamSpi.class, true);
1058
1059    ImageOutputStreamSpi foundSpi = null;
1060
1061    while(spis.hasNext())
1062      {
1063        ImageOutputStreamSpi spi = (ImageOutputStreamSpi) spis.next();
1064
1065        if (output.getClass().equals(spi.getOutputClass()))
1066          {
1067            foundSpi = spi;
1068            break;
1069          }
1070      }
1071
1072    return foundSpi == null ? null :
1073      foundSpi.createOutputStreamInstance (output,
1074                                           getUseCache(),
1075                                           getCacheDirectory());
1076  }
1077
1078  /**
1079   * Retrieve an image reader corresponding to an image writer, or
1080   * null if writer is not registered or if no corresponding reader is
1081   * registered.
1082   *
1083   * @param writer a registered image writer
1084   *
1085   * @return an image reader corresponding to writer, or null
1086   *
1087   * @exception IllegalArgumentException if writer is null
1088   */
1089  public static ImageReader getImageReader (ImageWriter writer)
1090  {
1091    if (writer == null)
1092      throw new IllegalArgumentException ("null argument");
1093
1094    ImageWriterSpi spi = writer.getOriginatingProvider();
1095
1096    String[] readerSpiNames = spi.getImageReaderSpiNames();
1097
1098    ImageReader r = null;
1099
1100    if (readerSpiNames != null)
1101      {
1102        try
1103          {
1104            Class readerClass = Class.forName (readerSpiNames[0]);
1105            r = (ImageReader) readerClass.newInstance ();
1106          }
1107        catch (Exception e)
1108          {
1109            return null;
1110          }
1111      }
1112    return r;
1113  }
1114
1115  /**
1116   * Retrieve an iterator over the collection of registered image
1117   * readers that support reading data from the given object.
1118   *
1119   * @param input the object for which to retrieve image readers
1120   *
1121   * @return an iterator over a collection of image readers
1122   */
1123  public static Iterator<ImageReader> getImageReaders (Object input)
1124  {
1125    if (input == null)
1126      throw new IllegalArgumentException ("null argument");
1127
1128    Iterator<ImageReaderSpi> spiIterator
1129      = getRegistry().getServiceProviders (ImageReaderSpi.class,
1130                                           new ReaderObjectFilter(input),
1131                                           true);
1132    return new ImageReaderIterator(spiIterator);
1133  }
1134
1135  /**
1136   * Retrieve an iterator over the collection of registered image
1137   * writers that support writing images of the given type and in the
1138   * given format.
1139   *
1140   * @param type the output image's colour and sample models
1141   * @param formatName the output image format
1142   *
1143   * @return an iterator over a collection of image writers
1144   */
1145  public static Iterator<ImageWriter> getImageWriters (ImageTypeSpecifier type,
1146                                          String formatName)
1147  {
1148    if (type == null || formatName == null)
1149      throw new IllegalArgumentException ("null argument");
1150
1151    final Iterator<ImageWriterSpi> spiIterator
1152      = getRegistry().getServiceProviders (ImageWriterSpi.class,
1153                                           new WriterObjectFilter(type,
1154                                                                  formatName),
1155                                                                  true);
1156    return new ImageWriterIterator(spiIterator);
1157  }
1158
1159  /**
1160   * Retrieve an image writer corresponding to an image reader, or
1161   * null if reader is not registered or if no corresponding writer is
1162   * registered.  This method is useful for preserving metadata
1163   * without needing to understand its format, since the returned
1164   * writer will be able to write, unchanged, the metadata passed to
1165   * it by the reader.
1166   *
1167   * @param reader a registered image reader
1168   *
1169   * @return an image writer corresponding to reader, or null
1170   *
1171   * @exception IllegalArgumentException if reader is null
1172   */
1173  public static ImageWriter getImageWriter (ImageReader reader)
1174  {
1175    if (reader == null)
1176      throw new IllegalArgumentException ("null argument");
1177
1178    ImageReaderSpi spi = reader.getOriginatingProvider();
1179
1180    String[] writerSpiNames = spi.getImageWriterSpiNames();
1181
1182    ImageWriter w = null;
1183
1184    if (writerSpiNames != null)
1185      {
1186        try
1187          {
1188            Class writerClass = Class.forName (writerSpiNames[0]);
1189            w = (ImageWriter) writerClass.newInstance ();
1190          }
1191        catch (Exception e)
1192          {
1193            return null;
1194          }
1195      }
1196    return w;
1197  }
1198
1199  /**
1200   * Retrieve an iterator over a collection of image transcoders that
1201   * support transcoding from the given image reader's metadata format
1202   * to the given writer's metadata format.
1203   *
1204   * @param reader an image reader
1205   * @param writer an image writer
1206   *
1207   * @return an iterator over a collection of image transcoders
1208   *
1209   * @exception IllegalArgumentException if either reader or writer is
1210   * null
1211   */
1212  public static Iterator<ImageTranscoder> getImageTranscoders (ImageReader reader,
1213                                                               ImageWriter writer)
1214  {
1215    if (reader == null || writer == null)
1216      throw new IllegalArgumentException ("null argument");
1217
1218    final Iterator<ImageTranscoderSpi> spiIterator
1219      = getRegistry().getServiceProviders (ImageTranscoderSpi.class,
1220                                           new TranscoderFilter (reader,
1221                                                                 writer),
1222                                           true);
1223    return new Iterator<ImageTranscoder>()
1224    {
1225      public boolean hasNext()
1226      {
1227        return spiIterator.hasNext();
1228      }
1229      
1230      public ImageTranscoder next()
1231      {
1232        return spiIterator.next().createTranscoderInstance();
1233      }
1234      
1235      public void remove()
1236      {
1237        throw new UnsupportedOperationException();
1238      }
1239    };
1240  }
1241}