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