source: other-projects/the-macronizer/trunk/src/java/util/Utils4JJ.java@ 29855

Last change on this file since 29855 was 29855, checked in by davidb, 9 years ago

John's code after refactoring by Tom over the summer of 2014/2015

File size: 56.6 KB
Line 
1/**
2 * Copyright (C) 2009 Future Invent Informationsmanagement GmbH. All rights
3 * reserved. <http://www.fuin.org/>
4 *
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; either version 3 of the License, or (at your option) any
8 * later version.
9 *
10 * This library is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18package util;
19
20import java.io.BufferedInputStream;
21import java.io.BufferedOutputStream;
22import java.io.BufferedWriter;
23import java.io.File;
24import java.io.FileFilter;
25import java.io.FileInputStream;
26import java.io.FileOutputStream;
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
30import java.io.OutputStreamWriter;
31import java.io.RandomAccessFile;
32import java.io.Writer;
33import java.lang.reflect.InvocationTargetException;
34import java.lang.reflect.Method;
35import java.net.MalformedURLException;
36import java.net.URL;
37import java.net.URLClassLoader;
38import java.nio.channels.FileChannel;
39import java.nio.channels.FileLock;
40import java.nio.channels.OverlappingFileLockException;
41import java.security.GeneralSecurityException;
42import java.security.MessageDigest;
43import java.security.NoSuchAlgorithmException;
44import java.util.Date;
45import java.util.Enumeration;
46import java.util.Map;
47import java.util.Properties;
48import java.util.zip.ZipEntry;
49import java.util.zip.ZipFile;
50import java.util.zip.ZipOutputStream;
51
52import javax.crypto.Cipher;
53import javax.crypto.SecretKey;
54import javax.crypto.SecretKeyFactory;
55import javax.crypto.spec.PBEKeySpec;
56import javax.crypto.spec.PBEParameterSpec;
57import org.fuin.utils4j.Cancelable;
58import org.fuin.utils4j.IllegalNullArgumentException;
59import org.fuin.utils4j.InvokeMethodFailedException;
60import org.fuin.utils4j.LockingFailedException;
61
62
63/**
64 * Common utility methods for use in Java applications and libraries.
65 */
66public final class Utils4JJ {
67
68 private static final String USER_HOME_KEY = "user.home";
69 private static final String TEMP_DIR_KEY = "java.io.tmpdir";
70 /**
71 * The difference between the Windows epoch (1601-01-01 00:00:00) and the
72 * Unix epoch (1970-01-01 00:00:00) in milliseconds: 11644473600000L.
73 */
74 private static final long EPOCH_DIFF = 11644473600000L;
75 /**
76 * Used building output as Hex.
77 */
78 private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6',
79 '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
80
81 /**
82 * Private default constructor.
83 */
84 private Utils4JJ() {
85 throw new UnsupportedOperationException(
86 "This utility class is not intended to be instanciated!");
87 }
88
89 /**
90 * Returns the package path of a class.
91 *
92 * @param clasz
93 * Class to determine the path for - Cannot be <code>null</code>.
94 *
95 * @return Package path for the class.
96 */
97 public static String getPackagePath(final Class clasz) {
98 checkNotNull("clasz", clasz);
99 return clasz.getPackage().getName().replace('.', '/');
100 }
101
102 /**
103 * Get the path to a resource located in the same package as a given class.
104 *
105 * @param clasz
106 * Class with the same package where the resource is located -
107 * Cannot be <code>null</code>.
108 * @param name
109 * Filename of the resource - Cannot be <code>null</code>.
110 *
111 * @return Resource URL.
112 */
113 public static URL getResource(final Class clasz, final String name) {
114 checkNotNull("clasz", clasz);
115 checkNotNull("name", name);
116 final String nameAndPath = "/" + getPackagePath(clasz) + "/" + name;
117 return clasz.getResource(nameAndPath);
118 }
119
120 /**
121 * Load properties from classpath.
122 *
123 * @param clasz
124 * Class in the same package as the properties file - Cannot be
125 * <code>null</code>.
126 * @param filename
127 * Name of the properties file (without path) - Cannot be
128 * <code>null</code>.
129 *
130 * @return Properties.
131 */
132 public static Properties loadProperties(final Class clasz,
133 final String filename) {
134 checkNotNull("clasz", clasz);
135 checkNotNull("filename", filename);
136
137 final String path = getPackagePath(clasz);
138 final String resPath = "/" + path + "/" + filename;
139 try {
140 final Properties props = new Properties();
141 final InputStream inStream = clasz.getResourceAsStream(resPath);
142 if (inStream == null) {
143 throw new IllegalArgumentException("Resource '" + resPath
144 + "' was not found!");
145 }
146 try {
147 props.load(inStream);
148 } finally {
149 inStream.close();
150 }
151 return props;
152 } catch (final IOException ex) {
153 throw new RuntimeException(ex);
154 }
155 }
156
157 /**
158 * Load properties from a file.
159 *
160 * @param file
161 * Properties file - Cannot be <code>null</code> and must be a
162 * valid file.
163 *
164 * @return Properties.
165 */
166 public static Properties loadProperties(final File file) {
167 checkNotNull("file", file);
168 checkValidFile(file);
169 try {
170 final Properties props = new Properties();
171 final InputStream inStream = new FileInputStream(file);
172 try {
173 props.load(inStream);
174 } finally {
175 inStream.close();
176 }
177 return props;
178 } catch (final IOException ex) {
179 throw new RuntimeException(ex);
180 }
181 }
182
183 /**
184 * Check if the argument is an existing file. If the check fails an
185 * <code>IllegalArgumentException</code> is thrown.
186 *
187 * @param file
188 * File to check - Cannot be <code>null</code>.
189 */
190 public static void checkValidFile(final File file) {
191 checkNotNull("file", file);
192 if (!file.exists()) {
193 throw new IllegalArgumentException("The file '" + file
194 + "' does not exist!");
195 }
196 if (!file.isFile()) {
197 throw new IllegalArgumentException("The name '" + file
198 + "' is not a file!");
199 }
200 }
201
202 /**
203 * Check if the argument is an existing directory. If the check fails an
204 * <code>IllegalArgumentException</code> is thrown.
205 *
206 * @param dir
207 * Directory to check - Cannot be <code>null</code>.
208 */
209 public static void checkValidDir(final File dir) {
210 checkNotNull("dir", dir);
211 if (!dir.exists()) {
212 throw new IllegalArgumentException("The directory '" + dir
213 + "' does not exist!");
214 }
215 if (!dir.isDirectory()) {
216 throw new IllegalArgumentException("The name '" + dir
217 + "' is not a directory!");
218 }
219 }
220
221 /**
222 * Save properties to a file.
223 *
224 * @param file
225 * Destination file - Cannot be <code>null</code> and parent
226 * directory must exist.
227 * @param props
228 * Properties to save - Cannot be <code>null</code>.
229 * @param comment
230 * Comment for the file.
231 */
232 public static void saveProperties(final File file, final Properties props,
233 final String comment) {
234 checkNotNull("file", file);
235 checkNotNull("props", props);
236
237 if (!file.getParentFile().exists()) {
238 throw new IllegalArgumentException("The parent directory '"
239 + file.getParentFile() + "' does not exist [file='" + file
240 + "']!");
241 }
242 try {
243 final OutputStream outStream = new FileOutputStream(file);
244 try {
245 props.store(outStream, comment);
246 } finally {
247 outStream.close();
248 }
249 } catch (final IOException ex) {
250 throw new RuntimeException(ex);
251 }
252 }
253
254 /**
255 * Create an instance with Class.forName(..) and wrap all exceptions into
256 * RuntimeExceptions.
257 *
258 * @param className
259 * Full qualified class name - Cannot be <code>null</code>.
260 *
261 * @return New instance of the class.
262 */
263 public static Object createInstance(final String className) {
264 checkNotNull("className", className);
265 try {
266 final Class clasz = Class.forName(className);
267 return clasz.newInstance();
268 } catch (final ClassNotFoundException e) {
269 throw new RuntimeException("Unknown class!", e);
270 } catch (final InstantiationException e) {
271 throw new RuntimeException("Error instanciating class!", e);
272 } catch (final IllegalAccessException e) {
273 throw new RuntimeException("Error accessing class!", e);
274 }
275 }
276
277 /**
278 * Adds an URL to the classpath.
279 *
280 * @param url
281 * URL to add - Cannot be <code>null</code>.
282 */
283 public static void addToClasspath(final String url) {
284 checkNotNull("url", url);
285 try {
286 addToClasspath(new URL(url));
287 } catch (final MalformedURLException e) {
288 throw new RuntimeException(e);
289 }
290 }
291
292 /**
293 * Checks if the array or URLs contains the given URL.
294 *
295 * @param urls
296 * Array of URLs - Cannot be <code>null</code>.
297 * @param url
298 * URL to find - Cannot be <code>null</code>.
299 *
300 * @return If the URL is in the array TRUE else FALSE.
301 */
302 public static boolean containsURL(final URL[] urls, final URL url) {
303 checkNotNull("urls", urls);
304 checkNotNull("url", url);
305 for (int i = 0; i < urls.length; i++) {
306 final URL element = urls[i];
307 final String elementStr = element.toExternalForm();
308 final String urlStr = url.toExternalForm();
309 if (elementStr.equals(urlStr)) {
310 return true;
311 }
312 }
313 return false;
314 }
315
316 /**
317 * Creates an MD5 hash from a file.
318 *
319 * @param file
320 * File to create an hash for - Cannot be <code>null</code>.
321 *
322 * @return Hash as text.
323 *
324 * @deprecated Use <code>createHashMD5</code> instead.
325 */
326 public static String createHash(final File file) {
327 return createHashMD5(file);
328 }
329
330 /**
331 * Creates an MD5 hash from a file.
332 *
333 * @param file
334 * File to create an hash for - Cannot be <code>null</code>.
335 *
336 * @return Hash as text.
337 */
338 public static String createHashMD5(final File file) {
339 return createHash(file, "MD5");
340 }
341
342 /**
343 * Creates a HEX encoded hash from a file.
344 *
345 * @param file
346 * File to create a hash for - Cannot be <code>null</code>.
347 * @param algorithm
348 * Hash algorithm like "MD5" or "SHA" - Cannot be
349 * <code>null</code>.
350 *
351 * @return HEX encoded hash.
352 */
353 public static String createHash(final File file, final String algorithm) {
354 checkNotNull("file", file);
355 checkNotNull("algorithm", algorithm);
356 try {
357 final FileInputStream in = new FileInputStream(file);
358 try {
359 return createHash(in, algorithm);
360 } finally {
361 in.close();
362 }
363 } catch (final IOException ex) {
364 throw new RuntimeException(ex);
365 }
366 }
367
368 /**
369 * Creates a HEX encoded hash from a stream.
370 *
371 * @param inputStream
372 * Stream to create a hash for - Cannot be <code>null</code>.
373 * @param algorithm
374 * Hash algorithm like "MD5" or "SHA" - Cannot be
375 * <code>null</code>.
376 *
377 * @return HEX encoded hash.
378 */
379 public static String createHash(final InputStream inputStream,
380 final String algorithm) {
381 checkNotNull("inputStream", inputStream);
382 checkNotNull("algorithm", algorithm);
383 try {
384 final MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
385 final BufferedInputStream in = new BufferedInputStream(inputStream);
386 try {
387 final byte[] buf = new byte[1024];
388 int count = 0;
389 while ((count = in.read(buf)) > -1) {
390 messageDigest.update(buf, 0, count);
391 }
392 } finally {
393 in.close();
394 }
395 return encodeHex(messageDigest.digest());
396 } catch (final NoSuchAlgorithmException ex) {
397 throw new RuntimeException(ex);
398 } catch (final IOException ex) {
399 throw new RuntimeException(ex);
400 }
401 }
402
403 /**
404 * Creates a cipher for encryption or decryption.
405 *
406 * @param algorithm
407 * PBE algorithm like "PBEWithMD5AndDES" or
408 * "PBEWithMD5AndTripleDES".
409 * @param mode
410 * Encyrption or decyrption.
411 * @param password
412 * Password.
413 * @param salt
414 * Salt usable with algorithm.
415 * @param count
416 * Iterations.
417 *
418 * @return Ready initialized cipher.
419 *
420 * @throws GeneralSecurityException
421 * Error creating the cipher.
422 */
423 private static Cipher createCipher(final String algorithm, final int mode,
424 final char[] password, final byte[] salt, final int count)
425 throws GeneralSecurityException {
426
427 final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
428 final PBEKeySpec keySpec = new PBEKeySpec(password);
429 final SecretKey key = keyFactory.generateSecret(keySpec);
430 final Cipher cipher = Cipher.getInstance(algorithm);
431 final PBEParameterSpec params = new PBEParameterSpec(salt, count);
432 cipher.init(mode, key, params);
433 return cipher;
434
435 }
436
437 /**
438 * Encrypts some data based on a password.
439 *
440 * @param algorithm
441 * PBE algorithm like "PBEWithMD5AndDES" or
442 * "PBEWithMD5AndTripleDES" - Cannot be <code>null</code>.
443 * @param data
444 * Data to encrypt - Cannot be <code>null</code>.
445 * @param password
446 * Password - Cannot be <code>null</code>.
447 * @param salt
448 * Salt usable with algorithm - Cannot be <code>null</code>.
449 * @param count
450 * Iterations.
451 *
452 * @return Encrypted data.
453 */
454 public static byte[] encryptPasswordBased(final String algorithm,
455 final byte[] data, final char[] password, final byte[] salt,
456 final int count) {
457
458 checkNotNull("algorithm", algorithm);
459 checkNotNull("data", data);
460 checkNotNull("password", password);
461 checkNotNull("salt", salt);
462
463 try {
464 final Cipher cipher = createCipher(algorithm, Cipher.ENCRYPT_MODE,
465 password, salt, count);
466 return cipher.doFinal(data);
467 } catch (final Exception ex) {
468 throw new RuntimeException(
469 "Error encrypting the password!", ex);
470 }
471 }
472
473 /**
474 * Decrypts some data based on a password.
475 *
476 * @param algorithm
477 * PBE algorithm like "PBEWithMD5AndDES" or
478 * "PBEWithMD5AndTripleDES" - Cannot be <code>null</code>.
479 * @param encryptedData
480 * Data to decrypt - Cannot be <code>null</code>.
481 * @param password
482 * Password - Cannot be <code>null</code>.
483 * @param salt
484 * Salt usable with algorithm - Cannot be <code>null</code>.
485 * @param count
486 * Iterations.
487 *
488 * @return Encrypted data.
489 */
490 public static byte[] decryptPasswordBased(final String algorithm,
491 final byte[] encryptedData, final char[] password,
492 final byte[] salt, final int count) {
493
494 checkNotNull("algorithm", algorithm);
495 checkNotNull("encryptedData", encryptedData);
496 checkNotNull("password", password);
497 checkNotNull("salt", salt);
498
499 try {
500 final Cipher cipher = createCipher(algorithm, Cipher.DECRYPT_MODE,
501 password, salt, count);
502 return cipher.doFinal(encryptedData);
503 } catch (final Exception ex) {
504 throw new RuntimeException(
505 "Error decrypting the password!", ex);
506 }
507 }
508
509 /**
510 * Creates an URL based on a directory a relative path and a filename.
511 *
512 * @param baseUrl
513 * Directory URL with or without slash ("/") at the end of the
514 * string - Cannot be <code>null</code>.
515 * @param path
516 * Relative path inside the base URL (with or without slash ("/")
517 * at the end of the string) - Can be <code>null</code> or an
518 * empty string.
519 * @param filename
520 * Filename without path - Cannot be <code>null</code>.
521 *
522 * @return URL.
523 */
524 public static URL createUrl(final URL baseUrl, final String path,
525 final String filename) {
526 checkNotNull("baseUrl", baseUrl);
527 checkNotNull("filename", filename);
528 try {
529 String baseUrlStr = baseUrl.toString();
530 if (!baseUrlStr.endsWith("/")) {
531 baseUrlStr = baseUrlStr + "/";
532 }
533 final String pathStr;
534 if ((path == null) || (path.length() == 0)) {
535 pathStr = "";
536 } else {
537 if (path.endsWith("/")) {
538 pathStr = path;
539 } else {
540 pathStr = path + "/";
541 }
542 }
543 return new URL(baseUrlStr + pathStr + filename);
544 } catch (final IOException ex) {
545 throw new RuntimeException(ex);
546 }
547 }
548
549 /**
550 * Returns a relative path based on a base directory. If the
551 * <code>dir</code> is not inside <code>baseDir</code> an
552 * <code>IllegalArgumentException</code> is thrown.
553 *
554 * @param baseDir
555 * Base directory the path is relative to - Cannot be
556 * <code>null</code>.
557 * @param dir
558 * Directory inside the base directory - Cannot be
559 * <code>null</code>.
560 *
561 * @return Path of <code>dir</code> relative to <code>baseDir</code>. If
562 * both are equal an empty string is returned.
563 */
564 public static String getRelativePath(final File baseDir, final File dir) {
565 checkNotNull("baseDir", baseDir);
566 checkNotNull("dir", dir);
567 try {
568 final String base = baseDir.getCanonicalPath();
569 final String path = dir.getCanonicalPath();
570 if (!path.startsWith(base)) {
571 throw new IllegalArgumentException("The path '" + path
572 + "' is not inside the base directory '" + base + "'!");
573 }
574 if (base.equals(path)) {
575 return "";
576 }
577 return path.substring(base.length() + 1);
578 } catch (final IOException ex) {
579 throw new RuntimeException(ex);
580 }
581 }
582
583 /**
584 * Load a file from an directory.
585 *
586 * @param baseUrl
587 * Directory URL - Cannot be <code>null</code>.
588 * @param filename
589 * Filename without path - Cannot be <code>null</code>.
590 *
591 * @return Properties.
592 */
593 public static Properties loadProperties(final URL baseUrl,
594 final String filename) {
595 return loadProperties(createUrl(baseUrl, "", filename));
596 }
597
598 /**
599 * Load a file from an URL.
600 *
601 * @param fileURL
602 * Property file URL - Cannot be <code>null</code>.
603 *
604 * @return Properties.
605 */
606 public static Properties loadProperties(final URL fileURL) {
607 checkNotNull("fileURL", fileURL);
608 try {
609 final Properties props = new Properties();
610 final InputStream inStream = fileURL.openStream();
611 try {
612 props.load(inStream);
613 } finally {
614 inStream.close();
615 }
616 return props;
617 } catch (final IOException ex) {
618 throw new RuntimeException(ex);
619 }
620 }
621
622 /**
623 * Load a file from an directory. Wraps a possible
624 * <code>MalformedURLException</code> exception into a
625 * <code>RuntimeException</code>.
626 *
627 * @param baseUrl
628 * Directory URL as <code>String</code> - Cannot be
629 * <code>null</code>.
630 * @param filename
631 * Filename without path - Cannot be <code>null</code>.
632 *
633 * @return Properties.
634 */
635 public static Properties loadProperties(final String baseUrl,
636 final String filename) {
637 checkNotNull("baseUrl", baseUrl);
638 checkNotNull("filename", filename);
639
640 try {
641 final URL url = new URL(baseUrl);
642 return loadProperties(url, filename);
643 } catch (final MalformedURLException ex) {
644 // Should be an IllegalArgumentException but 1.4 has no "String,
645 // Throwable" constructor...
646 throw new RuntimeException(
647 "The argument 'srcUrl' is not a valid URL [" + baseUrl
648 + "]!", ex);
649 }
650
651 }
652
653 /**
654 * Adds an URL to the classpath.
655 *
656 * @param url
657 * URL to add - Cannot be <code>null</code>.
658 */
659 public static void addToClasspath(final URL url) {
660 checkNotNull("url", url);
661 final ClassLoader classLoader = Utils4JJ.class.getClassLoader();
662 if (!(classLoader instanceof URLClassLoader)) {
663 throw new IllegalArgumentException("Cannot add '" + url
664 + "' to classloader because it's not an URL classloader");
665 }
666 final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
667 if (!containsURL(urlClassLoader.getURLs(), url)) {
668 try {
669
670 final Method addURL = URLClassLoader.class.getDeclaredMethod(
671 "addURL", new Class[]{URL.class});
672 addURL.setAccessible(true);
673 addURL.invoke(urlClassLoader, new Object[]{url});
674 } catch (final NoSuchMethodException e) {
675 throw new RuntimeException(e);
676 } catch (final IllegalArgumentException e) {
677 throw new RuntimeException(e);
678 } catch (final IllegalAccessException e) {
679 throw new RuntimeException(e);
680 } catch (final InvocationTargetException e) {
681 throw new RuntimeException(e);
682 }
683 }
684 }
685
686 /**
687 * Checks if a variable is not <code>null</code> and throws an
688 * <code>IllegalNullArgumentException</code> if this rule is violated.
689 *
690 * @param name
691 * Name of the variable to be displayed in an error message.
692 * @param value
693 * Value to check for <code>null</code>.
694 */
695 public static void checkNotNull(final String name, final Object value) {
696 if (value == null) {
697 throw new IllegalNullArgumentException(name);
698 }
699 }
700
701 /**
702 * Checks if a variable is not <code>empty</code> and throws an
703 * <code>IllegalNullArgumentException</code> if this rule is violated. A
704 * String with spaces is NOT considered empty!
705 *
706 * @param name
707 * Name of the variable to be displayed in an error message.
708 * @param value
709 * Value to check for an empty String - Cannot be
710 * <code>null</code>.
711 */
712 public static void checkNotEmpty(final String name, final String value) {
713 if (value.length() == 0) {
714 throw new IllegalArgumentException("The argument '" + name
715 + "' cannot be empty!!");
716 }
717 }
718
719 /**
720 * Creates a textual representation of the method.
721 *
722 * @param returnType
723 * Return type of the method - Can be <code>null</code>.
724 * @param methodName
725 * Name of the method - Cannot be <code>null</code>.
726 * @param argTypes
727 * The list of parameters - Can be <code>null</code>.
728 *
729 * @return Textual signature of the method.
730 */
731 private static String getMethodSignature(final String returnType,
732 final String methodName, final Class[] argTypes) {
733 final StringBuffer sb = new StringBuffer();
734 if (returnType != null) {
735 sb.append(returnType);
736 sb.append(" ");
737 }
738 sb.append(methodName);
739 sb.append("(");
740 if (argTypes != null) {
741 for (int i = 0; i < argTypes.length; i++) {
742 if (i > 0) {
743 sb.append(", ");
744 }
745 sb.append(argTypes[i].getName());
746 }
747 }
748 sb.append(")");
749 return sb.toString();
750 }
751
752 /**
753 * Calls a method with reflection and maps all errors into one exception.
754 *
755 * @param obj
756 * The object the underlying method is invoked from - Cannot be
757 * <code>null</code>.
758 * @param methodName
759 * Name of the Method - Cannot be <code>null</code>.
760 * @param argTypes
761 * The list of parameters - May be <code>null</code>.
762 * @param args
763 * Arguments the arguments used for the method call - May be
764 * <code>null</code> if "argTypes" is also <code>null</code>.
765 *
766 * @return The result of dispatching the method represented by this object
767 * on <code>obj</code> with parameters <code>args</code>.
768 *
769 * @throws InvokeMethodFailedException
770 * Invoking the method failed for some reason.
771 */
772 public static Object invoke(final Object obj, final String methodName,
773 final Class[] argTypes, final Object[] args)
774 throws InvokeMethodFailedException {
775
776 checkNotNull("obj", obj);
777 checkNotNull("methodName", methodName);
778
779 final Class[] argTypesIntern;
780 final Object[] argsIntern;
781 if (argTypes == null) {
782 argTypesIntern = new Class[]{};
783 if (args != null) {
784 throw new IllegalArgumentException(
785 "The argument 'argTypes' is null but "
786 + "'args' containes values!");
787 }
788 argsIntern = new Object[]{};
789 } else {
790 argTypesIntern = argTypes;
791 if (args == null) {
792 throw new IllegalArgumentException(
793 "The argument 'argTypes' contains classes "
794 + "but 'args' is null!");
795 }
796 argsIntern = args;
797 }
798 checkSameLength(argTypesIntern, argsIntern);
799
800 String returnType = null;
801 try {
802 final Method method = obj.getClass().getMethod(methodName,
803 argTypesIntern);
804 if (method.getReturnType() == null) {
805 returnType = "void";
806 } else {
807 returnType = method.getReturnType().getName();
808 }
809 return method.invoke(obj, argsIntern);
810 } catch (final SecurityException ex) {
811 throw new InvokeMethodFailedException(
812 "Security problem with '"
813 + getMethodSignature(returnType, methodName,
814 argTypesIntern) + "'! ["
815 + obj.getClass().getName() + "]", ex);
816 } catch (final NoSuchMethodException ex) {
817 throw new InvokeMethodFailedException(
818 "Method '"
819 + getMethodSignature(returnType, methodName,
820 argTypesIntern) + "' not found! ["
821 + obj.getClass().getName() + "]", ex);
822 } catch (final IllegalArgumentException ex) {
823 throw new InvokeMethodFailedException(
824 "Argument problem with '"
825 + getMethodSignature(returnType, methodName,
826 argTypesIntern) + "'! ["
827 + obj.getClass().getName() + "]", ex);
828 } catch (final IllegalAccessException ex) {
829 throw new InvokeMethodFailedException(
830 "Access problem with '"
831 + getMethodSignature(returnType, methodName,
832 argTypesIntern) + "'! ["
833 + obj.getClass().getName() + "]", ex);
834 } catch (final InvocationTargetException ex) {
835 throw new InvokeMethodFailedException(
836 "Got an exception when calling '"
837 + getMethodSignature(returnType, methodName,
838 argTypesIntern) + "'! ["
839 + obj.getClass().getName() + "]", ex);
840 }
841
842 }
843
844 private static void checkSameLength(final Class[] argTypes,
845 final Object[] args) {
846 if (argTypes.length != args.length) {
847 throw new IllegalArgumentException(
848 "The argument 'argTypes' contains " + argTypes.length
849 + " classes " + "but 'args' only contains "
850 + args.length + " arguments!");
851 }
852 }
853
854 /**
855 * Unzips a file into a given directory. WARNING: Only relative path entries
856 * are allowed inside the archive!
857 *
858 * @param zipFile
859 * Source ZIP file - Cannot be <code>null</code> and must be a
860 * valid ZIP file.
861 * @param destDir
862 * Destination directory - Cannot be <code>null</code> and must
863 * exist.
864 *
865 * @throws IOException
866 * Error unzipping the file.
867 */
868 public static void unzip(final File zipFile, final File destDir)
869 throws IOException {
870 unzip(zipFile, destDir, null, null);
871 }
872
873 /**
874 * Unzips a file into a given directory. WARNING: Only relative path entries
875 * are allowed inside the archive!
876 *
877 * @param zipFile
878 * Source ZIP file - Cannot be <code>null</code> and must be a
879 * valid ZIP file.
880 * @param destDir
881 * Destination directory - Cannot be <code>null</code> and must
882 * exist.
883 * @param wrapper
884 * Callback interface to give the caller the chance to wrap the
885 * ZIP input stream into another one. This is useful for example
886 * to display a progress bar - Can be <code>null</code> if no
887 * wrapping is required.
888 * @param cancelable
889 * Signals if the unzip should be canceled - Can be
890 * <code>null</code> if no cancel option is required.
891 *
892 * @throws IOException
893 * Error unzipping the file.
894 */
895 public static void unzip(final File zipFile, final File destDir,
896 final UnzipInputStreamWrapper wrapper, final Cancelable cancelable)
897 throws IOException {
898
899 checkNotNull("zipFile", zipFile);
900 checkValidFile(zipFile);
901 checkNotNull("destDir", destDir);
902 checkValidDir(destDir);
903
904 final ZipFile zip = new ZipFile(zipFile);
905 try {
906 final Enumeration enu = zip.entries();
907 while (enu.hasMoreElements()
908 && ((cancelable == null) || !cancelable.isCanceled())) {
909 final ZipEntry entry = (ZipEntry) enu.nextElement();
910 final File file = new File(entry.getName());
911 if (file.isAbsolute()) {
912 throw new IllegalArgumentException(
913 "Only relative path entries are allowed! ["
914 + entry.getName() + "]");
915 }
916 if (entry.isDirectory()) {
917 final File dir = new File(destDir, entry.getName());
918 createIfNecessary(dir);
919 } else {
920 final File outFile = new File(destDir, entry.getName());
921 createIfNecessary(outFile.getParentFile());
922 final InputStream in;
923 if (wrapper == null) {
924 in = new BufferedInputStream(zip.getInputStream(entry));
925 } else {
926 in = new BufferedInputStream(wrapper.wrapInputStream(
927 zip.getInputStream(entry), entry, outFile));
928 }
929 try {
930 final OutputStream out = new BufferedOutputStream(
931 new FileOutputStream(outFile));
932 try {
933 final byte[] buf = new byte[4096];
934 int len;
935 while ((len = in.read(buf)) > 0) {
936 out.write(buf, 0, len);
937 }
938 } finally {
939 out.close();
940 }
941 } finally {
942 in.close();
943 }
944 }
945 }
946 } finally {
947 zip.close();
948 }
949 }
950
951 private static void createIfNecessary(final File dir) throws IOException {
952 if (dir.exists()) {
953 return;
954 }
955 if (!dir.mkdirs()) {
956 throw new IOException("Error creating directory '" + dir + "'!");
957 }
958 }
959
960 /**
961 * Returns the user home directory and checks if it is valid and exists. If
962 * not a <code>IllegalStateException</code> is thrown.
963 *
964 * @return Directory.
965 */
966 public static File getUserHomeDir() {
967 final String str = System.getProperty(USER_HOME_KEY);
968 if (str == null) {
969 throw new IllegalStateException("System property '" + USER_HOME_KEY
970 + "' not found!");
971 }
972 final String userHome = str.trim();
973 if (userHome.length() == 0) {
974 throw new IllegalStateException("System property '" + USER_HOME_KEY
975 + "' is empty!");
976 }
977 final File dir = new File(userHome);
978 try {
979 checkValidDir(dir);
980 } catch (final IllegalArgumentException ex) {
981 throw new IllegalStateException("System property '" + USER_HOME_KEY
982 + "' is not valid! [" + ex.getMessage() + "]");
983 }
984 return dir;
985 }
986
987 /**
988 * Returns the temporary directory and checks if it is valid and exists. If
989 * not a <code>IllegalStateException</code> is thrown.
990 *
991 * @return Directory.
992 */
993 public static File getTempDir() {
994 final String str = System.getProperty(TEMP_DIR_KEY);
995 if (str == null) {
996 throw new IllegalStateException("System property '" + TEMP_DIR_KEY
997 + "' not found!");
998 }
999 final String tempDirStr = str.trim();
1000 if (tempDirStr.length() == 0) {
1001 throw new IllegalStateException("System property '" + TEMP_DIR_KEY
1002 + "' is empty!");
1003 }
1004 final File dir = new File(tempDirStr);
1005 try {
1006 checkValidDir(dir);
1007 } catch (final IllegalArgumentException ex) {
1008 throw new IllegalStateException("System property '" + TEMP_DIR_KEY
1009 + "' is not valid! [" + ex.getMessage() + "]");
1010 }
1011 return dir;
1012 }
1013
1014 /**
1015 * Replaces all variables inside a string with values from a map.
1016 *
1017 * @param str
1018 * Text with variables (Format: ${key} ) - May be
1019 * <code>null</code> or empty.
1020 * @param vars
1021 * Map with key/values (both of type <code>String</code> - Cannot
1022 * be <code>null</code>.
1023 *
1024 * @return String with replaced variables. Unknown variables will remain
1025 * unchanged.
1026 */
1027 public static String replaceVars(final String str, final Map vars) {
1028
1029 if (str == null) {
1030 return null;
1031 }
1032 if (str.length() == 0) {
1033 return str;
1034 }
1035
1036 checkNotNull("vars", vars);
1037
1038 final StringBuffer sb = new StringBuffer();
1039
1040 int end = -1;
1041 int from = 0;
1042 int start = -1;
1043 while ((start = str.indexOf("${", from)) > -1) {
1044 sb.append(str.substring(end + 1, start));
1045 end = str.indexOf('}', start + 1);
1046 if (end == -1) {
1047 // No closing bracket found...
1048 sb.append(str.substring(start));
1049 from = str.length();
1050 } else {
1051 final String key = str.substring(start + 2, end);
1052 final String value = (String) vars.get(key);
1053 if (value == null) {
1054 sb.append("${");
1055 sb.append(key);
1056 sb.append("}");
1057 } else {
1058 sb.append(value);
1059 }
1060 from = end + 1;
1061 }
1062 }
1063
1064 sb.append(str.substring(from));
1065
1066 return sb.toString();
1067 }
1068
1069 /**
1070 * Converts Date into a Windows FILETIME. The Windows FILETIME structure
1071 * holds a date and time associated with a file. The structure identifies a
1072 * 64-bit integer specifying the number of 100-nanosecond intervals which
1073 * have passed since January 1, 1601. This code is copied from the
1074 * <code>org.apache.poi.hpsf.Util</code> class.
1075 *
1076 * @param date
1077 * The date to be converted - Cannot be <code>null</code>.
1078 * @return The file time
1079 */
1080 public static long dateToFileTime(final Date date) {
1081 checkNotNull("date", date);
1082 final long msSince19700101 = date.getTime();
1083 final long msSince16010101 = msSince19700101 + EPOCH_DIFF;
1084 return msSince16010101 * (1000 * 10);
1085 }
1086
1087 /**
1088 * Creates an URL Link on the Windows Desktop. This is done by creating a
1089 * file (URL File Format) with an ".url" extension. For a description see
1090 * http://www.cyanwerks.com/file-format-url.html .
1091 *
1092 * @param baseUrl
1093 * Base URL for the link - Cannot be <code>null</code> or empty.
1094 * @param url
1095 * Target URL - Cannot be <code>null</code> or empty.
1096 * @param workingDir
1097 * It's the "working folder" that your URL file uses. The working
1098 * folder is possibly the folder to be set as the current folder
1099 * for the application that would open the file. However Internet
1100 * Explorer does not seem to be affected by this field - Can be
1101 * <code>null</code>.
1102 * @param showCommand
1103 * Normal=<code>null</code>, Minimized=7, Maximized=3
1104 * @param iconIndex
1105 * The Icon Index within the icon library specified by IconFile.
1106 * In an icon library, which can be generally be either a ICO,
1107 * DLL or EXE file, the icons are indexed with numbers. The first
1108 * icon index starts at 0 - Can be <code>null</code> if the file
1109 * is not indexed.
1110 * @param iconFile
1111 * Specifies the path of the icon library file. Generally the
1112 * icon library can be an ICO, DLL or EXE file. The default icon
1113 * library used tends to be the URL.DLL library on the system's
1114 * Windows\System directory - Can be <code>null</code> if no icon
1115 * is required.
1116 * @param hotKey
1117 * The HotKey field specifies what is the shortcut key used to
1118 * automatically launch the Internet shortcut. The field uses a
1119 * number to specify what hotkey is used. To get the appropriate
1120 * code simply create a shortcut with MSIE and examine the file's
1121 * content.
1122 * @param linkFilenameWithoutExtension
1123 * Name for the link file (displayed as text) - Cannot be
1124 * <code>null</code> or empty.
1125 * @param overwrite
1126 * Overwrite an existing ".url" file.
1127 * @param modified
1128 * Timestamp.
1129 *
1130 * @throws IOException
1131 * Error writing the file.
1132 */
1133 // CHECKSTYLE:OFF Maximum Parameters
1134 public static void createWindowsDesktopUrlLink(final String baseUrl,
1135 final String url, final File workingDir, final Integer showCommand,
1136 final Integer iconIndex, final File iconFile, final Integer hotKey,
1137 final String linkFilenameWithoutExtension, final boolean overwrite,
1138 final Date modified) throws IOException {
1139 // CHECKSTYLE:ON
1140
1141 checkNotNull("baseUrl", baseUrl);
1142 checkNotEmpty("baseUrl", baseUrl);
1143 checkNotNull("url", url);
1144 checkNotEmpty("url", url);
1145 checkNotNull("linkFilenameWithoutExtension",
1146 linkFilenameWithoutExtension);
1147 checkNotEmpty("linkFilenameWithoutExtension",
1148 linkFilenameWithoutExtension);
1149
1150 final File userHomeDir = new File(System.getProperty("user.home"));
1151 final File desktopDir = new File(userHomeDir, "Desktop");
1152 final File linkFile = new File(desktopDir, linkFilenameWithoutExtension
1153 + ".url");
1154 if (linkFile.exists() && !overwrite) {
1155 // Do nothing
1156 return;
1157 }
1158 final String content = createWindowsDesktopUrlLinkContent(baseUrl, url,
1159 workingDir, showCommand, iconIndex, iconFile, hotKey, modified);
1160 final Writer writer = new BufferedWriter(new OutputStreamWriter(
1161 new FileOutputStream(linkFile), "Cp1252"));
1162 try {
1163 writer.write(content);
1164 } finally {
1165 writer.close();
1166 }
1167
1168 }
1169
1170 /**
1171 * Creates the content of an URL Link file (.url) on the Windows Desktop.
1172 * For a description see http://www.cyanwerks.com/file-format-url.html .
1173 *
1174 * @param baseUrl
1175 * Base URL for the link - Cannot be <code>null</code> or empty.
1176 * @param url
1177 * Target URL - Cannot be <code>null</code> or empty.
1178 * @param workingDir
1179 * It's the "working folder" that your URL file uses. The working
1180 * folder is possibly the folder to be set as the current folder
1181 * for the application that would open the file. However Internet
1182 * Explorer does not seem to be affected by this field - Can be
1183 * <code>null</code>.
1184 * @param showCommand
1185 * Normal=<code>null</code>, Minimized=7, Maximized=3
1186 * @param iconIndex
1187 * The Icon Index within the icon library specified by IconFile.
1188 * In an icon library, which can be generally be either a ICO,
1189 * DLL or EXE file, the icons are indexed with numbers. The first
1190 * icon index starts at 0 - Can be <code>null</code> if the file
1191 * is not indexed.
1192 * @param iconFile
1193 * Specifies the path of the icon library file. Generally the
1194 * icon library can be an ICO, DLL or EXE file. The default icon
1195 * library used tends to be the URL.DLL library on the system's
1196 * Windows\System directory - Can be <code>null</code> if no icon
1197 * is required.
1198 * @param hotKey
1199 * The HotKey field specifies what is the shortcut key used to
1200 * automatically launch the Internet shortcut. The field uses a
1201 * number to specify what hotkey is used. To get the appropriate
1202 * code simply create a shortcut with MSIE and examine the file's
1203 * content.
1204 * @param modified
1205 * Timestamp.
1206 *
1207 * @return INI file text.
1208 */
1209 // CHECKSTYLE:OFF
1210 public static String createWindowsDesktopUrlLinkContent(
1211 final String baseUrl, final String url, final File workingDir,
1212 final Integer showCommand, final Integer iconIndex,
1213 final File iconFile, final Integer hotKey, final Date modified) {
1214 // CHECKSTYLE:ON
1215
1216 checkNotNull("baseUrl", baseUrl);
1217 checkNotEmpty("baseUrl", baseUrl);
1218 checkNotNull("url", url);
1219 checkNotEmpty("url", url);
1220
1221 final StringBuffer sb = new StringBuffer();
1222 sb.append("[DEFAULT]\r\n");
1223 sb.append("BASEURL=" + baseUrl + "\r\n");
1224 sb.append("\r\n");
1225 sb.append("[InternetShortcut]\r\n");
1226 sb.append("URL=" + url + "\r\n");
1227 if (workingDir != null) {
1228 sb.append("WorkingDirectory=" + workingDir + "\r\n");
1229 }
1230 if (showCommand != null) {
1231 sb.append("ShowCommand=" + showCommand);
1232 }
1233 if ((iconFile != null) && (iconFile.exists())) {
1234 if (iconIndex == null) {
1235 sb.append("IconIndex=0\r\n");
1236 } else {
1237 sb.append("IconIndex=" + iconIndex);
1238 }
1239 sb.append("IconFile=" + iconFile + "\r\n");
1240 }
1241 sb.append("Modified=" + dateToFileTime(new Date()) + "\r\n");
1242 if (hotKey != null) {
1243 sb.append("HotKey=" + hotKey + "\r\n");
1244 }
1245 return sb.toString();
1246 }
1247
1248 /**
1249 * Concatenate a path and a filename taking <code>null</code> and empty
1250 * string values into account.
1251 *
1252 * @param path
1253 * Path - Can be <code>null</code> or an empty string.
1254 * @param filename
1255 * Filename - Cannot be <code>null</code>.
1256 * @param separator
1257 * Separator for directories - Can be <code>null</code> or an
1258 * empty string.
1259 *
1260 * @return Path and filename divided by the separator.
1261 */
1262 public static String concatPathAndFilename(final String path,
1263 final String filename, final String separator) {
1264
1265 checkNotNull("filename", filename);
1266 checkNotNull("separator", separator);
1267 checkNotEmpty("separator", separator);
1268
1269 if (path == null) {
1270 return filename;
1271 }
1272 final String trimmedPath = path.trim();
1273 if (trimmedPath.length() == 0) {
1274 return filename;
1275 }
1276 final String trimmedFilename = filename.trim();
1277 if (trimmedPath.endsWith(separator)) {
1278 return trimmedPath + trimmedFilename;
1279 }
1280 return trimmedPath + separator + trimmedFilename;
1281
1282 }
1283
1284 /**
1285 * Converts an array of bytes into an array of characters representing the
1286 * hexidecimal values of each byte in order. The returned array will be
1287 * double the length of the passed array, as it takes two characters to
1288 * represent any given byte.
1289 *
1290 * Author: Apache Software Foundation
1291 * See: org.apache.commons.codec.binary.Hex
1292 *
1293 * @param data
1294 * A byte[] to convert to Hex characters - Cannot be
1295 * <code>null</code>.
1296 *
1297 * @return A string containing hexidecimal characters
1298 */
1299 public static String encodeHex(final byte[] data) {
1300
1301 checkNotNull("data", data);
1302
1303 final int l = data.length;
1304 final char[] out = new char[l << 1];
1305 // two characters form the hex value.
1306 int j = 0;
1307 for (int i = 0; i < l; i++) {
1308 out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
1309 out[j++] = DIGITS[0x0F & data[i]];
1310 }
1311 return String.copyValueOf(out);
1312 }
1313
1314 /**
1315 * Converts an array of characters representing hexidecimal values into an
1316 * array of bytes of those same values. The returned array will be half the
1317 * length of the passed array, as it takes two characters to represent any
1318 * given byte. An exception is thrown if the passed char array has an odd
1319 * number of elements.
1320 *
1321 * @param data
1322 * An array of characters containing hexidecimal digits - Cannot
1323 * be <code>null</code>.
1324 *
1325 * @return A byte array containing binary data decoded from the supplied
1326 * char array.
1327 *
1328 * Author: Apache Software Foundation
1329 * See: org.apache.commons.codec.binary.Hex
1330 */
1331 public static byte[] decodeHex(final String data) {
1332
1333 checkNotNull("data", data);
1334
1335 final int len = data.length();
1336
1337 if ((len & 0x01) != 0) {
1338 throw new RuntimeException("Odd number of characters.");
1339 }
1340
1341 final byte[] out = new byte[len >> 1];
1342
1343 // two characters form the hex value.
1344 for (int i = 0, j = 0; j < len; i++) {
1345 int f = toDigit(data.charAt(j), j) << 4;
1346 j++;
1347 f = f | toDigit(data.charAt(j), j);
1348 j++;
1349 out[i] = (byte) (f & 0xFF);
1350 }
1351
1352 return out;
1353 }
1354
1355 /**
1356 * Converts a hexadecimal character to an integer.
1357 *
1358 * @param ch
1359 * A character to convert to an integer digit
1360 * @param index
1361 * The index of the character in the source
1362 * @return An integer
1363 *
1364 * @author Apache Software Foundation
1365 * @see org.apache.commons.codec.binary.Hex
1366 */
1367 private static int toDigit(final char ch, final int index) {
1368 final int digit = Character.digit(ch, 16);
1369 if (digit == -1) {
1370 throw new RuntimeException("Illegal hexadecimal charcter " + ch
1371 + " at index " + index);
1372 }
1373 return digit;
1374 }
1375
1376 /**
1377 * Lock the file.
1378 *
1379 * @param file
1380 * File to lock - Cannot be <code>null</code>.
1381 * @param tryLockMax
1382 * Number of tries to lock before throwing an exception.
1383 * @param tryWaitMillis
1384 * Milliseconds to sleep between retries.
1385 *
1386 * @return FileLock.
1387 *
1388 * @throws LockingFailedException
1389 * Locking the file failed.
1390 */
1391 public static FileLock lockRandomAccessFile(final RandomAccessFile file,
1392 final int tryLockMax, final long tryWaitMillis)
1393 throws LockingFailedException {
1394
1395 checkNotNull("file", file);
1396
1397 final FileChannel channel = file.getChannel();
1398
1399 int tryCount = 0;
1400 while (tryCount < tryLockMax) {
1401 tryCount++;
1402 try {
1403 final FileLock lock = channel.tryLock();
1404 if (lock != null) {
1405 return lock;
1406 }
1407 } catch (final IOException ex) {
1408 throw new LockingFailedException("Unexpected I/O-Exception!",
1409 ex);
1410 } catch (final OverlappingFileLockException ex) {
1411 ignore();
1412 }
1413 try {
1414 Thread.sleep(tryWaitMillis);
1415 } catch (final InterruptedException ex) {
1416 throw new LockingFailedException("Unexpected interrupt!", ex);
1417 }
1418 }
1419 throw new LockingFailedException("Number of max tries (" + tryLockMax
1420 + ") exceeded!");
1421
1422 }
1423
1424 private static void ignore() {
1425 // Do nothing - Just to document rare empty catches.
1426 }
1427
1428 /**
1429 * Adds a file to a ZIP output stream.
1430 *
1431 * @param srcFile
1432 * File to add - Cannot be <code>null</code>.
1433 * @param destPath
1434 * Path to use for the file - May be <code>null</code> or empty.
1435 * @param out
1436 * Destination stream - Cannot be <code>null</code>.
1437 *
1438 * @throws IOException
1439 * Error writing to the output stream.
1440 */
1441 private static void zipFile(final File srcFile, final String destPath,
1442 final ZipOutputStream out) throws IOException {
1443
1444 final byte[] buf = new byte[1024];
1445 final InputStream in = new BufferedInputStream(new FileInputStream(
1446 srcFile));
1447 try {
1448 out.putNextEntry(new ZipEntry(concatPathAndFilename(destPath,
1449 srcFile.getName(), "/")));
1450 int len;
1451 while ((len = in.read(buf)) > 0) {
1452 out.write(buf, 0, len);
1453 }
1454 out.closeEntry();
1455 } finally {
1456 in.close();
1457 }
1458 }
1459
1460 /**
1461 * List all files for a directory.
1462 *
1463 * @param srcDir
1464 * Directory to list the files for - Cannot be <code>null</code>
1465 * and must be a valid directory.
1466 * @param filter
1467 * Filter or <code>null</code> for all files.
1468 *
1469 * @return List of child entries of the directory.
1470 */
1471 private static File[] listFiles(final File srcDir, final FileFilter filter) {
1472
1473 final File[] files;
1474 if (filter == null) {
1475 files = srcDir.listFiles();
1476 } else {
1477 files = srcDir.listFiles(filter);
1478 }
1479 return files;
1480
1481 }
1482
1483 /**
1484 * Add a directory to a ZIP output stream.
1485 *
1486 * @param srcDir
1487 * Directory to add - Cannot be <code>null</code> and must be a
1488 * valid directory.
1489 * @param filter
1490 * Filter or <code>null</code> for all files.
1491 * @param destPath
1492 * Path to use for the ZIP archive - May be <code>null</code> or
1493 * an empyt string.
1494 * @param out
1495 * Destination stream - Cannot be <code>null</code>.
1496 *
1497 * @throws IOException
1498 * Error writing to the output stream.
1499 */
1500 private static void zipDir(final File srcDir, final FileFilter filter,
1501 final String destPath, final ZipOutputStream out)
1502 throws IOException {
1503
1504
1505 final File[] files = listFiles(srcDir, filter);
1506 for (int i = 0; i < files.length; i++) {
1507 if (files[i].isDirectory()) {
1508 /*
1509 zipDir(files[i], filter, concatPathAndFilename(destPath,
1510 files[i].getName(), "/"), out);*/
1511
1512 final File[] tempFiles = files[i].listFiles();
1513 if (tempFiles.length != 0) {
1514 zipDir(files[i], filter, concatPathAndFilename(destPath,
1515 files[i].getName(), "/"), out);
1516 } else {
1517 try {
1518 ZipEntry dirEntry = new ZipEntry(concatPathAndFilename(destPath, files[i].getName(), "/") + "/");
1519 out.putNextEntry(dirEntry);
1520 out.closeEntry();
1521 }
1522 catch (IOException e) {
1523 e.printStackTrace();
1524 }
1525 }
1526
1527 } else {
1528 zipFile(files[i], destPath, out);
1529 }
1530 }
1531 }
1532
1533 /**
1534 * Creates a ZIP file and adds all files in a directory and all it's sub
1535 * directories to the archive. Only entries are added that comply to the
1536 * file filter.
1537 *
1538 * @param srcDir
1539 * Directory to add - Cannot be <code>null</code> and must be a
1540 * valid directory.
1541 * @param filter
1542 * Filter or <code>null</code> for all files/directories.
1543 * @param destPath
1544 * Path to use for the ZIP archive - May be <code>null</code> or
1545 * an empyt string.
1546 * @param destFile
1547 * Target ZIP file - Cannot be <code>null</code>.
1548 *
1549 * @throws IOException
1550 * Error writing to the output stream.
1551 */
1552 public static void zipDir(final File srcDir, final FileFilter filter,
1553 final String destPath, final File destFile) throws IOException {
1554
1555 Utils4JJ.checkNotNull("srcDir", srcDir);
1556 Utils4JJ.checkValidDir(srcDir);
1557 Utils4JJ.checkNotNull("destFile", destFile);
1558
1559 final ZipOutputStream out = new ZipOutputStream(
1560 new BufferedOutputStream(new FileOutputStream(destFile)));
1561 try {
1562 zipDir(srcDir, filter, destPath, out);
1563 } finally {
1564 out.close();
1565 }
1566
1567 }
1568
1569 /**
1570 * Creates a ZIP file and adds all files in a directory and all it's sub
1571 * directories to the archive.
1572 *
1573 * @param srcDir
1574 * Directory to add - Cannot be <code>null</code> and must be a
1575 * valid directory.
1576 * @param destPath
1577 * Path to use for the ZIP archive - May be <code>null</code> or
1578 * an empyt string.
1579 * @param destFile
1580 * Target ZIP file - Cannot be <code>null</code>.
1581 *
1582 * @throws IOException
1583 * Error writing to the output stream.
1584 */
1585 public static void zipDir(final File srcDir, final String destPath,
1586 final File destFile) throws IOException {
1587
1588 zipDir(srcDir, null, destPath, destFile);
1589
1590 }
1591
1592 /**
1593 * Wraps a given input stream into another one an returns it.
1594 */
1595 public static interface UnzipInputStreamWrapper {
1596
1597 /**
1598 * Wraps the input stream into another one.
1599 *
1600 * @param in
1601 * Stream to create a wrapping stream for.
1602 * @param entry
1603 * Zip entry the input stream is reading.
1604 * @param destFile
1605 * Destination file.
1606 *
1607 * @return Wrapped input stream.
1608 */
1609 public InputStream wrapInputStream(InputStream in, ZipEntry entry,
1610 File destFile);
1611 }
1612}
Note: See TracBrowser for help on using the repository browser.