1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jdo.enhancer;
18
19 import java.io.IOException;
20 import java.io.FileNotFoundException;
21 import java.io.File;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.BufferedInputStream;
29 import java.io.BufferedOutputStream;
30 import java.io.PrintWriter;
31 import java.io.FileReader;
32 import java.io.BufferedReader;
33
34 import java.util.Map;
35 import java.util.List;
36 import java.util.Collection;
37 import java.util.Enumeration;
38 import java.util.Iterator;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.Hashtable;
42 import java.util.Properties;
43 import java.util.StringTokenizer;
44
45 import java.util.zip.ZipFile;
46 import java.util.zip.ZipEntry;
47 import java.util.zip.ZipInputStream;
48 import java.util.zip.ZipOutputStream;
49 import java.util.zip.ZipException;
50
51 import java.net.URL;
52
53 import org.apache.jdo.impl.enhancer.ClassFileEnhancer;
54 import org.apache.jdo.impl.enhancer.ClassFileEnhancerHelper;
55 import org.apache.jdo.impl.enhancer.ClassFileEnhancerTimer;
56 import org.apache.jdo.impl.enhancer.EnhancerFatalError;
57 import org.apache.jdo.impl.enhancer.EnhancerUserException;
58 import org.apache.jdo.impl.enhancer.OutputStreamWrapper;
59 import org.apache.jdo.impl.enhancer.core.EnhancerFilter;
60 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
61 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError;
62 import org.apache.jdo.impl.enhancer.meta.model.EnhancerMetaDataJDOModelImpl;
63 import org.apache.jdo.impl.enhancer.meta.prop.EnhancerMetaDataPropertyImpl;
64 import org.apache.jdo.impl.enhancer.meta.util.EnhancerMetaDataBaseModel;
65 import org.apache.jdo.impl.enhancer.meta.util.EnhancerMetaDataTimer;
66 import org.apache.jdo.impl.enhancer.util.CombinedResourceLocator;
67 import org.apache.jdo.impl.enhancer.util.ListResourceLocator;
68 import org.apache.jdo.impl.enhancer.util.PathResourceLocator;
69 import org.apache.jdo.impl.enhancer.util.ResourceLocator;
70 import org.apache.jdo.impl.enhancer.util.ResourceLocatorTimer;
71 import org.apache.jdo.impl.enhancer.util.Support;
72
73
74
75
76
77 /***
78 * Main is the starting point for the persistent filter tool.
79 */
80 public class Main
81 extends Support
82 {
83
84 static public final int OK = 0;
85 static public final int USAGE_ERROR = -1;
86 static public final int METADATA_ERROR = -2;
87 static public final int CLASS_LOCATOR_ERROR = -3;
88 static public final int INTERNAL_ERROR = -4;
89
90 /***
91 * The stream to write messages to.
92 */
93 private final PrintWriter out = new PrintWriter(System.out, true);
94
95 /***
96 * The stream to write error messages to.
97 */
98 private final PrintWriter err = new PrintWriter(System.err, true);
99
100 /***
101 * The command line options.
102 */
103 private final CmdLineOptions opts = new CmdLineOptions();
104
105 /***
106 * The byte code enhancer.
107 */
108 private ClassFileEnhancer enhancer;
109
110 /***
111 * The locator for classes.
112 */
113 protected ResourceLocator classLocator;
114
115 /***
116 * The metadata for the enhancer.
117 */
118 private EnhancerMetaData jdoMetaData;
119
120 /***
121 * Construct a filter tool instance
122 */
123 public Main()
124 {}
125
126
127
128 /***
129 * This is where it all starts.
130 */
131 public static void main(String[] argv)
132 {
133 int res;
134 final Main main = new Main();
135
136
137 try {
138 res = main.process(argv);
139 } catch (RuntimeException ex) {
140 main.out.flush();
141 main.err.println("Internal error while postprocessing: "
142 + ex.getMessage());
143 ex.printStackTrace(main.err);
144 main.err.flush();
145 res = INTERNAL_ERROR;
146 } finally {
147
148 if (main.opts.doTiming) {
149 Support.timer.print();
150 }
151 }
152 System.exit(res);
153 }
154
155 /***
156 * Process command line options and run enhancer.
157 */
158 public int process(String[] argv)
159 {
160 int res;
161
162 if ((res = opts.processArgs(argv)) != OK) {
163 printMessage("aborted with errors.");
164 return res;
165 }
166
167
168 try {
169 if (opts.doTiming) {
170 timer.push("Main.process(String[])");
171 }
172
173 if ((res = createEnhancer()) != OK) {
174 printMessage("aborted with errors.");
175 return res;
176 }
177
178 if ((res = enhanceInputFiles(opts.classNames,
179 opts.classFileNames,
180 opts.zipFileNames,
181 opts.jdoFileNames)) != OK) {
182 printMessage("aborted with errors.");
183 return res;
184 }
185
186 printMessage("done.");
187 return 0;
188 } finally {
189 if (opts.doTiming) {
190 timer.pop();
191 }
192 }
193 }
194
195
196
197 /***
198 * A class for holding the command line options.
199 */
200 private class CmdLineOptions
201 {
202 final List classNames = new ArrayList();
203 final List classFileNames = new ArrayList();
204 final List zipFileNames = new ArrayList();
205 final List jdoFileNames = new ArrayList();
206 String sourcePath = null;
207 String destinationDirectory = null;
208 String propertiesFileName = null;
209 boolean doTiming = false;
210 boolean verbose = false;
211 boolean quiet = false;
212 boolean forceWrite = false;
213 boolean noWrite = false;
214 boolean dumpClass = false;
215 boolean noAugment = false;
216 boolean noAnnotate = false;
217
218 /***
219 * Print a usage message to System.err.
220 */
221 public void usage() {
222 err.println("Usage: main <options> <arguments>...");
223 err.println("Options:");
224 err.println(" -h, --help print usage message and exit gently");
225 err.println(" -v, --verbose print verbose messages");
226 err.println(" -q, --quiet supress warnings");
227 err.println(" -s, --sourcepath <path> source path for jdo and classfiles");
228 err.println(" -d, --destdir <dir> destination directory for output files");
229 err.println(" -f, --force overwrite output files");
230 err.println(" -n, --nowrite never write output files");
231 err.println(" -t, --timing do timing messures");
232 err.println();
233 err.println("Debugging Options:");
234 err.println(" --properties <file> use property file for meta data");
235 err.println(" --dumpclass print out disassembled code of classes");
236 err.println(" --noaugment do not enhance for persistence-capability");
237 err.println(" --noannotate do not enhance for persistence-awareness");
238 err.println();
239 err.println("Arguments:");
240
241 err.println(" <jdofile> the name of a .jdo file");
242 err.println(" <classfile> the name of a .class file");
243
244 err.println();
245 err.println("Returns a non-zero value in case of errors.");
246 }
247
248 /***
249 * Process command line options.
250 */
251 protected int processArgs(String[] argv)
252 {
253 final Collection inputNames = new ArrayList();
254 for (int i = 0; i < argv.length; i++) {
255 final String arg = argv[i];
256 if (arg.equals("-h")
257 || arg.equals("--help")) {
258 usage();
259 return OK;
260 }
261 if (arg.equals("-v")
262 || arg.equals("--verbose")) {
263 verbose = true;
264 quiet = false;
265 continue;
266 }
267 if (arg.equals("-q")
268 || arg.equals("--quiet")) {
269 quiet = true;
270 verbose = false;
271 continue;
272 }
273 if (arg.equals("-t") ||
274 arg.equals("--timing")) {
275 doTiming = true;
276 continue;
277 }
278 if (arg.equals("-f")
279 || arg.equals("--force")) {
280 forceWrite = true;
281 noWrite = false;
282 continue;
283 }
284 if (arg.equals("-n")
285 || arg.equals("--nowrite")) {
286 noWrite = true;
287 forceWrite = false;
288 continue;
289 }
290 if (arg.equals("--dumpclass")) {
291 dumpClass = true;
292 continue;
293 }
294 if (arg.equals("--noaugment")) {
295 noAugment = true;
296 continue;
297 }
298 if (arg.equals("--noannotate")) {
299 noAnnotate = true;
300 continue;
301 }
302 if (arg.equals("-s")
303 || arg.equals("--sourcepath")) {
304 if (argv.length - i < 2) {
305 printError("Missing argument to the -s/--sourcepath option", null);
306 usage();
307 return USAGE_ERROR;
308 }
309 sourcePath = argv[++i];
310 continue;
311 }
312 if (arg.equals("-d")
313 || arg.equals("--destdir")) {
314 if (argv.length - i < 2) {
315 printError("Missing argument to the -d/-destdir option", null);
316 usage();
317 return USAGE_ERROR;
318 }
319 destinationDirectory = argv[++i];
320 continue;
321 }
322 if (arg.equals("--properties")) {
323 if (argv.length - i < 2) {
324 printError("Missing argument to the --properties option", null);
325 usage();
326 return USAGE_ERROR;
327 }
328 propertiesFileName = argv[++i];
329 continue;
330 }
331 if (arg.length() > 0 && arg.charAt(0) == '-') {
332 printError("Unrecognized option:" + arg, null);
333 usage();
334 return USAGE_ERROR;
335 }
336 if (arg.length() == 0) {
337 printMessage("Ignoring empty command line argument.");
338 continue;
339 }
340
341 inputNames.add(arg);
342 }
343
344
345 for (Iterator names = inputNames.iterator(); names.hasNext();) {
346 final String name = (String)names.next();
347 if (isJdoFileName(name)) {
348 jdoFileNames.add(name);
349 } else if (isClassFileName(name)) {
350 classFileNames.add(name);
351 } else if (isZipFileName(name)) {
352 zipFileNames.add(name);
353 } else {
354 classNames.add(name);
355 }
356 }
357
358 if (verbose) {
359 printArgs();
360 }
361 return checkArgs();
362 }
363
364 /***
365 * Check command line options.
366 */
367 protected int checkArgs()
368 {
369
370 if (classNames.isEmpty()
371 && classFileNames.isEmpty()
372 && zipFileNames.isEmpty()) {
373 final String msg
374 = "No classes specified";
375 printError(msg, null);
376 usage();
377 return USAGE_ERROR;
378 }
379
380
381 if (classFileNames.size() > 0
382 && (jdoFileNames.isEmpty()
383 && propertiesFileName == null
384 && sourcePath == null)) {
385 final String msg
386 = "No JDO meta-data source specified for class files";
387 printError(msg, null);
388 usage();
389 return USAGE_ERROR;
390 }
391
392
393 if (!jdoFileNames.isEmpty() && propertiesFileName != null) {
394 final String msg
395 = "Cannot have both jdo files and properties specified";
396 printError(msg, null);
397 usage();
398 return USAGE_ERROR;
399 }
400
401 return OK;
402 }
403
404 /***
405 * Print command line options.
406 */
407 protected void printArgs()
408 {
409 out.println("Enhancer: options:");
410 out.println(" verbose = " + verbose);
411 out.println(" quiet = " + quiet);
412 out.println(" forceWrite = " + forceWrite);
413 out.println(" noWrite = " + noWrite);
414 out.println(" sourcePath = " + sourcePath);
415 out.println(" destinationDirectory = " + destinationDirectory);
416 out.println(" propertiesFileName = " + propertiesFileName);
417 out.println(" doTiming = " + doTiming);
418 out.println(" classNames = {");
419 for (Iterator i = classNames.iterator(); i.hasNext();) {
420 out.println(" " + i.next());
421 }
422 out.println(" }");
423 out.println(" jdoFileNames = {");
424 for (Iterator i = jdoFileNames.iterator(); i.hasNext();) {
425 out.println(" " + i.next());
426 }
427 out.println(" }");
428 out.println(" classFileNames = {");
429 for (Iterator i = classFileNames.iterator(); i.hasNext();) {
430 out.println(" " + i.next());
431 }
432 out.println(" }");
433 out.println(" zipFileNames = {");
434 for (Iterator i = zipFileNames.iterator(); i.hasNext();) {
435 out.println(" " + i.next());
436 }
437 out.println(" }");
438 out.println(" dumpClass = " + dumpClass);
439 out.println(" noAugment = " + noAugment);
440 out.println(" noAnnotate = " + noAnnotate);
441 }
442 }
443
444 private int initClassLocator()
445 {
446 final boolean verbose = opts.verbose;
447 final List classFileNames = opts.classFileNames;
448 final List zipFileNames = opts.zipFileNames;
449 final String sourcePath = opts.sourcePath;
450 try {
451 final List locators = new ArrayList();
452
453
454 if (classFileNames != null && !classFileNames.isEmpty()) {
455 final ResourceLocator classes
456 = new ListResourceLocator(out, verbose, classFileNames);
457 if (verbose) {
458 out.println("Class Locator: using class files: {");
459 for (Iterator i = classFileNames.iterator(); i.hasNext();) {
460 out.println(" " + i.next());
461 }
462 out.println("}");
463 }
464 locators.add(classes);
465 }
466
467
468 if (zipFileNames != null && !zipFileNames.isEmpty()) {
469 final StringBuffer s = new StringBuffer();
470 final Iterator i = zipFileNames.iterator();
471 s.append(i.next());
472 while (i.hasNext()) {
473 s.append(File.pathSeparator + i.next());
474 }
475 final ResourceLocator zips
476 = new PathResourceLocator(out, verbose, s.toString());
477 if (verbose)
478 out.println("Class Locator: using jar/zip files: "
479 + s.toString());
480 locators.add(zips);
481 }
482
483
484 if (sourcePath != null && sourcePath.length() > 0) {
485 final ResourceLocator path
486 = new PathResourceLocator(out, verbose, sourcePath);
487 if (verbose)
488 out.println("Class Locator: using source path: "
489 + sourcePath);
490 locators.add(path);
491 }
492
493
494 affirm(!locators.isEmpty());
495
496
497
498
499
500 classLocator
501 = new CombinedResourceLocator(out, verbose, locators);
502
503
504 if (opts.doTiming) {
505 classLocator = new ResourceLocatorTimer(classLocator);
506 }
507 } catch (IOException ex) {
508 printError("Cannot initialize resource locator for classes", ex);
509 return CLASS_LOCATOR_ERROR;
510 }
511 return OK;
512 }
513
514 private int initEnhancerMetaData()
515 {
516 final boolean verbose = opts.verbose;
517 final String propertiesFileName = opts.propertiesFileName;
518 final List jdoFileNames = opts.jdoFileNames;
519 final List zipFileNames = opts.zipFileNames;
520 final String sourcePath = opts.sourcePath;
521 try {
522 if (propertiesFileName != null) {
523 jdoMetaData
524 = new EnhancerMetaDataPropertyImpl(out, verbose,
525 propertiesFileName);
526 } else {
527 jdoMetaData
528 = new EnhancerMetaDataJDOModelImpl(out, verbose,
529 jdoFileNames,
530 zipFileNames,
531 sourcePath);
532 }
533
534
535 if (opts.doTiming) {
536 jdoMetaData = new EnhancerMetaDataTimer(jdoMetaData);
537 }
538 } catch (EnhancerMetaDataFatalError ex) {
539 printError("Cannot initialize JDO meta-data source", ex);
540 return METADATA_ERROR;
541 }
542 return OK;
543 }
544
545 private int createEnhancer()
546 {
547 int res0 = initClassLocator();
548 if (res0 < 0) {
549 return res0;
550 }
551 affirm(classLocator != null);
552
553 int res = initEnhancerMetaData();
554 if (res < 0) {
555 return res;
556 }
557 affirm(jdoMetaData != null);
558
559 final Properties props = new Properties();
560 if (opts.verbose) {
561 props.put(EnhancerFilter.VERBOSE_LEVEL,
562 EnhancerFilter.VERBOSE_LEVEL_VERBOSE);
563 }
564
565 if (opts.doTiming) {
566 props.put(EnhancerFilter.DO_TIMING_STATISTICS,
567 Boolean.TRUE.toString());
568 }
569
570 if (opts.dumpClass) {
571 props.put(EnhancerFilter.DUMP_CLASS,
572 Boolean.TRUE.toString());
573 }
574
575 if (opts.noAugment) {
576 props.put(EnhancerFilter.NO_AUGMENT,
577 Boolean.TRUE.toString());
578 }
579
580 if (opts.noAnnotate) {
581 props.put(EnhancerFilter.NO_ANNOTATE,
582 Boolean.TRUE.toString());
583 }
584
585 try {
586 enhancer = new EnhancerFilter(jdoMetaData, props, out, err);
587 if (opts.doTiming) {
588
589 enhancer = new ClassFileEnhancerTimer(enhancer);
590 }
591 return 0;
592 } catch (EnhancerUserException ex) {
593 printError("Error while creating the enhancer", ex);
594 return -1;
595 } catch (EnhancerFatalError ex) {
596
597 printError("Fatal error while creating the enhancer", ex);
598 enhancer = null;
599 return -1;
600 }
601 }
602
603
604
605 /***
606 * Enhances all files entered in the command line.
607 *
608 * @param classNames List of class names.
609 * @param classFileNames List of class file names.
610 * @param zipFileNames List of zip file names.
611 * @param jdoFileNames List of jdo file names.
612 */
613 private int enhanceInputFiles(List classNames,
614 List classFileNames,
615 List zipFileNames,
616 List jdoFileNames)
617 {
618 int res = 0;
619 try {
620 String name = null;
621 for (Iterator i = zipFileNames.iterator(); i.hasNext();) {
622 try {
623 name = (String)i.next();
624 enhanceZipFile(name);
625 } catch (EnhancerUserException ex) {
626 printError("Error while enhancing " + name, ex);
627 res++;
628 continue;
629 }
630 }
631 for (Iterator i = classFileNames.iterator(); i.hasNext();) {
632 try {
633 name = (String)i.next();
634 enhanceClassFile(openFileInputStream(name));
635 } catch (EnhancerUserException ex) {
636 printError("Error while enhancing " + name, ex);
637 res++;
638 continue;
639 }
640 }
641 for (Iterator i = classNames.iterator(); i.hasNext();) {
642 try {
643 name = (String)i.next();
644 enhanceClassFile(openClassInputStream(name));
645 } catch (EnhancerUserException ex) {
646 printError("Error while enhancing " + name, ex);
647 res++;
648 continue;
649 }
650 }
651 } catch (IOException ex) {
652 printError("IO Error while enhancing", ex);
653 return ++res;
654 } catch (EnhancerFatalError ex) {
655
656 printError("Fatal error while enhancing", ex);
657 enhancer = null;
658 return ++res;
659 }
660 return res;
661 }
662
663 /***
664 * Enhances a classfile.
665 *
666 * @param in The input stream of the classfile.
667 */
668 private void enhanceClassFile(InputStream in)
669 throws IOException, EnhancerUserException, EnhancerFatalError
670 {
671 OutputStream out = null;
672 try {
673 final File temp = File.createTempFile("enhancer", ".class");
674 out = new BufferedOutputStream(new FileOutputStream(temp));
675
676
677 final OutputStreamWrapper wrapper = new OutputStreamWrapper(out);
678 final boolean enhanced = enhancer.enhanceClassFile(in, wrapper);
679
680 closeOutputStream(out);
681 out = null;
682 createOutputFile(enhanced,
683 createClassFileName(wrapper.getClassName()),
684 temp);
685 } finally {
686 closeInputStream(in);
687 closeOutputStream(out);
688 }
689 }
690
691 /***
692 * Enhances a zipfile.
693 *
694 * @param filename The filename of the zipfile.
695 */
696 private void enhanceZipFile(String filename)
697 throws IOException, EnhancerUserException, EnhancerFatalError
698 {
699 ZipInputStream in = null;
700 ZipOutputStream out = null;
701 try {
702 final File temp = File.createTempFile("enhancer", ".zip");
703 in = new ZipInputStream(new BufferedInputStream(
704 new FileInputStream(new File(filename))));
705 out = new ZipOutputStream(new BufferedOutputStream(
706 new FileOutputStream(temp)));
707
708
709 final boolean enhanced
710 = ClassFileEnhancerHelper.enhanceZipFile(enhancer, in, out);
711
712
713 closeOutputStream(out);
714 out = null;
715 createOutputFile(enhanced, new File(filename).getName(), temp);
716 } finally {
717 closeOutputStream(out);
718 closeInputStream(in);
719 }
720 }
721
722 /***
723 * Opens an input stream for the given filename
724 *
725 * @param filename The name of the file.
726 * @return The input stream.
727 * @exception FileNotFoundException If the file could not be found.
728 */
729 static private InputStream openFileInputStream(String filename)
730 throws FileNotFoundException
731 {
732 return new BufferedInputStream(new FileInputStream(new File(filename)));
733 }
734
735 /***
736 * Opens an input stream for the given classname. The input stream is
737 * created via an URL that is obtained by the value of the sourcepath
738 * option and zip/jar file arguments.
739 *
740 * @param classname The name of the class (dot-notation).
741 * @return The input stream.
742 * @exception IOException If an I/O error occured.
743 */
744 private InputStream openClassInputStream(String classname)
745 throws IOException
746 {
747 final String resourcename = createClassFileName(classname);
748 return classLocator.getInputStreamForResource(resourcename);
749 }
750
751 /***
752 * Creates a file object that represents the output zipfile for a given
753 * zipfile to enhance.
754 *
755 * @param zipfilename The input zipfile name.
756 * @return The output zipfile name.
757 */
758 private File createZipOutputFile(String zipfilename)
759 {
760 return new File(opts.destinationDirectory,
761 new File(zipfilename).getName());
762 }
763
764 /***
765 * Creates the output file for an enhaced class- or zipfile. If the
766 * enhanced file is written back depends on the command line options.
767 *
768 * @param enhanced Has the input file been enhanced?
769 * @param filename The name of the output file.
770 * @param temp The temp file, the output is written to.
771 * @exception IOException If the file could not be created.
772 */
773 private void createOutputFile(boolean enhanced,
774 String filename,
775 File temp)
776 throws IOException
777 {
778
779 if (opts.noWrite || (!enhanced && !opts.forceWrite)) {
780 temp.deleteOnExit();
781 return;
782 }
783
784 File file = new File(opts.destinationDirectory, filename);
785 createPathOfFile(file);
786 file.delete();
787 boolean renamed = temp.renameTo(file);
788 if (!renamed) {
789
790
791 InputStream in = null;
792 OutputStream out = null;
793 try {
794 in = new FileInputStream(temp);
795 out = new FileOutputStream(file);
796 int PAGESIZE = 4096;
797 byte data[] = new byte[PAGESIZE];
798 while (in.available() > 0) {
799 int numRead = in.read(data, 0, PAGESIZE);
800 out.write(data, 0, numRead);
801 }
802 renamed = true;
803 } catch (IOException ex) {
804 throw new IOException("Could not rename temp file '" +
805 temp.getAbsolutePath() +
806 "' to '" + file.getAbsolutePath()
807 + "': " + ex);
808 } finally {
809 closeInputStream(in);
810 closeOutputStream(out);
811 }
812 if (renamed) {
813 temp.delete();
814 }
815 else {
816 throw new IOException("Could not rename temp file '" +
817 temp.getAbsolutePath() +
818 "' to '" + file.getAbsolutePath() + "'.");
819 }
820 }
821 }
822
823 /***
824 * Closes an input stream.
825 *
826 * @param in The input stream.
827 */
828 private void closeInputStream(InputStream in)
829 {
830 if (in != null) {
831 try {
832 in.close();
833 } catch (IOException ex) {
834 printError(null, ex);
835 }
836 }
837 }
838
839 /***
840 * Closes an output stream.
841 *
842 * @param out The output stream.
843 */
844 private void closeOutputStream(OutputStream out)
845 {
846 if (out != null) {
847 try {
848 out.close();
849 } catch (IOException ex) {
850 printError(null, ex);
851 }
852 }
853 }
854
855 /***
856 * Tests if a filename is a classfile name (by testing if the filename
857 * ends with <code>".class"</code>).
858 *
859 * @param filename The name of the file.
860 * @return Do we have a potential classfile?
861 */
862 static private boolean isClassFileName(String filename)
863 {
864 return filename.toLowerCase().endsWith(".class");
865 }
866
867 /***
868 * Tests if a filename is a zipfile (by testing if the filename
869 * ends with <code>".zip"</code> or <code>".jar"</code>).
870 *
871 * @param filename The name of the file.
872 */
873 static private boolean isZipFileName(String filename)
874 {
875 final int n = filename.length();
876 if (n < 5) {
877 return false;
878 }
879 final String ext = filename.substring(n - 4);
880 return ext.equalsIgnoreCase(".zip") || ext.equalsIgnoreCase(".jar");
881 }
882
883 /***
884 * Tests if a filename is a jdo file name (by testing if the filename
885 * ends with <code>".jdo"</code>).
886 *
887 * @param filename The name of the file.
888 * @return Do we have a potential jdo file?
889 */
890 static private boolean isJdoFileName(String filename)
891 {
892 return filename.toLowerCase().endsWith(".jdo");
893 }
894
895 /***
896 * Creates a filename from a classname.
897 * This is done by replacing <code>'.'</code> by <code>'/'</code>.
898 *
899 * @param classname The classname.
900 * @return The filename.
901 */
902 static private String createClassFileName(String classname)
903 {
904 return classname.replace('.', '/') + ".class";
905 }
906
907 /***
908 * Creates only the path of the given file.
909 *
910 * @param file The file.
911 * @exception IOException If an error occured.
912 */
913 static private void createPathOfFile(File file)
914 throws IOException
915 {
916 File dir = file.getAbsoluteFile().getParentFile();
917 if (!dir.exists() && !dir.mkdirs()) {
918 throw new IOException("Error creating directory '"
919 + dir.getAbsolutePath() + "'.");
920 }
921 }
922
923
924
925 /***
926 * Prints out an error.
927 *
928 * @param msg The error message (can be <code>null</code>).
929 * @param ex An optional exception (can be <code>null</code>).
930 */
931 private void printError(String msg,
932 Throwable ex)
933 {
934 out.flush();
935 if (msg != null) {
936 err.println(msg);
937 }
938 if (ex != null) {
939 if (opts.verbose) {
940 ex.printStackTrace(err);
941 }
942 else {
943 err.println(ex.toString());
944 }
945 }
946 }
947
948 /***
949 * Prints out a message.
950 *
951 * @param msg The message.
952 */
953 private void printMessage(String msg)
954 {
955 out.println(msg);
956 }
957 }