1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Rustem V. Rafikov
19 * @version $Revision: 1.3 $
20 */
21
22package javax.imageio;
23
24import javax.imageio.stream.ImageInputStream;
25import javax.imageio.stream.ImageOutputStream;
26import javax.imageio.spi.*;
27import java.io.File;
28import java.io.IOException;
29import java.io.InputStream;
30import java.io.OutputStream;
31import java.util.Iterator;
32import java.util.Arrays;
33import java.awt.image.BufferedImage;
34import java.awt.image.RenderedImage;
35import java.net.URL;
36
37/**
38 * The ImageIO class provides static methods to perform reading and writing
39 * operations using registered ImageReader and ImageWriter objects.
40 *
41 * @since Android 1.0
42 */
43public final class ImageIO {
44
45    /**
46     * The constant registry.
47     */
48    private static final IIORegistry registry = IIORegistry.getDefaultInstance();
49
50    /**
51     * Instantiates a new ImageIO.
52     */
53    private ImageIO() {
54    }
55
56    /**
57     * Scans for plug-ins in the class path, loads spi classes, and registers
58     * them with the IIORegistry.
59     */
60    public static void scanForPlugins() {
61        throw new UnsupportedOperationException("Not supported yet");
62    }
63
64    /**
65     * Sets flag which indicates whether a cache file is used when creating
66     * ImageInputStreams and ImageOutputStreams or not.
67     *
68     * @param useCache
69     *            the use cache flag.
70     */
71    public static void setUseCache(boolean useCache) {
72        throw new UnsupportedOperationException("Not supported yet");
73    }
74
75    /**
76     * Gets the flag which indicates whether a cache file is used when creating
77     * ImageInputStreams and ImageOutputStreams or not. This method returns the
78     * current value which is set by setUseCache method.
79     *
80     * @return the use cache flag.
81     */
82    public static boolean getUseCache() {
83        // TODO implement
84        return false;
85    }
86
87    /**
88     * Sets the cache directory.
89     *
90     * @param cacheDirectory
91     *            the File which specifies a cache directory.
92     */
93    public static void setCacheDirectory(File cacheDirectory) {
94        throw new UnsupportedOperationException("Not supported yet");
95    }
96
97    /**
98     * Gets the directory where cache files are created, returned the file which
99     * is set by setCacheDirectory method, or null.
100     *
101     * @return the File object which is set by setCacheDirectory method, or
102     *         null.
103     */
104    public static File getCacheDirectory() {
105        // TODO implement
106        // -- null indicates system-dep default temporary directory
107        return null;
108    }
109
110    /**
111     * Creates an ImageInputStream from the specified Object. The specified
112     * Object should obtain the input source such as File, or InputStream.
113     *
114     * @param input
115     *            the input Object such as File, or InputStream.
116     * @return the ImageInputStream object, or null.
117     * @throws IOException
118     *             if an I/O exception has occurred.
119     */
120    public static ImageInputStream createImageInputStream(Object input) throws IOException {
121
122        if (input == null) {
123            throw new IllegalArgumentException("input source cannot be NULL");
124        }
125
126        Iterator<ImageInputStreamSpi> it = registry.getServiceProviders(ImageInputStreamSpi.class,
127                true);
128
129        while (it.hasNext()) {
130            ImageInputStreamSpi spi = it.next();
131            if (spi.getInputClass().isInstance(input)) {
132                return spi.createInputStreamInstance(input);
133            }
134        }
135        return null;
136    }
137
138    /**
139     * Creates an ImageOutputStream using the specified Object. The specified
140     * Object should obtain the output source such as File, or OutputStream.
141     *
142     * @param output
143     *            the output Object such as File, or OutputStream.
144     * @return the ImageOutputStream object, or null.
145     * @throws IOException
146     *             if an I/O exception has occurred.
147     */
148    public static ImageOutputStream createImageOutputStream(Object output) throws IOException {
149        if (output == null) {
150            throw new IllegalArgumentException("output destination cannot be NULL");
151        }
152
153        Iterator<ImageOutputStreamSpi> it = registry.getServiceProviders(
154                ImageOutputStreamSpi.class, true);
155
156        while (it.hasNext()) {
157            ImageOutputStreamSpi spi = it.next();
158            if (spi.getOutputClass().isInstance(output)) {
159                // todo - use getUseCache and getCacheDir here
160                return spi.createOutputStreamInstance(output);
161            }
162        }
163        return null;
164    }
165
166    /**
167     * Gets the array of format names as String which can be decoded by
168     * registered ImageReader objects.
169     *
170     * @return the array of format names.
171     */
172    public static String[] getReaderFormatNames() {
173        throw new UnsupportedOperationException("Not supported yet");
174    }
175
176    /**
177     * Gets the array of MIME types as String which can be decoded by registered
178     * ImageReader objects.
179     *
180     * @return the array of MIME types.
181     */
182    public static String[] getReaderMIMETypes() {
183        throw new UnsupportedOperationException("Not supported yet");
184    }
185
186    /**
187     * Gets the Iterator of registered ImageReader which are able to decode an
188     * input data specified by input Object.
189     *
190     * @param input
191     *            the input Object with encoded data such as ImageInputStream
192     *            object.
193     * @return the Iterator of registered ImageReader.
194     */
195    public static Iterator<ImageReader> getImageReaders(Object input) {
196        if (input == null) {
197            throw new NullPointerException("input cannot be NULL");
198        }
199
200        Iterator<ImageReaderSpi> it = registry.getServiceProviders(ImageReaderSpi.class,
201                new CanReadFilter(input), true);
202
203        return new SpiIteratorToReadersIteratorWrapper(it);
204    }
205
206    /**
207     * Gets the Iterator of registered ImageReader which are able to decode the
208     * specified format.
209     *
210     * @param formatName
211     *            the format name such as "jpeg", or "gif".
212     * @return the Iterator of registered ImageReader.
213     */
214    public static Iterator<ImageReader> getImageReadersByFormatName(String formatName) {
215        if (formatName == null) {
216            throw new NullPointerException("format name cannot be NULL");
217        }
218
219        Iterator<ImageReaderSpi> it = registry.getServiceProviders(ImageReaderSpi.class,
220                new FormatFilter(formatName), true);
221
222        return new SpiIteratorToReadersIteratorWrapper(it);
223    }
224
225    /**
226     * Gets the Iterator which lists the registered ImageReader objects that are
227     * able to decode files with the specified suffix.
228     *
229     * @param fileSuffix
230     *            the file suffix such as "jpg".
231     * @return the Iterator of registered ImageReaders.
232     */
233    public static Iterator<ImageReader> getImageReadersBySuffix(String fileSuffix) {
234        if (fileSuffix == null) {
235            throw new NullPointerException("suffix cannot be NULL");
236        }
237        Iterator<ImageReaderSpi> it = registry.getServiceProviders(ImageReaderSpi.class,
238                new SuffixFilter(fileSuffix), true);
239
240        return new SpiIteratorToReadersIteratorWrapper(it);
241    }
242
243    /**
244     * Gets the Iterator of registered ImageReader objects that are able to
245     * decode files with the specified MIME type.
246     *
247     * @param MIMEType
248     *            the MIME type such as "image/jpeg".
249     * @return the Iterator of registered ImageReaders.
250     */
251    public static Iterator<ImageReader> getImageReadersByMIMEType(String MIMEType) {
252        throw new UnsupportedOperationException("Not supported yet");
253    }
254
255    /**
256     * Gets an array of Strings giving the names of the formats supported by
257     * registered ImageWriter objects.
258     *
259     * @return the array of format names.
260     */
261    public static String[] getWriterFormatNames() {
262        throw new UnsupportedOperationException("Not supported yet");
263    }
264
265    /**
266     * Gets an array of Strings giving the MIME types of the formats supported
267     * by registered ImageWriter objects.
268     *
269     * @return the array of MIME types.
270     */
271    public static String[] getWriterMIMETypes() {
272        throw new UnsupportedOperationException("Not supported yet");
273    }
274
275    /**
276     * Gets the Iterator which lists the registered ImageReader objects that are
277     * able to encode the specified image format.
278     *
279     * @param formatName
280     *            the image format name such as "jpeg".
281     * @return the Iterator of registered ImageWriter.
282     */
283    public static Iterator<ImageWriter> getImageWritersByFormatName(String formatName) {
284        if (formatName == null) {
285            throw new NullPointerException("format name cannot be NULL");
286        }
287
288        Iterator<ImageWriterSpi> it = registry.getServiceProviders(ImageWriterSpi.class,
289                new FormatFilter(formatName), true);
290
291        return new SpiIteratorToWritersIteratorWrapper(it);
292    }
293
294    /**
295     * Gets the Iterator which lists the registered ImageReader objects that are
296     * able to encode the specified suffix.
297     *
298     * @param fileSuffix
299     *            the file suffix such as "jpg".
300     * @return the Iterator of registered ImageWriter.
301     */
302    public static Iterator<ImageWriter> getImageWritersBySuffix(String fileSuffix) {
303        if (fileSuffix == null) {
304            throw new NullPointerException("suffix cannot be NULL");
305        }
306        Iterator<ImageWriterSpi> it = registry.getServiceProviders(ImageWriterSpi.class,
307                new SuffixFilter(fileSuffix), true);
308        return new SpiIteratorToWritersIteratorWrapper(it);
309    }
310
311    /**
312     * Gets the Iterator which lists the registered ImageReader objects that are
313     * able to encode the specified MIME type.
314     *
315     * @param MIMEType
316     *            the MIME type such as "image/jpeg".
317     * @return the Iterator of registered ImageWriter.
318     */
319    public static Iterator<ImageWriter> getImageWritersByMIMEType(String MIMEType) {
320        throw new UnsupportedOperationException("Not supported yet");
321    }
322
323    /**
324     * Gets an ImageWriter object which corresponds to the specified
325     * ImageReader, or returns null if the specified ImageReader is not
326     * registered.
327     *
328     * @param reader
329     *            the specified ImageReader.
330     * @return the ImageWriter, or null.
331     */
332    public static ImageWriter getImageWriter(ImageReader reader) {
333        throw new UnsupportedOperationException("Not supported yet");
334    }
335
336    /**
337     * Gets an ImageReader object which corresponds to the specified
338     * ImageWriter, or returns null if the specified ImageWriter is not
339     * registered.
340     *
341     * @param writer
342     *            the registered ImageWriter object.
343     * @return the ImageReader.
344     */
345    public static ImageReader getImageReader(ImageWriter writer) {
346        throw new UnsupportedOperationException("Not supported yet");
347    }
348
349    /**
350     * Gets the Iterator of ImageWriter objects which are able to encode images
351     * with the specified ImageTypeSpecifier and format.
352     *
353     * @param type
354     *            the ImageTypeSpecifier, which defines layout.
355     * @param formatName
356     *            the format name.
357     * @return the Iterator of ImageWriter objects.
358     */
359    public static Iterator<ImageWriter> getImageWriters(ImageTypeSpecifier type, String formatName) {
360        if (type == null) {
361            throw new NullPointerException("type cannot be NULL");
362        }
363
364        if (formatName == null) {
365            throw new NullPointerException("format name cannot be NULL");
366        }
367
368        Iterator<ImageWriterSpi> it = registry.getServiceProviders(ImageWriterSpi.class,
369                new FormatAndEncodeFilter(type, formatName), true);
370
371        return new SpiIteratorToWritersIteratorWrapper(it);
372    }
373
374    /**
375     * Gets the Iterator of registered ImageTranscoders which are able to
376     * transcode the metadata of the specified ImageReader object to a suitable
377     * object for encoding by the specified ImageWriter.
378     *
379     * @param reader
380     *            the specified ImageReader.
381     * @param writer
382     *            the specified ImageWriter.
383     * @return the Iterator of registered ImageTranscoders.
384     */
385    public static Iterator<ImageTranscoder> getImageTranscoders(ImageReader reader,
386            ImageWriter writer) {
387        throw new UnsupportedOperationException("Not supported yet");
388    }
389
390    /**
391     * Reads image data from the specified File and decodes it using the
392     * appropriate registered ImageReader object. The File is wrapped in an
393     * ImageInputStream.
394     *
395     * @param input
396     *            the File to be read.
397     * @return the BufferedImage decoded from the specified File, or null.
398     * @throws IOException
399     *             if an I/O exception has occurred.
400     */
401    public static BufferedImage read(File input) throws IOException {
402        if (input == null) {
403            throw new IllegalArgumentException("input == null!");
404        }
405
406        ImageInputStream stream = createImageInputStream(input);
407        return read(stream);
408    }
409
410    /**
411     * Reads image data from the specified InputStream and decodes it using an
412     * appropriate registered an ImageReader object.
413     *
414     * @param input
415     *            the InputStream.
416     * @return the BufferedImage decoded from the specified InputStream, or
417     *         null.
418     * @throws IOException
419     *             if an I/O exception has occurred.
420     */
421    public static BufferedImage read(InputStream input) throws IOException {
422        if (input == null) {
423            throw new IllegalArgumentException("input == null!");
424        }
425
426        ImageInputStream stream = createImageInputStream(input);
427        return read(stream);
428    }
429
430    /**
431     * Reads image data from the specified URL and decodes it using the
432     * appropriate registered ImageReader object.
433     *
434     * @param input
435     *            the URL to be read.
436     * @return the BufferedImage decoded from the specified URL, or null.
437     * @throws IOException
438     *             if an I/O exception has occurred.
439     */
440    public static BufferedImage read(URL input) throws IOException {
441        if (input == null) {
442            throw new IllegalArgumentException("input == null!");
443        }
444
445        InputStream stream = input.openStream();
446        BufferedImage res = read(stream);
447        stream.close();
448
449        return res;
450    }
451
452    /**
453     * Reads image data from the specified ImageInputStream and decodes it using
454     * appropriate registered an ImageReader object.
455     *
456     * @param stream
457     *            the ImageInputStream.
458     * @return the BufferedImage decoded from the specified ImageInputStream, or
459     *         null.
460     * @throws IOException
461     *             if an I/O exception has occurred.
462     */
463    public static BufferedImage read(ImageInputStream stream) throws IOException {
464        if (stream == null) {
465            throw new IllegalArgumentException("stream == null!");
466        }
467
468        Iterator<ImageReader> imageReaders = getImageReaders(stream);
469        if (!imageReaders.hasNext()) {
470            return null;
471        }
472
473        ImageReader reader = imageReaders.next();
474        reader.setInput(stream, false, true);
475        BufferedImage res = reader.read(0);
476        reader.dispose();
477
478        try {
479            stream.close();
480        } catch (IOException e) {
481            // Stream could be already closed, proceed silently in this case
482        }
483
484        return res;
485    }
486
487    /**
488     * Writes the specified image in the specified format (using an appropriate
489     * ImageWriter) to the specified ImageOutputStream.
490     *
491     * @param im
492     *            the RenderedImage.
493     * @param formatName
494     *            the format name.
495     * @param output
496     *            the ImageOutputStream where Image to be written.
497     * @return true, if Image is written successfully, false otherwise.
498     * @throws IOException
499     *             if an I/O exception has occurred.
500     */
501    public static boolean write(RenderedImage im, String formatName, ImageOutputStream output)
502            throws IOException {
503
504        if (im == null) {
505            throw new IllegalArgumentException("image cannot be NULL");
506        }
507        if (formatName == null) {
508            throw new IllegalArgumentException("format name cannot be NULL");
509        }
510        if (output == null) {
511            throw new IllegalArgumentException("output cannot be NULL");
512        }
513
514        Iterator<ImageWriter> it = getImageWriters(ImageTypeSpecifier.createFromRenderedImage(im),
515                formatName);
516        if (it.hasNext()) {
517            ImageWriter writer = it.next();
518            writer.setOutput(output);
519            writer.write(im);
520            output.flush();
521            writer.dispose();
522            return true;
523        }
524        return false;
525    }
526
527    /**
528     * Writes the specified image in the specified format (using an appropriate
529     * ImageWriter) to the specified File.
530     *
531     * @param im
532     *            the RenderedImage.
533     * @param formatName
534     *            the format name.
535     * @param output
536     *            the output File where Image to be written.
537     * @return true, if Image is written successfully, false otherwise.
538     * @throws IOException
539     *             if an I/O exception has occurred.
540     */
541    public static boolean write(RenderedImage im, String formatName, File output)
542            throws IOException {
543
544        if (output == null) {
545            throw new IllegalArgumentException("output cannot be NULL");
546        }
547
548        if (output.exists()) {
549            output.delete();
550        }
551
552        ImageOutputStream ios = createImageOutputStream(output);
553        boolean rt = write(im, formatName, ios);
554        ios.close();
555        return rt;
556    }
557
558    /**
559     * Writes the specified image in the specified format (using an appropriate
560     * ImageWriter) to the specified OutputStream.
561     *
562     * @param im
563     *            the RenderedImage.
564     * @param formatName
565     *            the format name.
566     * @param output
567     *            the OutputStream where Image is to be written.
568     * @return true, if Image is written successfully, false otherwise.
569     * @throws IOException
570     *             if an I/O exception has occurred.
571     */
572    public static boolean write(RenderedImage im, String formatName, OutputStream output)
573            throws IOException {
574
575        if (output == null) {
576            throw new IllegalArgumentException("output cannot be NULL");
577        }
578
579        ImageOutputStream ios = createImageOutputStream(output);
580        boolean rt = write(im, formatName, ios);
581        ios.close();
582        return rt;
583    }
584
585    /**
586     * Filter to match spi by format name.
587     */
588    static class FormatFilter implements ServiceRegistry.Filter {
589
590        /**
591         * The name.
592         */
593        private String name;
594
595        /**
596         * Instantiates a new format filter.
597         *
598         * @param name
599         *            the name.
600         */
601        public FormatFilter(String name) {
602            this.name = name;
603        }
604
605        public boolean filter(Object provider) {
606            ImageReaderWriterSpi spi = (ImageReaderWriterSpi)provider;
607            return Arrays.asList(spi.getFormatNames()).contains(name);
608        }
609    }
610
611    /**
612     * Filter to match spi by format name and encoding possibility.
613     */
614    static class FormatAndEncodeFilter extends FormatFilter {
615
616        /**
617         * The type.
618         */
619        private ImageTypeSpecifier type;
620
621        /**
622         * Instantiates a new format and encode filter.
623         *
624         * @param type
625         *            the type.
626         * @param name
627         *            the name.
628         */
629        public FormatAndEncodeFilter(ImageTypeSpecifier type, String name) {
630            super(name);
631            this.type = type;
632        }
633
634        @Override
635        public boolean filter(Object provider) {
636            ImageWriterSpi spi = (ImageWriterSpi)provider;
637            return super.filter(provider) && spi.canEncodeImage(type);
638        }
639    }
640
641    /**
642     * Filter to match spi by suffix.
643     */
644    static class SuffixFilter implements ServiceRegistry.Filter {
645
646        /**
647         * The suf.
648         */
649        private String suf;
650
651        /**
652         * Instantiates a new suffix filter.
653         *
654         * @param suf
655         *            the suf.
656         */
657        public SuffixFilter(String suf) {
658            this.suf = suf;
659        }
660
661        public boolean filter(Object provider) {
662            ImageReaderWriterSpi spi = (ImageReaderWriterSpi)provider;
663            return Arrays.asList(spi.getFileSuffixes()).contains(suf);
664        }
665    }
666
667    /**
668     * Filter to match spi by decoding possibility.
669     */
670    static class CanReadFilter implements ServiceRegistry.Filter {
671
672        /**
673         * The input.
674         */
675        private Object input;
676
677        /**
678         * Instantiates a new can read filter.
679         *
680         * @param input
681         *            the input.
682         */
683        public CanReadFilter(Object input) {
684            this.input = input;
685        }
686
687        public boolean filter(Object provider) {
688            ImageReaderSpi spi = (ImageReaderSpi)provider;
689            try {
690                return spi.canDecodeInput(input);
691            } catch (IOException e) {
692                return false;
693            }
694        }
695    }
696
697    /**
698     * Wraps Spi's iterator to ImageWriter iterator.
699     */
700    static class SpiIteratorToWritersIteratorWrapper implements Iterator<ImageWriter> {
701
702        /**
703         * The backend.
704         */
705        private Iterator<ImageWriterSpi> backend;
706
707        /**
708         * Instantiates a new spi iterator to writers iterator wrapper.
709         *
710         * @param backend
711         *            the backend.
712         */
713        public SpiIteratorToWritersIteratorWrapper(Iterator<ImageWriterSpi> backend) {
714            this.backend = backend;
715        }
716
717        /**
718         * Next.
719         *
720         * @return the image writer.
721         */
722        public ImageWriter next() {
723            try {
724                return backend.next().createWriterInstance();
725            } catch (IOException e) {
726                e.printStackTrace();
727                return null;
728            }
729        }
730
731        /**
732         * Checks for next.
733         *
734         * @return true, if successful.
735         */
736        public boolean hasNext() {
737            return backend.hasNext();
738        }
739
740        /**
741         * Removes the.
742         */
743        public void remove() {
744            throw new UnsupportedOperationException(
745                    "Use deregisterServiceprovider instead of Iterator.remove()");
746        }
747    }
748
749    /**
750     * Wraps spi's iterator to ImageReader iterator.
751     */
752    static class SpiIteratorToReadersIteratorWrapper implements Iterator<ImageReader> {
753
754        /**
755         * The backend.
756         */
757        private Iterator<ImageReaderSpi> backend;
758
759        /**
760         * Instantiates a new spi iterator to readers iterator wrapper.
761         *
762         * @param backend
763         *            the backend.
764         */
765        public SpiIteratorToReadersIteratorWrapper(Iterator<ImageReaderSpi> backend) {
766            this.backend = backend;
767        }
768
769        /**
770         * Next.
771         *
772         * @return the image reader.
773         */
774        public ImageReader next() {
775            try {
776                return backend.next().createReaderInstance();
777            } catch (IOException e) {
778                e.printStackTrace();
779                return null;
780            }
781        }
782
783        /**
784         * Checks for next.
785         *
786         * @return true, if successful.
787         */
788        public boolean hasNext() {
789            return backend.hasNext();
790        }
791
792        /**
793         * Removes the.
794         */
795        public void remove() {
796            throw new UnsupportedOperationException(
797                    "Use deregisterServiceprovider instead of Iterator.remove()");
798        }
799    }
800}
801