1 | /* This file is part of the project "Hilbert II" - http://www.qedeq.org |
2 | * |
3 | * Copyright 2000-2014, Michael Meyling <mime@qedeq.org>. |
4 | * |
5 | * "Hilbert II" is free software; you can redistribute |
6 | * it and/or modify it under the terms of the GNU General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | */ |
15 | |
16 | package org.qedeq.base.io; |
17 | |
18 | import java.io.BufferedOutputStream; |
19 | import java.io.BufferedReader; |
20 | import java.io.BufferedWriter; |
21 | import java.io.ByteArrayInputStream; |
22 | import java.io.File; |
23 | import java.io.FileFilter; |
24 | import java.io.FileInputStream; |
25 | import java.io.FileOutputStream; |
26 | import java.io.FileReader; |
27 | import java.io.FileWriter; |
28 | import java.io.IOException; |
29 | import java.io.InputStream; |
30 | import java.io.InputStreamReader; |
31 | import java.io.OutputStream; |
32 | import java.io.OutputStreamWriter; |
33 | import java.io.Reader; |
34 | import java.io.UnsupportedEncodingException; |
35 | import java.io.Writer; |
36 | import java.net.URL; |
37 | import java.nio.charset.Charset; |
38 | import java.util.ArrayList; |
39 | import java.util.Arrays; |
40 | import java.util.Enumeration; |
41 | import java.util.Iterator; |
42 | import java.util.List; |
43 | import java.util.Map; |
44 | import java.util.Properties; |
45 | import java.util.StringTokenizer; |
46 | import java.util.TreeMap; |
47 | |
48 | import org.apache.commons.lang.SystemUtils; |
49 | |
50 | |
51 | /** |
52 | * A collection of useful static methods for input and output. |
53 | * |
54 | * LATER mime 20070101: use StringBuilder instead of StringBuffer if working under JDK 1.5 |
55 | * |
56 | * @author Michael Meyling |
57 | */ |
58 | public final class IoUtility { |
59 | |
60 | /** |
61 | * Constructor, should never be called. |
62 | */ |
63 | private IoUtility() { |
64 | // don't call me |
65 | } |
66 | |
67 | /** |
68 | * Get default encoding for this system. |
69 | * |
70 | * @return Default encoding for this system. |
71 | */ |
72 | public static String getDefaultEncoding() { |
73 | return SystemUtils.FILE_ENCODING; |
74 | // mime 20090630: under ubuntu the following gave the encoding ASCII: |
75 | // return new InputStreamReader( |
76 | // new ByteArrayInputStream(new byte[0])).getEncoding(); |
77 | // but it was: file.encoding="ANSI_X3.41968" |
78 | } |
79 | |
80 | /** |
81 | * Get working Java encoding. |
82 | * |
83 | * @param encoding Try this encoding. |
84 | * @return This is <code>encoding</code> if it is supported. Or an other |
85 | * encoding that is supported by this system. |
86 | */ |
87 | public static String getWorkingEncoding(final String encoding) { |
88 | if (encoding != null) { |
89 | try { |
90 | if (Charset.isSupported(encoding) |
91 | && Charset.forName(encoding).canEncode()) { |
92 | return encoding; |
93 | } |
94 | } catch (RuntimeException e) { |
95 | // ignore |
96 | } |
97 | } |
98 | // we must inform someone, but using |
99 | // Trace within this class is not wise, because it is used |
100 | // before the Trace is initialized. |
101 | System.err.println("not supported encoding: " + encoding); |
102 | return "ISO-8859-1"; // every system must support this |
103 | } |
104 | |
105 | /** |
106 | * Reads a file and returns the contents as a <code>String</code>. |
107 | * |
108 | * @param filename Name of the file (could include path). |
109 | * @param encoding Take this encoding. |
110 | * @return Contents of file. |
111 | * @throws IOException File exception occurred. |
112 | */ |
113 | public static String loadFile(final String filename, final String encoding) |
114 | throws IOException { |
115 | |
116 | final StringBuffer buffer = new StringBuffer(); |
117 | loadFile(filename, buffer, encoding); |
118 | return buffer.toString(); |
119 | } |
120 | |
121 | /** |
122 | * Reads contents of a file into a string buffer. |
123 | * |
124 | * @param filename Name of the file (could include path). |
125 | * @param buffer Buffer to fill with file contents. |
126 | * @param encoding Take this encoding. |
127 | * @throws IOException File exception occurred. |
128 | */ |
129 | public static void loadFile(final String filename, |
130 | final StringBuffer buffer, final String encoding) |
131 | throws IOException { |
132 | loadFile(new File(filename), buffer, encoding); |
133 | } |
134 | |
135 | /** |
136 | * Reads contents of a stream into a string buffer. Stream is not closed. |
137 | * |
138 | * @param in This stream will be loaded. |
139 | * @param buffer Buffer to fill with file contents. |
140 | * @throws IOException File exception occurred. |
141 | * |
142 | * @deprecated Use {@link #loadReader(Reader, StringBuffer)}. |
143 | */ |
144 | public static void loadStream(final InputStream in, final StringBuffer buffer) |
145 | throws IOException { |
146 | |
147 | buffer.setLength(0); |
148 | int c; |
149 | while ((c = in.read()) >= 0) { |
150 | buffer.append((char) c); |
151 | } |
152 | } |
153 | |
154 | /** |
155 | * Returns contents of a stream into a string, respecting a maximum length. |
156 | * No exceptions are thrown. Stream is not closed. |
157 | * |
158 | * @param in This stream will be loaded. |
159 | * @param maxLength This length is not exceeded. |
160 | * @return readData Data read, is not <code>null</code>. |
161 | */ |
162 | public static String loadStreamWithoutException(final InputStream in, final int maxLength) { |
163 | |
164 | if (in == null) { |
165 | return ""; |
166 | } |
167 | final StringBuffer buffer = new StringBuffer(); |
168 | buffer.setLength(0); |
169 | try { |
170 | int counter = 0; |
171 | int c; |
172 | while (counter++ < maxLength) { |
173 | c = in.read(); |
174 | if (c < 0) { |
175 | break; |
176 | } |
177 | buffer.append((char) c); |
178 | } |
179 | } catch (IOException e) { |
180 | // ignored |
181 | } catch (RuntimeException e) { |
182 | // ignored |
183 | } |
184 | return buffer.toString(); |
185 | } |
186 | |
187 | /** |
188 | * Reads contents of a {@link Reader} into a string buffer. Reader is not closed. |
189 | * |
190 | * @param in This reader will be loaded. |
191 | * @param buffer Buffer to fill with file contents. |
192 | * @throws IOException File exception occurred. |
193 | */ |
194 | public static void loadReader(final Reader in, final StringBuffer buffer) |
195 | throws IOException { |
196 | |
197 | buffer.setLength(0); |
198 | int c; |
199 | while ((c = in.read()) >= 0) { |
200 | buffer.append((char) c); |
201 | } |
202 | } |
203 | |
204 | /** |
205 | * Reads contents of a file into a string buffer. Uses default encoding. |
206 | * |
207 | * @param file This file will be loaded. |
208 | * @param buffer Buffer to fill with file contents. |
209 | * @throws IOException File exception occurred. |
210 | * |
211 | * @deprecated Use {@link #loadFile(File, StringBuffer, String)}. |
212 | */ |
213 | public static void loadFile(final File file, |
214 | final StringBuffer buffer) |
215 | throws IOException { |
216 | |
217 | final int size = (int) file.length(); |
218 | final char[] data = new char[size]; |
219 | buffer.setLength(0); |
220 | FileReader in = null; |
221 | try { |
222 | in = new FileReader(file); |
223 | int charsread = 0; |
224 | while (charsread < size) { |
225 | charsread += in.read(data, charsread, size - charsread); |
226 | } |
227 | } finally { |
228 | close(in); |
229 | } |
230 | buffer.insert(0, data); |
231 | } |
232 | |
233 | /** |
234 | * Reads contents of a file into a string buffer. |
235 | * |
236 | * @param file This file will be loaded. |
237 | * @param buffer Buffer to fill with file contents. |
238 | * @param encoding Take this encoding. |
239 | * @throws IOException File exception occurred. |
240 | */ |
241 | public static void loadFile(final File file, |
242 | final StringBuffer buffer, final String encoding) |
243 | throws IOException { |
244 | |
245 | buffer.setLength((int) file.length()); // ensure capacity |
246 | buffer.setLength(0); |
247 | final InputStreamReader in = new InputStreamReader(new FileInputStream(file), encoding); |
248 | final char[] data = new char[10 * 1024]; |
249 | |
250 | try { |
251 | int charsread = 0; |
252 | while (0 < (charsread = in.read(data, 0, data.length))) { |
253 | buffer.append(data, 0, charsread); |
254 | } |
255 | } finally { |
256 | in.close(); |
257 | } |
258 | } |
259 | |
260 | /** |
261 | * Reads a file and returns the contents as a <code>String</code>. |
262 | * |
263 | * @param file File to load from. |
264 | * @return Contents of file. |
265 | * @throws IOException File exception occurred. |
266 | */ |
267 | public static final byte[] loadFileBinary(final File file) throws IOException { |
268 | final int size = (int) file.length(); |
269 | final FileInputStream in = new FileInputStream(file); |
270 | try { |
271 | final byte[] data = new byte[size]; |
272 | int charsread = 0; |
273 | while (charsread < size) { |
274 | final int read = in.read(data, charsread, size - charsread); |
275 | if (read == -1) { |
276 | final byte[] result = new byte[charsread]; |
277 | System.arraycopy(data, 0, result, 0, charsread); |
278 | return result; |
279 | } |
280 | charsread += read; |
281 | } |
282 | in.close(); |
283 | return data; |
284 | } finally { |
285 | close(in); |
286 | } |
287 | } |
288 | |
289 | |
290 | /** |
291 | * Reads contents of an URL into a string buffer. The filling is character set dependent. |
292 | * Content is added to the end of buffer. (Existing data is not cleared.) |
293 | * <p> |
294 | * All parameters should not be <code>null</code>. |
295 | * @param url This URL will be loaded. |
296 | * @param buffer Buffer to fill with file contents. |
297 | * @throws IOException Reading failed. |
298 | * |
299 | * @deprecated Choose correct encoding. |
300 | */ |
301 | public static void loadFile(final URL url, final StringBuffer buffer) throws IOException { |
302 | InputStream in = null; |
303 | BufferedReader dis = null; |
304 | try { |
305 | in = url.openStream(); |
306 | dis = new BufferedReader(new InputStreamReader(in)); |
307 | int i; |
308 | while ((i = dis.read()) != -1) { |
309 | buffer.append((char) i); |
310 | } |
311 | } finally { |
312 | close(in); |
313 | close(dis); |
314 | } |
315 | } |
316 | |
317 | /** |
318 | * Reads contents of an URL into a StringBuffer. The filling is character set dependent. The |
319 | * buffer is not cleared, contents is just added. |
320 | * <p> |
321 | * All parameters should not be <code>null</code>. |
322 | * @param url This URL will be loaded. |
323 | * @param buffer Buffer to fill with file contents. |
324 | * @param encoding Take this encoding. |
325 | * @throws IOException Reading failed. |
326 | */ |
327 | public static void loadFile(final URL url, final StringBuffer buffer, final String encoding) |
328 | throws IOException { |
329 | InputStream in = null; |
330 | BufferedReader dis = null; |
331 | try { |
332 | in = url.openStream(); |
333 | dis = new BufferedReader(new InputStreamReader(in, encoding)); |
334 | int i; |
335 | while ((i = dis.read()) != -1) { |
336 | buffer.append((char) i); |
337 | } |
338 | } finally { |
339 | close(in); |
340 | close(dis); |
341 | } |
342 | } |
343 | |
344 | /** |
345 | * Save binary contents of an URL into a file. Existing files are overwritten. |
346 | * |
347 | * @param url This URL will be loaded. |
348 | * @param file Write into this file. |
349 | * @throws IOException Reading or writing failed. |
350 | */ |
351 | public static void saveFile(final URL url, final File file) throws IOException { |
352 | saveFile(url.openStream(), file); |
353 | } |
354 | |
355 | /** |
356 | * Save binary contents of an input stream into a file. The input stream is closed even |
357 | * if exceptions occur. Existing files are overwritten. |
358 | * @param in Read this stream. |
359 | * @param file Write into this file. |
360 | * |
361 | * @throws IOException Reading or writing failed. |
362 | */ |
363 | public static void saveFile(final InputStream in, final File file) throws IOException { |
364 | createNecessaryDirectories(file); |
365 | FileOutputStream out = null; |
366 | try { |
367 | out = new FileOutputStream(file); |
368 | final byte[] data = new byte[8 * 1024]; |
369 | int length; |
370 | while ((length = in.read(data)) != -1) { |
371 | out.write(data, 0, length); |
372 | } |
373 | } finally { |
374 | close(in); |
375 | close(out); |
376 | } |
377 | } |
378 | |
379 | /** |
380 | * Convert String into a {@link Reader}. |
381 | * |
382 | * <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4094886"> |
383 | * Bug ID: 4094886</a> |
384 | * |
385 | * @param data Convert this. |
386 | * @return Resulting reader. |
387 | */ |
388 | public static final Reader stringToReader(final String data) { |
389 | try { |
390 | return new InputStreamReader(new ByteArrayInputStream(data.getBytes("ISO-8859-1"))); |
391 | } catch (UnsupportedEncodingException e) { |
392 | // should never occur |
393 | throw new RuntimeException(e); |
394 | } |
395 | } |
396 | |
397 | /** |
398 | * Saves a <code>String</code> into a file. Existing files are overwritten. |
399 | * |
400 | * @param filename Name of the file (could include path). |
401 | * @param text Data to save in the file. |
402 | * @throws IOException File exception occurred. |
403 | * |
404 | * @deprecated Use {@link #saveFile(File, String, String)} that has an encoding. |
405 | */ |
406 | public static void saveFile(final String filename, final String text) |
407 | throws IOException { |
408 | saveFile(new File(filename), text); |
409 | } |
410 | |
411 | /** |
412 | * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten. |
413 | * |
414 | * @param filename Name of the file (could include path). |
415 | * @param text Data to save in the file. |
416 | * @throws IOException File exception occurred. |
417 | * |
418 | * @deprecated Use {@link #saveFile(File, StringBuffer, String)} that has an encoding. |
419 | */ |
420 | public static void saveFile(final String filename, final StringBuffer text) |
421 | throws IOException { |
422 | saveFile(new File(filename), text.toString()); |
423 | } |
424 | |
425 | /** |
426 | * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten. |
427 | * |
428 | * @param file File to save into. |
429 | * @param text Data to save in the file. |
430 | * @throws IOException File exception occurred. |
431 | * |
432 | * @deprecated Use {@link #saveFile(File, StringBuffer, String)} that has an encoding |
433 | * parameter. |
434 | */ |
435 | public static void saveFile(final File file, final StringBuffer text) |
436 | throws IOException { |
437 | saveFile(file, text.toString()); |
438 | } |
439 | |
440 | /** |
441 | * Saves a <code>String</code> in a file. Uses default encoding. Existing files are |
442 | * overwritten. |
443 | * |
444 | * @param file File to save the data in. |
445 | * @param text Data to save in the file. |
446 | * @throws IOException File exception occurred. |
447 | * |
448 | * @deprecated Use {@link #saveFile(File, String, String)} that has an encoding parameter. |
449 | */ |
450 | public static void saveFile(final File file, final String text) |
451 | throws IOException { |
452 | createNecessaryDirectories(file); |
453 | BufferedWriter out = null; |
454 | try { |
455 | out = new BufferedWriter(new FileWriter(file)); |
456 | out.write(text); |
457 | } finally { |
458 | close(out); |
459 | } |
460 | } |
461 | |
462 | /** |
463 | * Saves a <code>String</code> in a file. Existing files are overwritten. |
464 | * |
465 | * @param file File to save the data in. |
466 | * @param text Data to save in the file. |
467 | * @param encoding Use this encoding. |
468 | * @throws IOException File exception occurred. |
469 | */ |
470 | public static void saveFile(final File file, final StringBuffer text, final String encoding) |
471 | throws IOException { |
472 | saveFile(file, text.toString(), encoding); |
473 | } |
474 | |
475 | /** |
476 | * Saves a <code>String</code> in a file. |
477 | * |
478 | * @param file File to save the data in. |
479 | * @param text Data to save in the file. |
480 | * @param encoding Use this encoding. |
481 | * @throws IOException File exception occurred. |
482 | */ |
483 | public static void saveFile(final File file, final String text, final String encoding) |
484 | throws IOException { |
485 | createNecessaryDirectories(file); |
486 | BufferedWriter out = new BufferedWriter( |
487 | new OutputStreamWriter(new FileOutputStream(file), encoding)); |
488 | try { |
489 | out.write(text); |
490 | } finally { |
491 | out.close(); |
492 | } |
493 | } |
494 | |
495 | /** |
496 | * Saves a <code>data</code> in a file. Existing files are overwritten. |
497 | * |
498 | * @param file File to save the data in. |
499 | * @param data Data to save in the file. |
500 | * @throws IOException File exception occurred. |
501 | */ |
502 | public static void saveFileBinary(final File file, final byte[] data) |
503 | throws IOException { |
504 | createNecessaryDirectories(file); |
505 | BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); |
506 | try { |
507 | out.write(data); |
508 | } finally { |
509 | out.close(); |
510 | } |
511 | } |
512 | |
513 | /** |
514 | * Copies a file to a different location. |
515 | * |
516 | * @param from Copy source. |
517 | * @param to Copy destination. |
518 | * @throws IOException File exception occurred. |
519 | */ |
520 | public static void copyFile(final File from, final File to) |
521 | throws IOException { |
522 | |
523 | if (from.getCanonicalFile().equals(to.getCanonicalFile())) { |
524 | return; |
525 | } |
526 | createNecessaryDirectories(to); |
527 | FileInputStream in = null; |
528 | FileOutputStream out = null; |
529 | try { |
530 | in = new FileInputStream(from); |
531 | out = new FileOutputStream(to); |
532 | |
533 | byte[] data = new byte[8 * 1024]; |
534 | int length; |
535 | while ((length = in.read(data)) != -1) { |
536 | out.write(data, 0, length); |
537 | } |
538 | } finally { |
539 | close(in); |
540 | close(out); |
541 | } |
542 | } |
543 | |
544 | /** |
545 | * Copy one file or directory to another location. |
546 | * If targetLocation does not exist, it will be created. |
547 | * |
548 | * @param sourceLocation Copy from here. This can be a file or a directory. |
549 | * @param targetLocation Copy to this location. If source is a file this must be a file too. |
550 | * @throws IOException Something went wrong. |
551 | */ |
552 | public static void copy(final String sourceLocation, final String targetLocation) |
553 | throws IOException { |
554 | copy(new File(sourceLocation), new File(targetLocation)); |
555 | } |
556 | |
557 | /** |
558 | * Copy one directory to another location. |
559 | * If targetLocation does not exist, it will be created. |
560 | * |
561 | * @param sourceLocation Copy from here. |
562 | * @param targetLocation Copy to this location |
563 | * @throws IOException Something went wrong. |
564 | */ |
565 | public static void copy(final File sourceLocation, final File targetLocation) |
566 | throws IOException { |
567 | |
568 | if (sourceLocation.isDirectory()) { |
569 | if (!targetLocation.exists()) { |
570 | targetLocation.mkdir(); |
571 | } |
572 | String[] children = sourceLocation.list(); |
573 | for (int i = 0; i < children.length; i++) { // recursive call for all children |
574 | copy(new File(sourceLocation, children[i]), |
575 | new File(targetLocation, children[i])); |
576 | } |
577 | } else { // copy file |
578 | copyFile(sourceLocation, targetLocation); |
579 | } |
580 | } |
581 | |
582 | /** |
583 | * List all matching files. Searches all matching sub directories recursively. |
584 | * Remember to return <code>true</code> for <code>accept(File pathname)</code> if |
585 | * <code>pathname</code> is a directory if you want to search all sub directories! |
586 | * If <code>sourceLocation</code> is a single file, this is the only file that will |
587 | * be in the resulting list. |
588 | * |
589 | * @param sourceLocation Check all files in this directory. (Or add this single file.) |
590 | * @param filter Accept only these directories and files. |
591 | * @return List of matching files. Contains no directories. |
592 | * @throws IOException Something went wrong. |
593 | */ |
594 | public static List listFilesRecursively(final File sourceLocation, final FileFilter filter) |
595 | throws IOException { |
596 | final List result = new ArrayList(); |
597 | if (sourceLocation.isDirectory()) { |
598 | final File[] children = sourceLocation.listFiles(); |
599 | for (int i = 0; i < children.length; i++) { // recursive call for all children |
600 | result.addAll(listFilesRecursivelyIntern(children[i], filter)); |
601 | } |
602 | } else { |
603 | result.add(sourceLocation); |
604 | } |
605 | return result; |
606 | } |
607 | |
608 | /** |
609 | * List all matching files. Searches all matching sub directories recursively. |
610 | * Remember to return <code>true</code> for <code>accept(File pathname)</code> if |
611 | * <code>pathname</code> is a directory if you want to search all sub directories! |
612 | * |
613 | * @param sourceLocation Check all files in this directory. |
614 | * @param filter Accept only these directories and files. |
615 | * @return List of matching files. Contains no directories. |
616 | * @throws IOException Something went wrong. |
617 | */ |
618 | private static List listFilesRecursivelyIntern(final File sourceLocation, |
619 | final FileFilter filter) throws IOException { |
620 | final List result = new ArrayList(); |
621 | if (filter.accept(sourceLocation)) { |
622 | if (sourceLocation.isDirectory()) { |
623 | File[] children = sourceLocation.listFiles(); |
624 | for (int i = 0; i < children.length; i++) { // recursive call for all children |
625 | result.addAll(listFilesRecursivelyIntern(children[i], filter)); |
626 | } |
627 | } else { |
628 | result.add(sourceLocation); |
629 | } |
630 | } |
631 | return result; |
632 | } |
633 | |
634 | /** |
635 | * Compare two files binary. |
636 | * |
637 | * @param from Compare source. This file must be <code>null</code> or be an existing file. |
638 | * @param with Compare with this file. This file must be <code>null</code> or be an |
639 | * existing file. |
640 | * @return Is the contents of the two files binary equal? |
641 | * @throws IOException File exception occurred. |
642 | */ |
643 | public static boolean compareFilesBinary(final File from, final File with) |
644 | throws IOException { |
645 | if (from == null && with == null) { |
646 | return true; |
647 | } |
648 | if (from == null || with == null) { |
649 | return false; |
650 | } |
651 | if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) { |
652 | return true; |
653 | } |
654 | if (from.length() != with.length()) { |
655 | return false; |
656 | } |
657 | byte[] dataOne = new byte[8 * 1024]; |
658 | byte[] dataTwo = new byte[8 * 1024]; |
659 | int length; |
660 | |
661 | FileInputStream one = null; |
662 | FileInputStream two = null; |
663 | try { |
664 | one = new FileInputStream(from); |
665 | two = new FileInputStream(with); |
666 | |
667 | while ((length = one.read(dataOne)) != -1) { |
668 | if (length != two.read(dataTwo)) { |
669 | return false; |
670 | } |
671 | if (!Arrays.equals(dataOne, dataTwo)) { |
672 | return false; |
673 | } |
674 | } |
675 | return true; |
676 | } finally { |
677 | close(one); |
678 | close(two); |
679 | } |
680 | } |
681 | |
682 | /** |
683 | * Compare two text files. Ignores different line separators. As there are: |
684 | * LF, CR, CR + LF, NEL, FF, LS, PS. |
685 | * |
686 | * @param from Compare source. |
687 | * @param with Compare with this file. |
688 | * @param encoding Use this character encoding. Must not be <code>null</code>. |
689 | * @return Is the contents of the two text files equal? |
690 | * @throws IOException File exception occurred or encoding is not supported. |
691 | * @throws NullPointerException Is encoding different from <code>null</code>? |
692 | */ |
693 | public static boolean compareTextFiles(final File from, final File with, final String encoding) |
694 | throws IOException { |
695 | if (from == null && with == null) { |
696 | return true; |
697 | } |
698 | if (from == null || with == null) { |
699 | return false; |
700 | } |
701 | if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) { |
702 | return true; |
703 | } |
704 | |
705 | BufferedReader one = null; |
706 | BufferedReader two = null; |
707 | FileInputStream fromIn = null; |
708 | FileInputStream withIn = null; |
709 | try { |
710 | fromIn = new FileInputStream(from); |
711 | one = new BufferedReader(new InputStreamReader(fromIn, encoding)); |
712 | withIn = new FileInputStream(with); |
713 | two = new BufferedReader(new InputStreamReader(withIn, encoding)); |
714 | |
715 | boolean crOne = false; |
716 | boolean crTwo = false; |
717 | do { |
718 | int readOne = one.read(); |
719 | int readTwo = two.read(); |
720 | if (readOne == readTwo) { |
721 | if (readOne < 0) { |
722 | break; |
723 | } |
724 | } else { |
725 | crOne = readOne == 0x0D; |
726 | crTwo = readTwo == 0x0D; |
727 | if (crOne) { |
728 | readOne = one.read(); |
729 | } |
730 | if (crTwo) { |
731 | readTwo = two.read(); |
732 | } |
733 | if (crOne && readOne != 0x0A && isCr(readTwo)) { |
734 | readTwo = two.read(); |
735 | } |
736 | if (crTwo && readTwo != 0x0A && isCr(readOne)) { |
737 | readOne = one.read(); |
738 | } |
739 | if (readOne != readTwo && (!isCr(readOne) && !isCr(readTwo))) { |
740 | return false; |
741 | } |
742 | } |
743 | } while (true); |
744 | return true; |
745 | } finally { |
746 | close(fromIn); |
747 | close(one); |
748 | close(two); |
749 | close(withIn); |
750 | } |
751 | } |
752 | |
753 | /** |
754 | * Compare two text files. Ignores different line separators. As there are: |
755 | * LF, CR, CR + LF |
756 | * |
757 | * @param from Compare source. |
758 | * @param with Compare with this file. |
759 | * @param startAtLine Start comparing at this line (beginning with 0). |
760 | * @param encoding Use this character encoding. Must not be <code>null</code>. |
761 | * @return Is the contents of the two text files equal? |
762 | * @throws IOException File exception occurred or encoding is not supported. |
763 | * @throws NullPointerException Is encoding different from <code>null</code>? |
764 | */ |
765 | public static boolean compareTextFiles(final File from, final File with, final int startAtLine, |
766 | final String encoding) throws IOException { |
767 | |
768 | if (from == null && with == null) { |
769 | return true; |
770 | } |
771 | if (from == null || with == null) { |
772 | return false; |
773 | } |
774 | if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) { |
775 | return true; |
776 | } |
777 | if (startAtLine < 0) { |
778 | return true; |
779 | } |
780 | BufferedReader one = null; |
781 | BufferedReader two = null; |
782 | FileInputStream fromIn = null; |
783 | FileInputStream withIn = null; |
784 | try { |
785 | fromIn = new FileInputStream(from); |
786 | one = new BufferedReader(new InputStreamReader(fromIn, encoding)); |
787 | withIn = new FileInputStream(with); |
788 | two = new BufferedReader(new InputStreamReader(withIn, encoding)); |
789 | int pos = 0; |
790 | do { |
791 | String lineOne = one.readLine(); |
792 | String lineTwo = two.readLine(); |
793 | if (lineOne == null) { |
794 | if (lineTwo == null) { |
795 | break; |
796 | } |
797 | return false; |
798 | } |
799 | if (pos++ >= startAtLine && !lineOne.equals(lineTwo)) { |
800 | return false; |
801 | } |
802 | } while (true); |
803 | return true; |
804 | } finally { |
805 | close(fromIn); |
806 | close(one); |
807 | close(two); |
808 | close(withIn); |
809 | } |
810 | } |
811 | |
812 | /** |
813 | * Test if character is LF, CR, NEL, FF, LS, PS. |
814 | * @param c Character to test. |
815 | * @return Is character a line terminator? |
816 | */ |
817 | private static boolean isCr(final int c) { |
818 | return c == 0x0A || c == 0x0D || c == 0x85 || c == 0x0C || c == 0x2028 || c == 0x2029; |
819 | } |
820 | |
821 | /** |
822 | * Delete file directory recursive. |
823 | * |
824 | * @param directory Directory to delete. Must not be a symbolic link. |
825 | * @param deleteDir Delete directory itself too? |
826 | * @return Was deletion successful? |
827 | */ |
828 | public static boolean deleteDir(final File directory, final boolean deleteDir) { |
829 | |
830 | // first we check if the file is a symbolic link |
831 | try { |
832 | if (isSymbolicLink(directory)) { |
833 | return false; |
834 | } |
835 | } catch (IOException e) { |
836 | return false; |
837 | } |
838 | final File candir; |
839 | try { |
840 | candir = directory.getCanonicalFile(); |
841 | } catch (IOException e) { |
842 | return false; |
843 | } |
844 | |
845 | // now we go through all of the files and subdirectories in the |
846 | // directory and delete them one by one |
847 | boolean success = true; |
848 | File[] files = candir.listFiles(); |
849 | if (files != null) { |
850 | for (int i = 0; i < files.length; i++) { |
851 | File file = files[i]; |
852 | |
853 | // in case this directory is actually a symbolic link, or it's |
854 | // empty, we want to try to delete the link before we try |
855 | // anything |
856 | boolean deleted = file.delete(); |
857 | if (!deleted) { |
858 | // deleting the file failed, so maybe it's a non-empty |
859 | // directory |
860 | if (file.isDirectory()) { |
861 | deleted = deleteDir(file, true); |
862 | } |
863 | |
864 | // otherwise, there's nothing else we can do |
865 | } |
866 | success = success && deleted; |
867 | } |
868 | } |
869 | |
870 | // now that we tried to clear the directory out, we can try to delete it |
871 | if (deleteDir && directory.exists()) { |
872 | return directory.delete(); |
873 | } |
874 | return success; |
875 | } |
876 | |
877 | /** |
878 | * Delete directory contents for all files that match the filter. The main directory itself is |
879 | * not deleted. |
880 | * |
881 | * @param directory Directory to scan for files to delete. |
882 | * @param filter Filter files (and directories) to delete. |
883 | * @return Was deletion successful? |
884 | */ |
885 | public static boolean deleteDir(final File directory, final FileFilter filter) { |
886 | // first we check if the file is a symbolic link |
887 | try { |
888 | if (isSymbolicLink(directory)) { |
889 | return false; |
890 | } |
891 | } catch (IOException e) { |
892 | return false; |
893 | } |
894 | final File candir; |
895 | try { |
896 | candir = directory.getCanonicalFile(); |
897 | } catch (IOException e) { |
898 | return false; |
899 | } |
900 | |
901 | // now we go through all of the files and subdirectories in the |
902 | // directory and delete them one by one |
903 | boolean success = true; |
904 | File[] files = candir.listFiles(filter); |
905 | if (files != null) { |
906 | for (int i = 0; i < files.length; i++) { |
907 | File file = files[i]; |
908 | |
909 | // in case this directory is actually a symbolic link, or it's |
910 | // empty, we want to try to delete the link before we try |
911 | // anything |
912 | boolean deleted = file.delete(); |
913 | if (!deleted) { |
914 | // deleting the file failed, so maybe it's a non-empty |
915 | // directory |
916 | if (file.isDirectory()) { |
917 | deleted = deleteDir(file, true); |
918 | } |
919 | |
920 | // otherwise, there's nothing else we can do |
921 | } |
922 | success = success && deleted; |
923 | } |
924 | } |
925 | |
926 | return success; |
927 | } |
928 | |
929 | /** |
930 | * Determines whether the specified file is a symbolic link rather than an actual file. |
931 | * See {@link |
932 | * https://svn.apache.org/repos/asf/commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java}. |
933 | * @param file File to check. |
934 | * @return Is the file is a symbolic link? |
935 | * @throws IOException IO error while checking the file. |
936 | */ |
937 | public static boolean isSymbolicLink(final File file) throws IOException { |
938 | if (file == null) { |
939 | throw new NullPointerException("File must not be null"); |
940 | } |
941 | // is windows file system in use? |
942 | if (File.separatorChar == '\\') { |
943 | // we have no symbolic links |
944 | return false; |
945 | } |
946 | File fileInCanonicalDir = null; |
947 | if (file.getParent() == null) { |
948 | fileInCanonicalDir = file; |
949 | } else { |
950 | File canonicalDir = file.getParentFile().getCanonicalFile(); |
951 | fileInCanonicalDir = new File(canonicalDir, file.getName()); |
952 | } |
953 | if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) { |
954 | return false; |
955 | } |
956 | return true; |
957 | } |
958 | |
959 | /** |
960 | * Print current system properties to System.out. |
961 | */ |
962 | public static void printAllSystemProperties() { |
963 | Properties sysprops = System.getProperties(); |
964 | for (Enumeration e = sysprops.propertyNames(); e.hasMoreElements(); ) { |
965 | String key = (String) e.nextElement(); |
966 | String value = sysprops.getProperty(key); |
967 | System.out.println(key + "=" + value); |
968 | } |
969 | } |
970 | |
971 | /** |
972 | * Get home directory of user. |
973 | * |
974 | * @return Home directory of user. |
975 | */ |
976 | public static File getUserHomeDirectory() { |
977 | return new File((String) System.getProperties().get("user.home")); |
978 | } |
979 | |
980 | /** |
981 | * Creates necessary parent directories for a file. |
982 | * |
983 | * @param file File. |
984 | * @throws IOException Creation failed. |
985 | */ |
986 | public static void createNecessaryDirectories(final File file) throws IOException { |
987 | if (file != null && file.getParentFile() != null) { |
988 | file.getParentFile().mkdirs(); |
989 | if (!file.getParentFile().exists()) { |
990 | throw new IOException("directory creation failed: " + file.getParent()); |
991 | } |
992 | } |
993 | } |
994 | |
995 | /** |
996 | * Create relative address from <code>origin</code> to <code>next</code>. |
997 | * The resulting file path has "/" as directory name separator. |
998 | * If the resulting file path is the same as origin specifies, we return "". |
999 | * Otherwise the result will always have an "/" as last character. |
1000 | * |
1001 | * @param origin This is the original location. Must be a directory. |
1002 | * @param next This should be the next location. Must also be a directory. |
1003 | * @return Relative (or if necessary absolute) file path. |
1004 | */ |
1005 | public static final String createRelativePath(final File origin, final File next) { |
1006 | if (origin.equals(next)) { |
1007 | return ""; |
1008 | } |
1009 | final Path org = new Path(origin.getPath().replace(File.separatorChar, '/'), ""); |
1010 | final Path ne = new Path(next.getPath().replace(File.separatorChar, '/'), ""); |
1011 | return org.createRelative(ne.toString()).toString(); |
1012 | } |
1013 | |
1014 | /** |
1015 | * Waits until a '\n' was read from System.in. |
1016 | */ |
1017 | public static void waitln() { |
1018 | System.out.println("\n..press <return> to continue"); |
1019 | try { |
1020 | (new java.io.BufferedReader(new java.io.InputStreamReader( |
1021 | System.in))).readLine(); |
1022 | } catch (IOException e) { |
1023 | // ignore |
1024 | } |
1025 | } |
1026 | |
1027 | /** |
1028 | * Closes input stream without exception. |
1029 | * |
1030 | * @param in Input stream, maybe <code>null</code>. |
1031 | */ |
1032 | public static void close(final InputStream in) { |
1033 | if (in != null) { |
1034 | try { |
1035 | in.close(); |
1036 | } catch (Exception e) { |
1037 | // ignore |
1038 | } |
1039 | } |
1040 | } |
1041 | |
1042 | /** |
1043 | * Closes writer without exception. |
1044 | * |
1045 | * @param writer Writer, maybe <code>null</code>. |
1046 | */ |
1047 | public static void close(final Writer writer) { |
1048 | if (writer != null) { |
1049 | try { |
1050 | writer.close(); |
1051 | } catch (Exception e) { |
1052 | // ignore |
1053 | } |
1054 | } |
1055 | } |
1056 | |
1057 | /** |
1058 | * Closes out stream without exception. |
1059 | * |
1060 | * @param out Output stream, maybe <code>null</code>. |
1061 | */ |
1062 | public static void close(final OutputStream out) { |
1063 | if (out != null) { |
1064 | try { |
1065 | out.close(); |
1066 | } catch (Exception e) { |
1067 | // ignore |
1068 | } |
1069 | } |
1070 | } |
1071 | |
1072 | /** |
1073 | * Closes input reader without exception. |
1074 | * |
1075 | * @param reader Reader, maybe <code>null</code>. |
1076 | */ |
1077 | public static void close(final Reader reader) { |
1078 | if (reader != null) { |
1079 | try { |
1080 | reader.close(); |
1081 | } catch (Exception e) { |
1082 | // ignore |
1083 | } |
1084 | } |
1085 | } |
1086 | |
1087 | /** |
1088 | * Get start directory for application. Within the start directory all newly created data is |
1089 | * stored in. If this is no Java Webstart version the result is |
1090 | * <code>new File(".")</code>. Otherwise the start directory is the subdirectory |
1091 | * "." concatenated <code>application</code> within <code>user.home</code>. |
1092 | * If a system property <code>application + ".startDirectory"</code> is defined we take this |
1093 | * as the start directory. |
1094 | * |
1095 | * @param application Application name, used for Java Webstart version. Should |
1096 | * be written in lowercase letters. A "." is automatically appended at |
1097 | * the beginning. |
1098 | * @return Start directory for application. |
1099 | */ |
1100 | public static final File getStartDirectory(final String application) { |
1101 | final File startDirectory; |
1102 | final String property = System.getProperty(application + ".startDirectory"); |
1103 | if (property != null && property.length() > 0) { |
1104 | startDirectory = new File(property); |
1105 | } else if (isWebStarted()) { |
1106 | startDirectory = new File(getUserHomeDirectory(), "." + application); |
1107 | } else { |
1108 | startDirectory = new File("."); |
1109 | } |
1110 | return startDirectory; |
1111 | } |
1112 | |
1113 | /** |
1114 | * Was the application started by Java Webstart? |
1115 | * |
1116 | * @return Was the application started by Java Webstart. |
1117 | */ |
1118 | public static final boolean isWebStarted() { |
1119 | final String webStart = (String) System.getProperties().get("javawebstart.version"); |
1120 | return webStart != null; |
1121 | } |
1122 | |
1123 | /** |
1124 | * Loads a property file from given URL. |
1125 | * |
1126 | * @param url URL to load properties from. Must not be <code>null</code>. |
1127 | * @return Loaded properties. |
1128 | * @throws IOException Reading error. |
1129 | */ |
1130 | public static Properties loadProperties(final URL url) |
1131 | throws IOException { |
1132 | Properties newprops = new Properties(); |
1133 | InputStream in = null; |
1134 | try { |
1135 | in = url.openStream(); |
1136 | newprops.load(in); |
1137 | } finally { |
1138 | close(in); |
1139 | } |
1140 | return newprops; |
1141 | } |
1142 | |
1143 | /** |
1144 | * Sleep my little class. |
1145 | * |
1146 | * @param ms Milliseconds to wait. |
1147 | */ |
1148 | public static void sleep(final int ms) { |
1149 | final Object monitor = new Object(); |
1150 | synchronized (monitor) { |
1151 | try { |
1152 | monitor.wait(ms); |
1153 | } catch (InterruptedException e) { |
1154 | } |
1155 | } |
1156 | } |
1157 | |
1158 | /** |
1159 | * Get currently running java version and subversion numbers. This is the running JRE version. |
1160 | * If no version could be identified <code>null</code> is returned. |
1161 | * |
1162 | * @return Array of version and subversion numbers. |
1163 | */ |
1164 | public static int[] getJavaVersion() { |
1165 | final String version = System.getProperty("java.version"); |
1166 | final List numbers = new ArrayList(); |
1167 | final StringTokenizer tokenizer = new StringTokenizer(version, "."); |
1168 | while (tokenizer.hasMoreElements()) { |
1169 | String sub = tokenizer.nextToken(); |
1170 | for (int i = 0; i < sub.length(); i++) { |
1171 | if (!Character.isDigit(sub.charAt(i))) { |
1172 | sub = sub.substring(0, i); |
1173 | break; |
1174 | } |
1175 | } |
1176 | try { |
1177 | numbers.add(new Integer(Integer.parseInt(sub))); |
1178 | } catch (Exception e) { |
1179 | e.printStackTrace(); |
1180 | break; |
1181 | } |
1182 | } |
1183 | if (numbers.size() == 0) { |
1184 | return null; |
1185 | } |
1186 | final int[] result = new int[numbers.size()]; |
1187 | for (int i = 0; i < numbers.size(); i++) { |
1188 | result[i] = ((Integer) numbers.get(i)).intValue(); |
1189 | } |
1190 | return result; |
1191 | } |
1192 | |
1193 | /** |
1194 | * Get key sorted list of all System Properties. |
1195 | * |
1196 | * @return Array with the two columns key and value. |
1197 | */ |
1198 | public static String[][] getSortedSystemProperties() { |
1199 | final Map map = new TreeMap(System.getProperties()); |
1200 | String[][] rowData = new String[map.size()][2]; |
1201 | int rowNum = 0; |
1202 | final Iterator iterator = map.entrySet().iterator(); |
1203 | while (iterator.hasNext()) { |
1204 | Map.Entry entry = (Map.Entry) iterator.next(); |
1205 | rowData[rowNum][0] = (String) entry.getKey(); |
1206 | rowData[rowNum][1] = (String) entry.getValue(); |
1207 | rowNum++; |
1208 | } |
1209 | return rowData; |
1210 | } |
1211 | |
1212 | } |