001    /*
002     * ImageLoader
003     *
004     * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.codecs;
009    
010    import java.io.File;
011    import java.io.FilenameFilter;
012    import java.io.IOException;
013    import java.util.Vector;
014    import net.sourceforge.jiu.codecs.InvalidFileStructureException;
015    import net.sourceforge.jiu.codecs.InvalidImageIndexException;
016    import net.sourceforge.jiu.codecs.WrongFileFormatException;
017    import net.sourceforge.jiu.codecs.BMPCodec;
018    import net.sourceforge.jiu.codecs.IFFCodec;
019    import net.sourceforge.jiu.codecs.PCDCodec;
020    import net.sourceforge.jiu.codecs.PNGCodec;
021    import net.sourceforge.jiu.codecs.PNMCodec;
022    import net.sourceforge.jiu.codecs.PSDCodec;
023    import net.sourceforge.jiu.codecs.RASCodec;
024    import net.sourceforge.jiu.codecs.tiff.TIFFCodec;
025    import net.sourceforge.jiu.data.PixelImage;
026    import net.sourceforge.jiu.ops.MissingParameterException;
027    import net.sourceforge.jiu.ops.OperationFailedException;
028    
029    /**
030     * A convenience class with static methods to load images from files using JIU codecs.
031     * The load methods of this class try to load an image with all codecs  registered with this class.
032     * This includes almost every codec that resides in the <code>net.sourceforge.jiu.codecs</code> package.
033     * You can register additional codecs with {@link #registerCodecClass} or remove the usage
034     * of codecs with {@link #removeCodecClass}.
035     * <p>
036     * A Codec that cannot safely identify a file to be in the format that it supports must not be used with ImageLoader.
037     * The failure to identify typically comes from the lack of magic byte sequences defined for the format.
038     * In order to load such a file, use the codec manually.
039     * Example: {@link PalmCodec}.
040     * <p>
041     * In order to load an image via {@link java.awt.Toolkit} (JPEG, PNG or GIF), use
042     * {@link net.sourceforge.jiu.gui.awt.ToolkitLoader}.
043     * It combines the loading features of java.awt.Toolkit and JIU's ImageLoader.
044     * <h3>Usage example</h3>
045     * <pre>
046     * PixelImage image = null;
047     * try
048     * {
049     *   image = ImageLoader.load("image.tif");
050     * }
051     * catch (Exception e)
052     * {
053     *   // handle exception
054     * }
055     * </pre>
056     * @author Marco Schmidt
057     */
058    public class ImageLoader
059    {
060            // all elements of class String
061            private static Vector fileExtensions;
062            private static Vector imageCodecClasses;
063    
064            static
065            {
066                    imageCodecClasses = new Vector();
067                    registerCodecClass(new BMPCodec());
068                    registerCodecClass(new IFFCodec());
069                    registerCodecClass(new PCDCodec());
070                    registerCodecClass(new PNGCodec());
071                    registerCodecClass(new PNMCodec());
072                    registerCodecClass(new PSDCodec());
073                    registerCodecClass(new RASCodec());
074                    registerCodecClass(new TIFFCodec());
075            }
076    
077            private ImageLoader()
078            {
079            }
080    
081            /**
082             * Creates an instance of one of the codec classes known to ImageLoader.
083             * @param index 0-based index of codec number, maximum value is {@link #getNumCodecs}<code> - 1</code>
084             * @return new codec object or <code>null</code> if no object could be instantiated
085             */
086            public static ImageCodec createCodec(int index)
087            {
088                    ImageCodec result = null;
089                    if (index >= 0 && index < getNumCodecs())
090                    {
091                            Class c = (Class)imageCodecClasses.elementAt(index);
092                            try
093                            {
094                                    Object obj = c.newInstance();
095                                    if (obj != null && obj instanceof ImageCodec)
096                                    {
097                                            result = (ImageCodec)obj;
098                                    }
099                            }
100                            catch (IllegalAccessException iae)
101                            {
102                                    // ignore
103                            }
104                            catch (InstantiationException ie)
105                            {
106                                    // ignore
107                            }
108                    }
109                    return result;
110            }
111    
112            /**
113             * Returns a filename filter ({@link java.io.FilenameFilter}) that accepts files
114             * with name extensions typical for the image file formats known to ImageLoader.
115             * The filter could then be used in an file dialog like {@link java.awt.FileDialog}.
116             * <p>
117             * Note that this filter does not include file formats supported by the AWT 
118             * {@link java.awt.Toolkit} (GIF and JPEG, also PNG since Java 1.3).
119             * @return filter for image file names
120             */
121            public static FilenameFilter createFilenameFilter()
122            {
123                    return new FilenameFilter()
124                    {
125                            public boolean accept(File dir, String name)
126                            {
127                                    if (name == null)
128                                    {
129                                            return false;
130                                    }
131                                    if (fileExtensions == null)
132                                    {
133                                            updateFileExtensions();
134                                    }
135                                    name = name.toLowerCase();
136                                    int index = 0;
137                                    while (index < fileExtensions.size())
138                                    {
139                                            String ext = (String)fileExtensions.elementAt(index++);
140                                            if (name.endsWith(ext))
141                                            {
142                                                    return true;
143                                            }
144                                    }
145                                    return false;
146                            }
147                    };
148            }
149    
150            /**
151             * Returns the number of codec classes currently known to ImageLoader.
152             * This number can be changed by registering additional codecs
153             * ({@link #registerCodecClass})
154             * or by removing codec classes ({@link #removeCodecClass}).
155             * @return number of known codec classes
156             */
157            public static int getNumCodecs()
158            {
159                    return imageCodecClasses.size();
160            }
161    
162            /**
163             * Attempts to load an image from a file.
164             * @param file the file from which an image is to be loaded
165             * @return the image on success or <code>null</code> on failure
166             */
167            public static PixelImage load(File file) throws
168                    IOException, 
169                    InvalidFileStructureException,
170                    InvalidImageIndexException,
171                    UnsupportedTypeException
172            {
173                    return load(file, null);
174            }
175    
176            /**
177             * Attempts to load an image from a file, notifying the
178             * argument progress listeners.
179             * @param file the file to load an image from
180             * @param listeners a Vector of ProgressListener objects to be notified 
181             * @return an instance of a class implementing {@link PixelImage}
182             */
183            public static PixelImage load(File file, Vector listeners) throws 
184                    IOException, 
185                    InvalidFileStructureException,
186                    InvalidImageIndexException,
187                    UnsupportedTypeException
188            {
189                    for (int i = 0; i < getNumCodecs(); i++)
190                    {
191                            PixelImage result = null;
192                            try
193                            {
194                                    ImageCodec codec = createCodec(i);
195                                    codec.setFile(file, CodecMode.LOAD);
196                                    codec.addProgressListeners(listeners);
197                                    codec.process();
198                                    result = codec.getImage();
199                                    if (result != null)
200                                    {
201                                            return result;
202                                    }
203                            }
204                            catch (MissingParameterException mpe)
205                            {
206                                    // ignore
207                            }
208                            catch (WrongFileFormatException wffe)
209                            {
210                                    // ignore
211                            }
212                            catch (IOException ioe)
213                            {
214                                    // ignore
215                            }
216                            catch (OperationFailedException ofe)
217                            {
218                                    // ignore
219                                    //System.out.println("codec: " + ofe);
220                            }
221                    }
222                    return null;
223            }
224    
225            /**
226             * Load an image from a file given by its name.
227             * Simply calls load(fileName, null).
228             * @param fileName name of the file from which an image is to be loaded
229             * @return the loaded image on success, null on failure
230             */
231            public static PixelImage load(String fileName) throws
232                    IOException,
233                    InvalidFileStructureException,
234                    InvalidImageIndexException,
235                    UnsupportedTypeException
236            {
237                    return load(fileName, null);
238            }
239    
240            /**
241             * Attempts to load an image from the file with the given name, 
242             * using the given list of progress listeners.
243             * @param fileName name of the file from which an image is to be loaded
244             * @param listeners a list of objects implementing ProgressListener
245             * @return the loaded image
246             */
247            public static PixelImage load(String fileName, Vector listeners) throws
248                    IOException,
249                    InvalidFileStructureException,
250                    InvalidImageIndexException,
251                    UnsupportedTypeException
252            {
253                    return load(new File(fileName), listeners);
254            }
255    
256            /**
257             * Registers a codec class with ImageLoader.
258             * The argument is an instance of the class to be registered.
259             * Note that the codec class must have an empty constructor.
260             * <p>
261             * Example: let's say you have written a new codec called ACMEImageCodec.
262             * Your codec supports loading images.
263             * Then you could register it like that:
264             * <pre>
265             * ImageLoader.registerCodecClass(new ACMEImageCodec());
266             * </pre>
267             * <p>
268             * @param codec an instance of the codec class to be registered
269             */
270            public static void registerCodecClass(ImageCodec codec)
271            {
272                    if (codec == null)
273                    {
274                            return;
275                    }
276                    if (imageCodecClasses.contains(codec.getClass()))
277                    {
278                            return;
279                    }
280                    if (!codec.isLoadingSupported())
281                    {
282                            throw new IllegalArgumentException("Codec does not support loading.");
283                    }
284                    imageCodecClasses.addElement(codec.getClass());
285                    updateFileExtensions();
286            }
287    
288            /**
289             * Removes all codec classes from the internal list of codec classes.
290             * After a call to this method, ImageLoader will not load anything unless
291             * new codecs get registered.
292             */
293            public static void removeAllCodecClasses()
294            {
295                    imageCodecClasses = new Vector();
296                    updateFileExtensions();
297            }
298    
299            /**
300             * Removes a codec class from the internal list of codec classes.
301             * @param codec an instance of the codec class to be removed
302             */
303            public static void removeCodecClass(ImageCodec codec)
304            {
305                    if (codec == null)
306                    {
307                            return;
308                    }
309                    int index = imageCodecClasses.indexOf(codec.getClass());
310                    if (index != -1)
311                    {
312                            imageCodecClasses.remove(index);
313                            updateFileExtensions();
314                    }
315                    else
316                    {
317                            throw new IllegalArgumentException("The argument codec's class " +
318                                    "could not be found in the internal list of codec classes.");
319                    }
320            }
321    
322            private static void updateFileExtensions()
323            {
324                    fileExtensions = new Vector();
325                    int index = 0;
326                    while (index < getNumCodecs())
327                    {
328                            try
329                            {
330                                    ImageCodec codec = createCodec(index++);
331                                    String[] extArray = codec.getFileExtensions();
332                                    if (extArray != null && extArray.length > 0)
333                                    {
334                                            for (int i = 0; i < extArray.length; i++)
335                                            {
336                                                    fileExtensions.addElement(extArray[i].toLowerCase());
337                                            }
338                                    }
339                            }
340                            catch (Exception e)
341                            {
342                            }
343                    }
344            }
345    }