001    /*
002     * jiuconvert
003     *
004     * Copyright (c) 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.apps;
009    
010    import java.io.File;
011    import java.io.IOException;
012    import java.util.Hashtable;
013    import java.util.Vector;
014    import net.sourceforge.jiu.codecs.BMPCodec;
015    import net.sourceforge.jiu.codecs.CodecMode;
016    import net.sourceforge.jiu.codecs.ImageCodec;
017    import net.sourceforge.jiu.codecs.ImageLoader;
018    import net.sourceforge.jiu.codecs.InvalidFileStructureException;
019    import net.sourceforge.jiu.codecs.InvalidImageIndexException;
020    import net.sourceforge.jiu.codecs.PalmCodec;
021    import net.sourceforge.jiu.codecs.PNMCodec;
022    import net.sourceforge.jiu.codecs.UnsupportedTypeException;
023    import net.sourceforge.jiu.data.PixelImage;
024    import net.sourceforge.jiu.ops.OperationFailedException;
025    
026    class JiuConvertSettings
027    {
028            static final int VERBOSITY_QUIET = 0;
029            static final int VERBOSITY_NORMAL = 1;
030            static final int VERBOSITY_HIGH = 2;
031            static final int FORMAT_UNKNOWN = -1;
032            static final int FORMAT_BMP = 0;
033            static final int FORMAT_PNM = 1;
034            static final int FORMAT_PALM = 2;
035    
036            Vector inputFileNames = new Vector();
037            File destinationDirectory;
038            int fileFormat;
039            boolean noAwtLoading;
040            boolean overwrite;
041            boolean testOnly;
042            long time1;
043            long time2;
044            int verbosity;
045    }
046    
047    abstract class Switch
048    {
049            void check(JiuConvertSettings settings) { }
050            abstract String getDescription();
051            int getMinParameters() { return 0; }
052            String getParameters() { return ""; }
053            abstract String[] getValues();
054            abstract int init(String[] args, int index, JiuConvertSettings settings);
055            void setDefaults(JiuConvertSettings settings) { }
056    }
057    
058    
059    class BMPSwitch extends Switch
060    {
061            void check(JiuConvertSettings settings)
062            {
063                    if (settings.fileFormat == JiuConvertSettings.FORMAT_UNKNOWN && !settings.testOnly)
064                    {
065                            System.err.println("ERROR: You must either use test mode or provide an output file format switch.");
066                            System.exit(1);
067                    }
068            }
069            String[] getValues() { return new String[] {"-b", "--bmp"}; }
070            String getDescription() { return "write Windows BMP output"; }
071            int init(String[] args, int index, JiuConvertSettings settings)
072            {
073                    settings.fileFormat = JiuConvertSettings.FORMAT_BMP;
074                    return index;
075            }
076            void setDefaults(JiuConvertSettings settings) { settings.fileFormat = JiuConvertSettings.FORMAT_UNKNOWN; }
077    }
078    
079    class DestinationDirectorySwitch extends Switch
080    {
081            int getMinParameters() { return 1; }
082            String getDescription() { return "write output files to directory DIR"; }
083            String getParameters() { return "DIR"; }
084            String[] getValues() { return new String[] {"-d", "--destdir"}; }
085            int init(String[] args, int index, JiuConvertSettings settings)
086            {
087                    String name = args[index++];
088                    File dir = new File(name);
089                    if (!dir.exists())
090                    {
091                            System.err.println("Directory " + name + " does not exist.");
092                            System.exit(1);
093                    }
094                    if (!dir.isDirectory())
095                    {
096                            System.err.println(name + " does not seem to be a directory.");
097                            System.exit(1);
098                    }
099                    settings.destinationDirectory = dir;
100                    return index;
101            }
102            void setDefaults(JiuConvertSettings settings) { settings.destinationDirectory = new File("."); }
103    }
104    
105    class NoAwtLoadingSwitch extends Switch
106    {
107            String[] getValues() { return new String[] {"--noawtloading"}; }
108            String getDescription() { return "never use AWT Toolkit to load images"; }
109            int init(String[] args, int index, JiuConvertSettings settings)
110            {
111                    settings.noAwtLoading = true;
112                    return index;
113            }
114            void setDefaults(JiuConvertSettings settings) { settings.noAwtLoading = false; }
115    }
116    
117    class OverwriteSwitch extends Switch
118    {
119            String[] getValues() { return new String[] {"-o", "--overwrite"}; }
120            String getDescription() { return "overwrite existing files"; }
121            int init(String[] args, int index, JiuConvertSettings settings)
122            {
123                    settings.overwrite = true;
124                    return index;
125            }
126            void setDefaults(JiuConvertSettings settings) { settings.overwrite = false; }
127    }
128    
129    class PalmSwitch extends Switch
130    {
131            String[] getValues() { return new String[] {"-P", "--palm"}; }
132            String getDescription() { return "write Palm native image output"; }
133            int init(String[] args, int index, JiuConvertSettings settings)
134            {
135                    settings.fileFormat = JiuConvertSettings.FORMAT_PALM;
136                    return index;
137            }
138    }
139    
140    class PNMSwitch extends Switch
141    {
142            String[] getValues() { return new String[] {"-p", "--pnm"}; }
143            String getDescription() { return "write Portable Anymap (PNM/PBM/PGM/PPM) output"; }
144            int init(String[] args, int index, JiuConvertSettings settings)
145            {
146                    settings.fileFormat = JiuConvertSettings.FORMAT_PNM;
147                    return index;
148            }
149    }
150    
151    class PrintHelpSwitch extends Switch
152    {
153            static Vector switches;
154            String[] getValues() { return new String[] {"-H", "--help"}; }
155            String getDescription() { return "print help text to stdout and terminate"; }
156            int init(String[] args, int index, JiuConvertSettings settings)
157            {
158                    System.out.println("Usage: java jiuconvert [OPTIONS] [FILEs]");
159                    System.out.println("");
160                    for (int i = 0; i < switches.size(); i++)
161                    {
162                            Switch sw = (Switch)switches.elementAt(i);
163                            System.out.print("\t");
164                            String[] values = sw.getValues();
165                            int chars = 0;
166                            for (int j = 0; j < values.length; j++)
167                            {
168                                    if (j > 0)
169                                    {
170                                            System.out.print(", ");
171                                            chars += 2;
172                                    }
173                                    System.out.print(values[j]);
174                                    chars += values[j].length();
175                            }
176                            String params = sw.getParameters();
177                            System.out.print(" " + params);
178                            chars += params.length() + 1;
179                            while (chars++ < 24)
180                            {
181                                    System.out.print(" ");
182                            }
183                            System.out.println(sw.getDescription());
184                    }
185                    System.exit(0);
186                    return 0; // compiler doesn't know that System.exit(0) prevents execution ever getting here
187            }
188    }
189    
190    class PrintVersionSwitch extends Switch
191    {
192            String[] getValues() { return new String[] {"-V", "--version"}; }
193            String getDescription() { return "print version to stdout and terminate"; }
194            int init(String[] args, int index, JiuConvertSettings settings)
195            {
196                    System.out.println("jiuconvert " + JiuInfo.JIU_VERSION);
197                    System.out.println("Written by Marco Schmidt.");
198                    System.out.println("Copyright (C) 2002 Marco Schmidt.");
199                    System.out.println("This is free software; see the source for copying conditions.  There is NO");
200                    System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
201                    System.exit(0);
202                    return 0; // compiler doesn't know that System.exit(0) prevents execution ever getting here
203            }
204    }
205    
206    class QuietSwitch extends Switch
207    {
208            String[] getValues() { return new String[] {"-q", "--quiet"}; }
209            String getDescription() { return "print only error messages"; }
210            int init(String[] args, int index, JiuConvertSettings settings)
211            {
212                    settings.verbosity = JiuConvertSettings.VERBOSITY_QUIET;
213                    return index;
214            }
215            void setDefaults(JiuConvertSettings settings) { settings.verbosity = JiuConvertSettings.VERBOSITY_NORMAL; }
216    }
217    
218    class TestSwitch extends Switch
219    {
220            String[] getValues() { return new String[] {"-t", "--test"}; }
221            String getDescription() { return "test loading, do not write any output files"; }
222            int init(String[] args, int index, JiuConvertSettings settings)
223            {
224                    settings.testOnly = true;
225                    return index;
226            }
227            void setDefaults(JiuConvertSettings settings) { settings.testOnly = false; }
228    }
229    
230    class VerbositySwitch extends Switch
231    {
232            String[] getValues() { return new String[] {"-v", "--verbose"}; }
233            String getDescription() { return "print extensive information to stdout"; }
234            int init(String[] args, int index, JiuConvertSettings settings)
235            {
236                    settings.verbosity = JiuConvertSettings.VERBOSITY_HIGH;
237                    return index;
238            }
239    }
240    
241    /**
242     * A command line program to convert between file formats.
243     * @author Marco Schmidt
244     * @since 0.10.0
245     */
246    public class jiuconvert
247    {
248            // to prevent instances of this class
249            private jiuconvert()
250            {
251            }
252    
253            private static void exit(JiuConvertSettings settings, int exitCode)
254            {
255                    settings.time2 = System.currentTimeMillis();
256                    println(JiuConvertSettings.VERBOSITY_NORMAL, settings, "Time: " + ((settings.time2 - settings.time1) / 1000L) + " s.");
257                    System.exit(exitCode);
258            }
259    
260            private static JiuConvertSettings initFromArguments(String[] args)
261            {
262                    // create switch objects
263                    Vector switches = new Vector();
264                    // note that the order in which the switches are added is the order in which they are displayed
265                    switches.addElement(new BMPSwitch());
266                    switches.addElement(new PalmSwitch());
267                    switches.addElement(new PNMSwitch());
268                    switches.addElement(new TestSwitch());
269                    switches.addElement(new NoAwtLoadingSwitch());
270                    switches.addElement(new OverwriteSwitch());
271                    switches.addElement(new DestinationDirectorySwitch());
272                    switches.addElement(new QuietSwitch());
273                    switches.addElement(new VerbositySwitch());
274                    switches.addElement(new PrintHelpSwitch());
275                    switches.addElement(new PrintVersionSwitch());
276                    PrintHelpSwitch.switches = switches;
277                    // set defaults
278                    JiuConvertSettings settings = new JiuConvertSettings();
279                    settings.time1 = System.currentTimeMillis();
280                    Hashtable switchHash = new Hashtable();
281                    for (int i = 0; i < switches.size(); i++)
282                    {
283                            Switch sw = (Switch)switches.elementAt(i);
284                            sw.setDefaults(settings);
285                            String[] values = sw.getValues();
286                            int j = 0;
287                            while (j < values.length)
288                            {
289                                    String value = values[j++];
290                                    if (switchHash.get(value) != null)
291                                    {
292                                            System.err.println("FATAL INTERNAL ERROR: Switch " + value + " is used more than once.");
293                                            System.exit(1);
294                                    }
295                                    switchHash.put(value, sw);
296                            }
297                    }
298                    // process arguments
299                    int index = 0;
300                    while (index < args.length)
301                    {
302                            String arg = args[index++];
303                            Switch sw = (Switch)switchHash.get(arg);
304                            if (sw == null)
305                            {
306                                    // maybe a switch that does not exist?
307                                    if (arg.charAt(0) == '-')
308                                    {
309                                            System.err.println("Error: Unknown switch " + arg);
310                                            System.exit(1);
311                                    }
312                                    // there is no switch of that name => must be a file
313                                    File file = new File(arg);
314                                    if (!file.exists() || !file.isFile())
315                                    {
316                                            System.err.println("Error: There is no file \"" + arg + "\".");
317                                            System.exit(1);
318                                    }
319                                    settings.inputFileNames.addElement(arg);
320                            }
321                            else
322                            {
323                                    int minParams = sw.getMinParameters();
324                                    if (index + minParams > args.length)
325                                    {
326                                            System.err.println("Error: switch " + arg + " needs at least " + minParams + " parameter(s).");
327                                            System.exit(1);
328                                    }
329                                    index = sw.init(args, index, settings);
330                            }
331                    }
332                    // now call check() on each switch
333                    for (int i = 0; i < switches.size(); i++)
334                    {
335                            Switch sw = (Switch)switches.elementAt(i);
336                            sw.check(settings);
337                    }
338                    // other checks
339                    if (settings.inputFileNames.size() < 1)
340                    {
341                            System.err.println("Error: You must provide at least one input file name.");
342                            System.exit(1);
343                    }
344                    return settings;
345            }
346    
347            private static void print(int minVerbosityLevel, JiuConvertSettings settings, String message)
348            {
349                    if (settings.verbosity >= minVerbosityLevel)
350                    {
351                            System.out.print(message);
352                    }
353            }
354    
355            private static void println(int minVerbosityLevel, JiuConvertSettings settings, String message)
356            {
357                    if (settings.verbosity >= minVerbosityLevel)
358                    {
359                            System.out.println(message);
360                    }
361            }
362    
363            private static void run(JiuConvertSettings settings, String inputFileName)
364            {
365                    String message = null;
366                    PixelImage image = null;
367                    try
368                    {
369                            image = ImageLoader.load(inputFileName, (Vector)null);
370                    }
371                    catch (InvalidImageIndexException iiie)
372                    {
373                            message = "Failed: " + iiie.toString();
374                    }
375                    catch (InvalidFileStructureException ifse)
376                    {
377                            message = "Failed: " + ifse.toString();
378                    }
379                    catch (UnsupportedTypeException ute)
380                    {
381                            message = "Failed: " + ute.toString();
382                    }
383                    catch (IOException ioe)
384                    {
385                            message = "Failed: " + ioe.toString();
386                    }
387                    if (message == null && image == null)
388                    {
389                            message = "Failed.";
390                    }
391                    if (message != null)
392                    {
393                            println(JiuConvertSettings.VERBOSITY_QUIET, settings, "\"" + inputFileName + "\" " + message);
394                            return;
395                    }
396                    else
397                    {
398                            print(JiuConvertSettings.VERBOSITY_NORMAL, settings, "\"" + inputFileName + "\" ");
399                            print(JiuConvertSettings.VERBOSITY_NORMAL, settings, "Loaded (" + 
400                                    //ImageDescriptionCreator.getDescription(image, Locale.US, state) + 
401                                    ").");
402                    }
403                    if (settings.testOnly)
404                    {
405                            println(JiuConvertSettings.VERBOSITY_NORMAL, settings, "");
406                            return;
407                    }
408                    String fileExtension = null;
409                    String outputFileName = inputFileName;
410                    String sep = System.getProperty("file.separator");
411                    int index = outputFileName.lastIndexOf(sep);
412                    if (index != -1)
413                    {
414                            outputFileName = outputFileName.substring(index + sep.length());
415                    }
416                    index = outputFileName.lastIndexOf(".");
417                    if (index != -1)
418                    {
419                            outputFileName = outputFileName.substring(0, index);
420                    }
421                    ImageCodec codec = null;
422                    switch(settings.fileFormat)
423                    {
424                            case(JiuConvertSettings.FORMAT_BMP):
425                            {
426                                    codec = new BMPCodec();
427                                    break;
428                            }
429                            case(JiuConvertSettings.FORMAT_PALM):
430                            {
431                                    codec = new PalmCodec();
432                                    break;
433                            }
434                            case(JiuConvertSettings.FORMAT_PNM):
435                            {
436                                    codec = new PNMCodec();
437                                    break;
438                            }
439                    }
440                    String ext = codec.suggestFileExtension(image);
441                    if (ext != null)
442                    {
443                            outputFileName += ext;
444                    }
445                    File outputFile = new File(settings.destinationDirectory, outputFileName);
446                    outputFileName = outputFile.getAbsolutePath();
447                    if (outputFile.exists() && !settings.overwrite)
448                    {
449                            println(JiuConvertSettings.VERBOSITY_NORMAL, settings, " File \"" + outputFileName + "\" already exists, skipping.");
450                    }
451                    codec.setImage(image);
452                    try
453                    {
454                            codec.setFile(outputFileName, CodecMode.SAVE);
455                            codec.process();
456                            codec.close();
457                            println(JiuConvertSettings.VERBOSITY_NORMAL, settings, " Wrote \"" + outputFileName + "\".");
458                    }
459                    catch (IOException ioe)
460                    {
461                            println(JiuConvertSettings.VERBOSITY_HIGH, settings, " I/O error writing \"" + outputFileName + "\": " + ioe.toString());
462                    }
463                    catch (OperationFailedException ofe)
464                    {
465                            println(JiuConvertSettings.VERBOSITY_HIGH, settings, " Error writing \"" + outputFileName + "\": " + ofe.toString());
466                    }
467            }
468    
469            private static void run(JiuConvertSettings settings)
470            {
471                    int index = 0;
472                    while (index < settings.inputFileNames.size())
473                    {
474                            String fileName = (String)settings.inputFileNames.elementAt(index++);
475                            run(settings, fileName);
476                    }
477                    exit(settings, 0);
478            }
479    
480            public static void main(String[] args)
481            {
482                    JiuConvertSettings settings = initFromArguments(args);
483                    run(settings);
484            }
485    }