IoUtility.java
0001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
0002  *
0003  * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
0004  *
0005  * "Hilbert II" is free software; you can redistribute
0006  * it and/or modify it under the terms of the GNU General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0013  * GNU General Public License for more details.
0014  */
0015 
0016 package org.qedeq.base.io;
0017 
0018 import java.io.BufferedOutputStream;
0019 import java.io.BufferedReader;
0020 import java.io.BufferedWriter;
0021 import java.io.ByteArrayInputStream;
0022 import java.io.File;
0023 import java.io.FileFilter;
0024 import java.io.FileInputStream;
0025 import java.io.FileOutputStream;
0026 import java.io.FileReader;
0027 import java.io.FileWriter;
0028 import java.io.IOException;
0029 import java.io.InputStream;
0030 import java.io.InputStreamReader;
0031 import java.io.OutputStream;
0032 import java.io.OutputStreamWriter;
0033 import java.io.Reader;
0034 import java.io.UnsupportedEncodingException;
0035 import java.io.Writer;
0036 import java.net.URL;
0037 import java.nio.charset.Charset;
0038 import java.util.ArrayList;
0039 import java.util.Arrays;
0040 import java.util.Enumeration;
0041 import java.util.Iterator;
0042 import java.util.List;
0043 import java.util.Map;
0044 import java.util.Properties;
0045 import java.util.StringTokenizer;
0046 import java.util.TreeMap;
0047 
0048 import org.apache.commons.lang.SystemUtils;
0049 
0050 
0051 /**
0052  * A collection of useful static methods for input and output.
0053  *
0054  * LATER mime 20070101: use StringBuilder instead of StringBuffer if working under JDK 1.5
0055  *
0056  @author  Michael Meyling
0057  */
0058 public final class IoUtility {
0059 
0060     /**
0061      * Constructor, should never be called.
0062      */
0063     private IoUtility() {
0064         // don't call me
0065     }
0066 
0067     /**
0068      * Get default encoding for this system.
0069      *
0070      @return  Default encoding for this system.
0071      */
0072     public static String getDefaultEncoding() {
0073         return SystemUtils.FILE_ENCODING;
0074 // mime 20090630: under ubuntu the following gave the encoding ASCII:
0075 //        return new InputStreamReader(
0076 //              new ByteArrayInputStream(new byte[0])).getEncoding();
0077 // but it was: file.encoding="ANSI_X3.41968"
0078     }
0079 
0080     /**
0081      * Get working Java encoding.
0082      *
0083      @param   encoding    Try this encoding.
0084      @return              This is <code>encoding</code> if it is supported. Or an other
0085      *                      encoding that is supported by this system.
0086      */
0087     public static String getWorkingEncoding(final String encoding) {
0088         if (encoding != null) {
0089             try {
0090                 if (Charset.isSupported(encoding)
0091                         && Charset.forName(encoding).canEncode()) {
0092                     return encoding;
0093                 }
0094             catch (RuntimeException e) {
0095                 // ignore
0096             }
0097         }
0098         // we must inform someone, but using
0099         // Trace within this class is not wise, because it is used
0100         // before the Trace is initialized.
0101         System.err.println("not supported encoding: " + encoding);
0102         return "ISO-8859-1";    // every system must support this
0103     }
0104 
0105     /**
0106      * Reads a file and returns the contents as a <code>String</code>.
0107      *
0108      @param   filename    Name of the file (could include path).
0109      @param   encoding    Take this encoding.
0110      @return  Contents of file.
0111      @throws  IOException File exception occurred.
0112      */
0113     public static String loadFile(final String filename, final String encoding)
0114             throws IOException {
0115 
0116         final StringBuffer buffer = new StringBuffer();
0117         loadFile(filename, buffer, encoding);
0118         return buffer.toString();
0119     }
0120 
0121     /**
0122      * Reads contents of a file into a string buffer.
0123      *
0124      @param   filename    Name of the file (could include path).
0125      @param   buffer      Buffer to fill with file contents.
0126      @param   encoding    Take this encoding.
0127      @throws  IOException File exception occurred.
0128      */
0129     public static void loadFile(final String filename,
0130             final StringBuffer buffer, final String encoding)
0131             throws IOException {
0132         loadFile(new File(filename), buffer, encoding);
0133     }
0134 
0135     /**
0136      * Reads contents of a stream into a string buffer. Stream is not closed.
0137      *
0138      @param   in          This stream will be loaded.
0139      @param   buffer      Buffer to fill with file contents.
0140      @throws  IOException File exception occurred.
0141      *
0142      @deprecated  Use {@link #loadReader(Reader, StringBuffer)}.
0143      */
0144     public static void loadStream(final InputStream in, final StringBuffer buffer)
0145             throws IOException {
0146 
0147         buffer.setLength(0);
0148         int c;
0149         while ((c = in.read()) >= 0) {
0150             buffer.append((charc);
0151         }
0152     }
0153 
0154     /**
0155      * Returns contents of a stream into a string, respecting a maximum length.
0156      * No exceptions are thrown. Stream is not closed.
0157      *
0158      @param   in          This stream will be loaded.
0159      @param   maxLength   This length is not exceeded.
0160      @return  readData    Data read, is not <code>null</code>.
0161      */
0162     public static String loadStreamWithoutException(final InputStream in, final int maxLength) {
0163 
0164         if (in == null) {
0165             return "";
0166         }
0167         final StringBuffer buffer = new StringBuffer();
0168         buffer.setLength(0);
0169         try {
0170             int counter = 0;
0171             int c;
0172             while (counter++ < maxLength) {
0173                 c = in.read();
0174                 if (c < 0) {
0175                     break;
0176                 }
0177                 buffer.append((charc);
0178             }
0179         catch (IOException e) {
0180             // ignored
0181         catch (RuntimeException e) {
0182             // ignored
0183         }
0184         return buffer.toString();
0185     }
0186 
0187     /**
0188      * Reads contents of a {@link Reader} into a string buffer. Reader is not closed.
0189      *
0190      @param   in          This reader will be loaded.
0191      @param   buffer      Buffer to fill with file contents.
0192      @throws  IOException File exception occurred.
0193      */
0194     public static void loadReader(final Reader in, final StringBuffer buffer)
0195             throws IOException {
0196 
0197         buffer.setLength(0);
0198         int c;
0199         while ((c = in.read()) >= 0) {
0200             buffer.append((charc);
0201         }
0202     }
0203 
0204     /**
0205      * Reads contents of a file into a string buffer. Uses default encoding.
0206      *
0207      @param   file        This file will be loaded.
0208      @param   buffer      Buffer to fill with file contents.
0209      @throws  IOException File exception occurred.
0210      *
0211      @deprecated  Use {@link #loadFile(File, StringBuffer, String)}.
0212      */
0213     public static void loadFile(final File file,
0214             final StringBuffer buffer)
0215             throws IOException {
0216 
0217         final int size = (intfile.length();
0218         final char[] data = new char[size];
0219         buffer.setLength(0);
0220         FileReader in = null;
0221         try {
0222             in = new FileReader(file);
0223             int charsread = 0;
0224             while (charsread < size) {
0225                 charsread += in.read(data, charsread, size - charsread);
0226             }
0227         finally {
0228             close(in);
0229         }
0230         buffer.insert(0, data);
0231     }
0232 
0233     /**
0234      * Reads contents of a file into a string buffer.
0235      *
0236      @param   file        This file will be loaded.
0237      @param   buffer      Buffer to fill with file contents.
0238      @param   encoding    Take this encoding.
0239      @throws  IOException File exception occurred.
0240      */
0241     public static void loadFile(final File file,
0242             final StringBuffer buffer, final String encoding)
0243             throws IOException {
0244 
0245         buffer.setLength((intfile.length());    // ensure capacity
0246         buffer.setLength(0);
0247         final InputStreamReader in = new InputStreamReader(new FileInputStream(file), encoding);
0248         final char[] data = new char[10 1024];
0249 
0250         try {
0251             int charsread = 0;
0252             while ((charsread = in.read(data, 0, data.length))) {
0253                 buffer.append(data, 0, charsread);
0254             }
0255         finally {
0256             in.close();
0257         }
0258     }
0259 
0260     /**
0261      * Reads a file and returns the contents as a <code>String</code>.
0262      *
0263      @param   file        File to load from.
0264      @return  Contents of file.
0265      @throws  IOException File exception occurred.
0266      */
0267     public static final byte[] loadFileBinary(final File filethrows IOException {
0268         final int size = (intfile.length();
0269         final FileInputStream in = new FileInputStream(file);
0270         try {
0271             final byte[] data = new byte[size];
0272             int charsread = 0;
0273             while (charsread < size) {
0274                 final int read = in.read(data, charsread, size - charsread);
0275                 if (read == -1) {
0276                     final byte[] result = new byte[charsread];
0277                     System.arraycopy(data, 0, result, 0, charsread);
0278                     return result;
0279                 }
0280                 charsread += read;
0281             }
0282             in.close();
0283             return data;
0284         finally {
0285             close(in);
0286         }
0287     }
0288 
0289 
0290     /**
0291      * Reads contents of an URL into a string buffer. The filling is character set dependent.
0292      * Content is added to the end of buffer. (Existing data is not cleared.)
0293      <p>
0294      * All parameters should not be <code>null</code>.
0295      @param   url         This URL will be loaded.
0296      @param   buffer      Buffer to fill with file contents.
0297      @throws  IOException Reading failed.
0298      *
0299      @deprecated  Choose correct encoding.
0300      */
0301     public static void loadFile(final URL url, final StringBuffer bufferthrows IOException {
0302         InputStream in = null;
0303         BufferedReader dis = null;
0304         try {
0305             in = url.openStream();
0306             dis = new BufferedReader(new InputStreamReader(in));
0307             int i;
0308             while ((i = dis.read()) != -1) {
0309                 buffer.append((chari);
0310             }
0311         finally {
0312             close(in);
0313             close(dis);
0314         }
0315     }
0316 
0317     /**
0318      * Reads contents of an URL into a StringBuffer. The filling is character set dependent. The
0319      * buffer is not cleared, contents is just added.
0320      <p>
0321      * All parameters should not be <code>null</code>.
0322      @param   url         This URL will be loaded.
0323      @param   buffer      Buffer to fill with file contents.
0324      @param   encoding    Take this encoding.
0325      @throws  IOException Reading failed.
0326      */
0327     public static void loadFile(final URL url, final StringBuffer buffer, final String encoding)
0328             throws IOException {
0329         InputStream in = null;
0330         BufferedReader dis = null;
0331         try {
0332             in = url.openStream();
0333             dis = new BufferedReader(new InputStreamReader(in, encoding));
0334             int i;
0335             while ((i = dis.read()) != -1) {
0336                 buffer.append((chari);
0337             }
0338         finally {
0339             close(in);
0340             close(dis);
0341         }
0342     }
0343 
0344     /**
0345      * Save binary contents of an URL into a file. Existing files are overwritten.
0346      *
0347      @param   url     This URL will be loaded.
0348      @param   file    Write into this file.
0349      @throws  IOException Reading or writing failed.
0350      */
0351     public static void saveFile(final URL url, final File filethrows IOException {
0352         saveFile(url.openStream(), file);
0353     }
0354 
0355     /**
0356      * Save binary contents of an input stream into a file. The input stream is closed even
0357      * if exceptions occur.  Existing files are overwritten.
0358      @param   in      Read this stream.
0359      @param   file    Write into this file.
0360      *
0361      @throws  IOException Reading or writing failed.
0362      */
0363     public static void saveFile(final InputStream in, final File filethrows IOException {
0364         createNecessaryDirectories(file);
0365         FileOutputStream out = null;
0366         try {
0367             out = new FileOutputStream(file);
0368             final byte[] data = new byte[1024];
0369             int length;
0370             while ((length = in.read(data)) != -1) {
0371                 out.write(data, 0, length);
0372             }
0373         finally {
0374             close(in);
0375             close(out);
0376         }
0377     }
0378 
0379     /**
0380      * Convert String into a {@link Reader}.
0381      *
0382      * <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4094886">
0383      * Bug ID: 4094886</a>
0384      *
0385      @param   data    Convert this.
0386      @return  Resulting reader.
0387      */
0388     public static final Reader stringToReader(final String data) {
0389         try {
0390             return new InputStreamReader(new ByteArrayInputStream(data.getBytes("ISO-8859-1")));
0391         catch (UnsupportedEncodingException e) {
0392             // should never occur
0393             throw new RuntimeException(e);
0394         }
0395     }
0396 
0397     /**
0398      * Saves a <code>String</code> into a file. Existing files are overwritten.
0399      *
0400      @param   filename    Name of the file (could include path).
0401      @param   text        Data to save in the file.
0402      @throws  IOException File exception occurred.
0403      *
0404      @deprecated  Use {@link #saveFile(File, String, String)} that has an encoding.
0405      */
0406     public static void saveFile(final String filename, final String text)
0407             throws IOException {
0408         saveFile(new File(filename), text);
0409     }
0410 
0411     /**
0412      * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten.
0413      *
0414      @param   filename    Name of the file (could include path).
0415      @param   text        Data to save in the file.
0416      @throws  IOException File exception occurred.
0417      *
0418      @deprecated  Use {@link #saveFile(File, StringBuffer, String)} that has an encoding.
0419      */
0420     public static void saveFile(final String filename, final StringBuffer text)
0421             throws IOException {
0422         saveFile(new File(filename), text.toString());
0423     }
0424 
0425     /**
0426      * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten.
0427      *
0428      @param   file        File to save into.
0429      @param   text        Data to save in the file.
0430      @throws  IOException File exception occurred.
0431      *
0432      @deprecated  Use {@link #saveFile(File, StringBuffer, String)} that has an encoding
0433      * parameter.
0434      */
0435     public static void saveFile(final File file, final StringBuffer text)
0436             throws IOException {
0437         saveFile(file, text.toString());
0438     }
0439 
0440     /**
0441      * Saves a <code>String</code> in a file. Uses default encoding.  Existing files are
0442      * overwritten.
0443      *
0444      @param   file        File to save the data in.
0445      @param   text        Data to save in the file.
0446      @throws  IOException File exception occurred.
0447      *
0448      @deprecated  Use {@link #saveFile(File, String, String)} that has an encoding parameter.
0449      */
0450     public static void saveFile(final File file, final String text)
0451             throws IOException {
0452         createNecessaryDirectories(file);
0453         BufferedWriter out = null;
0454         try {
0455             out = new BufferedWriter(new FileWriter(file));
0456             out.write(text);
0457         finally {
0458             close(out);
0459         }
0460     }
0461 
0462     /**
0463      * Saves a <code>String</code> in a file.  Existing files are overwritten.
0464      *
0465      @param   file        File to save the data in.
0466      @param   text        Data to save in the file.
0467      @param   encoding    Use this encoding.
0468      @throws  IOException File exception occurred.
0469      */
0470     public static void saveFile(final File file, final StringBuffer text, final String encoding)
0471             throws IOException {
0472         saveFile(file, text.toString(), encoding);
0473     }
0474 
0475     /**
0476      * Saves a <code>String</code> in a file.
0477      *
0478      @param   file        File to save the data in.
0479      @param   text        Data to save in the file.
0480      @param   encoding    Use this encoding.
0481      @throws  IOException File exception occurred.
0482      */
0483     public static void saveFile(final File file, final String text, final String encoding)
0484             throws IOException {
0485         createNecessaryDirectories(file);
0486         BufferedWriter out = new BufferedWriter(
0487             new OutputStreamWriter(new FileOutputStream(file), encoding));
0488         try {
0489             out.write(text);
0490         finally {
0491             out.close();
0492         }
0493     }
0494 
0495     /**
0496      * Saves a <code>data</code> in a file. Existing files are overwritten.
0497      *
0498      @param   file        File to save the data in.
0499      @param   data        Data to save in the file.
0500      @throws  IOException File exception occurred.
0501      */
0502     public static void saveFileBinary(final File file, final byte[] data)
0503             throws IOException {
0504         createNecessaryDirectories(file);
0505         BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
0506         try {
0507             out.write(data);
0508         finally {
0509             out.close();
0510         }
0511     }
0512 
0513     /**
0514      * Copies a file to a different location.
0515      *
0516      @param   from    Copy source.
0517      @param   to      Copy destination.
0518      @throws  IOException File exception occurred.
0519      */
0520     public static void copyFile(final File from, final File to)
0521             throws IOException {
0522 
0523         if (from.getCanonicalFile().equals(to.getCanonicalFile())) {
0524             return;
0525         }
0526         createNecessaryDirectories(to);
0527         FileInputStream in = null;
0528         FileOutputStream out = null;
0529         try {
0530             in = new FileInputStream(from);
0531             out = new FileOutputStream(to);
0532 
0533             byte[] data = new byte[1024];
0534             int length;
0535             while ((length = in.read(data)) != -1) {
0536                 out.write(data, 0, length);
0537             }
0538         finally {
0539             close(in);
0540             close(out);
0541         }
0542     }
0543 
0544     /**
0545      * Copy one file or directory to another location.
0546      * If targetLocation does not exist, it will be created.
0547      *
0548      @param   sourceLocation  Copy from here. This can be a file or a directory.
0549      @param   targetLocation  Copy to this location. If source is a file this must be a file too.
0550      @throws  IOException     Something went wrong.
0551      */
0552     public static void copy(final String sourceLocation, final String targetLocation)
0553             throws IOException {
0554         copy(new File(sourceLocation)new File(targetLocation));
0555     }
0556 
0557     /**
0558      * Copy one directory to another location.
0559      * If targetLocation does not exist, it will be created.
0560      *
0561      @param   sourceLocation  Copy from here.
0562      @param   targetLocation  Copy to this location
0563      @throws  IOException     Something went wrong.
0564      */
0565     public static void copy(final File sourceLocation, final File targetLocation)
0566             throws IOException {
0567 
0568         if (sourceLocation.isDirectory()) {
0569             if (!targetLocation.exists()) {
0570                 targetLocation.mkdir();
0571             }
0572             String[] children = sourceLocation.list();
0573             for (int i = 0; i < children.length; i++) { // recursive call for all children
0574                 copy(new File(sourceLocation, children[i]),
0575                         new File(targetLocation, children[i]));
0576             }
0577         else {    // copy file
0578             copyFile(sourceLocation, targetLocation);
0579         }
0580     }
0581 
0582     /**
0583      * List all matching files. Searches all matching sub directories recursively.
0584      * Remember to return <code>true</code> for <code>accept(File pathname)</code> if
0585      <code>pathname</code> is a directory if you want to search all sub directories!
0586      * If <code>sourceLocation</code> is a single file, this is the only file that will
0587      * be in the resulting list.
0588      *
0589      @param   sourceLocation  Check all files in this directory. (Or add this single file.)
0590      @param   filter          Accept only these directories and files.
0591      @return  List of matching files. Contains no directories.
0592      @throws  IOException     Something went wrong.
0593      */
0594     public static List listFilesRecursively(final File sourceLocation, final FileFilter filter)
0595             throws IOException {
0596         final List result = new ArrayList();
0597         if (sourceLocation.isDirectory()) {
0598             final File[] children = sourceLocation.listFiles();
0599             for (int i = 0; i < children.length; i++) { // recursive call for all children
0600                 result.addAll(listFilesRecursivelyIntern(children[i], filter));
0601             }
0602         else {
0603             result.add(sourceLocation);
0604         }
0605         return result;
0606     }
0607 
0608     /**
0609      * List all matching files. Searches all matching sub directories recursively.
0610      * Remember to return <code>true</code> for <code>accept(File pathname)</code> if
0611      <code>pathname</code> is a directory if you want to search all sub directories!
0612      *
0613      @param   sourceLocation  Check all files in this directory.
0614      @param   filter          Accept only these directories and files.
0615      @return  List of matching files. Contains no directories.
0616      @throws  IOException     Something went wrong.
0617      */
0618     private static List listFilesRecursivelyIntern(final File sourceLocation,
0619             final FileFilter filterthrows IOException {
0620         final List result = new ArrayList();
0621         if (filter.accept(sourceLocation)) {
0622             if (sourceLocation.isDirectory()) {
0623                 File[] children = sourceLocation.listFiles();
0624                 for (int i = 0; i < children.length; i++) { // recursive call for all children
0625                     result.addAll(listFilesRecursivelyIntern(children[i], filter));
0626                 }
0627             else {
0628                 result.add(sourceLocation);
0629             }
0630         }
0631         return result;
0632     }
0633 
0634     /**
0635      * Compare two files binary.
0636      *
0637      @param   from    Compare source. This file must be <code>null</code> or be an existing file.
0638      @param   with    Compare with this file. This file must be <code>null</code> or be an
0639      *                  existing file.
0640      @return  Is the contents of the two files binary equal?
0641      @throws  IOException File exception occurred.
0642      */
0643     public static boolean compareFilesBinary(final File from, final File with)
0644             throws IOException {
0645         if (from == null && with == null) {
0646             return true;
0647         }
0648         if (from == null || with == null) {
0649             return false;
0650         }
0651         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0652             return true;
0653         }
0654         if (from.length() != with.length()) {
0655             return false;
0656         }
0657         byte[] dataOne = new byte[1024];
0658         byte[] dataTwo = new byte[1024];
0659         int length;
0660 
0661         FileInputStream one = null;
0662         FileInputStream two = null;
0663         try {
0664             one = new FileInputStream(from);
0665             two = new FileInputStream(with);
0666 
0667             while ((length = one.read(dataOne)) != -1) {
0668                 if (length != two.read(dataTwo)) {
0669                     return false;
0670                 }
0671                 if (!Arrays.equals(dataOne, dataTwo)) {
0672                     return false;
0673                 }
0674             }
0675             return true;
0676         finally {
0677             close(one);
0678             close(two);
0679         }
0680     }
0681 
0682     /**
0683      * Compare two text files. Ignores different line separators. As there are:
0684      * LF, CR, CR + LF, NEL, FF, LS, PS.
0685      *
0686      @param   from        Compare source.
0687      @param   with        Compare with this file.
0688      @param   encoding    Use this character encoding. Must not be <code>null</code>.
0689      @return  Is the contents of the two text files equal?
0690      @throws  IOException File exception occurred or encoding is not supported.
0691      @throws  NullPointerException    Is encoding different from <code>null</code>?
0692      */
0693     public static boolean compareTextFiles(final File from, final File with, final String encoding)
0694             throws IOException {
0695         if (from == null && with == null) {
0696             return true;
0697         }
0698         if (from == null || with == null) {
0699             return false;
0700         }
0701         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0702             return true;
0703         }
0704 
0705         BufferedReader one = null;
0706         BufferedReader two = null;
0707         FileInputStream fromIn = null;
0708         FileInputStream withIn = null;
0709         try {
0710             fromIn = new FileInputStream(from);
0711             one = new BufferedReader(new InputStreamReader(fromIn, encoding));
0712             withIn = new FileInputStream(with);
0713             two = new BufferedReader(new InputStreamReader(withIn, encoding));
0714 
0715             boolean crOne = false;
0716             boolean crTwo = false;
0717             do {
0718                 int readOne = one.read();
0719                 int readTwo = two.read();
0720                 if (readOne == readTwo) {
0721                     if (readOne < 0) {
0722                         break;
0723                     }
0724                 else {
0725                     crOne = readOne == 0x0D;
0726                     crTwo = readTwo == 0x0D;
0727                     if (crOne) {
0728                         readOne = one.read();
0729                     }
0730                     if (crTwo) {
0731                         readTwo = two.read();
0732                     }
0733                     if (crOne && readOne != 0x0A && isCr(readTwo)) {
0734                         readTwo = two.read();
0735                     }
0736                     if (crTwo && readTwo != 0x0A && isCr(readOne)) {
0737                         readOne = one.read();
0738                     }
0739                     if (readOne != readTwo && (!isCr(readOne&& !isCr(readTwo))) {
0740                         return false;
0741                     }
0742                 }
0743             while (true);
0744             return true;
0745         finally {
0746             close(fromIn);
0747             close(one);
0748             close(two);
0749             close(withIn);
0750         }
0751     }
0752 
0753     /**
0754      * Compare two text files. Ignores different line separators. As there are:
0755      * LF, CR, CR + LF
0756      *
0757      @param   from        Compare source.
0758      @param   with        Compare with this file.
0759      @param   startAtLine Start comparing at this line (beginning with 0).
0760      @param   encoding    Use this character encoding. Must not be <code>null</code>.
0761      @return  Is the contents of the two text files equal?
0762      @throws  IOException File exception occurred or encoding is not supported.
0763      @throws  NullPointerException    Is encoding different from <code>null</code>?
0764      */
0765     public static boolean compareTextFiles(final File from, final File with, final int startAtLine,
0766             final String encodingthrows IOException {
0767 
0768         if (from == null && with == null) {
0769             return true;
0770         }
0771         if (from == null || with == null) {
0772             return false;
0773         }
0774         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0775             return true;
0776         }
0777         if (startAtLine < 0) {
0778             return true;
0779         }
0780         BufferedReader one = null;
0781         BufferedReader two = null;
0782         FileInputStream fromIn = null;
0783         FileInputStream withIn = null;
0784         try {
0785             fromIn = new FileInputStream(from);
0786             one = new BufferedReader(new InputStreamReader(fromIn, encoding));
0787             withIn = new FileInputStream(with);
0788             two = new BufferedReader(new InputStreamReader(withIn, encoding));
0789             int pos = 0;
0790             do {
0791                 String lineOne = one.readLine();
0792                 String lineTwo = two.readLine();
0793                 if (lineOne == null) {
0794                     if (lineTwo == null) {
0795                         break;
0796                     }
0797                     return false;
0798                 }
0799                 if (pos++ >= startAtLine && !lineOne.equals(lineTwo)) {
0800                     return false;
0801                 }
0802             while (true);
0803             return true;
0804         finally {
0805             close(fromIn);
0806             close(one);
0807             close(two);
0808             close(withIn);
0809         }
0810     }
0811 
0812     /**
0813      * Test if character is LF, CR, NEL, FF, LS, PS.
0814      @param   c   Character to test.
0815      @return  Is character a line terminator?
0816      */
0817     private static boolean isCr(final int c) {
0818         return c == 0x0A || c == 0x0D || c == 0x85 || c == 0x0C || c == 0x2028 || c == 0x2029;
0819     }
0820 
0821     /**
0822      * Delete file directory recursive.
0823      *
0824      @param   directory   Directory to delete. Must not be a symbolic link.
0825      @param   deleteDir   Delete directory itself too?
0826      @return  Was deletion successful?
0827      */
0828     public static boolean deleteDir(final File directory, final boolean deleteDir) {
0829 
0830         // first we check if the file is a symbolic link
0831         try {
0832             if (isSymbolicLink(directory)) {
0833                 return false;
0834             }
0835         catch (IOException e) {
0836             return false;
0837         }
0838         final File candir;
0839         try {
0840             candir = directory.getCanonicalFile();
0841         catch (IOException e) {
0842             return false;
0843         }
0844 
0845         // now we go through all of the files and subdirectories in the
0846         // directory and delete them one by one
0847         boolean success = true;
0848         File[] files = candir.listFiles();
0849         if (files != null) {
0850             for (int i = 0; i < files.length; i++) {
0851                 File file = files[i];
0852 
0853                 // in case this directory is actually a symbolic link, or it's
0854                 // empty, we want to try to delete the link before we try
0855                 // anything
0856                 boolean deleted = file.delete();
0857                 if (!deleted) {
0858                     // deleting the file failed, so maybe it's a non-empty
0859                     // directory
0860                     if (file.isDirectory()) {
0861                         deleted = deleteDir(file, true);
0862                     }
0863 
0864                     // otherwise, there's nothing else we can do
0865                 }
0866                 success = success && deleted;
0867             }
0868         }
0869 
0870         // now that we tried to clear the directory out, we can try to delete it
0871         if (deleteDir && directory.exists()) {
0872             return directory.delete();
0873         }
0874         return success;
0875     }
0876 
0877     /**
0878      * Delete directory contents for all files that match the filter. The main directory itself is
0879      * not deleted.
0880      *
0881      @param   directory   Directory to scan for files to delete.
0882      @param   filter      Filter files (and directories) to delete.
0883      @return  Was deletion successful?
0884      */
0885     public static boolean deleteDir(final File directory, final FileFilter filter) {
0886         // first we check if the file is a symbolic link
0887         try {
0888             if (isSymbolicLink(directory)) {
0889                 return false;
0890             }
0891         catch (IOException e) {
0892             return false;
0893         }
0894         final File candir;
0895         try {
0896             candir = directory.getCanonicalFile();
0897         catch (IOException e) {
0898             return false;
0899         }
0900 
0901         // now we go through all of the files and subdirectories in the
0902         // directory and delete them one by one
0903         boolean success = true;
0904         File[] files = candir.listFiles(filter);
0905         if (files != null) {
0906             for (int i = 0; i < files.length; i++) {
0907                 File file = files[i];
0908 
0909                 // in case this directory is actually a symbolic link, or it's
0910                 // empty, we want to try to delete the link before we try
0911                 // anything
0912                 boolean deleted = file.delete();
0913                 if (!deleted) {
0914                     // deleting the file failed, so maybe it's a non-empty
0915                     // directory
0916                     if (file.isDirectory()) {
0917                         deleted = deleteDir(file, true);
0918                     }
0919 
0920                     // otherwise, there's nothing else we can do
0921                 }
0922                 success = success && deleted;
0923             }
0924         }
0925 
0926         return success;
0927     }
0928 
0929     /**
0930      * Determines whether the specified file is a symbolic link rather than an actual file.
0931      * See {@link
0932      * https://svn.apache.org/repos/asf/commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java}.
0933      @param   file    File to check.
0934      @return  Is the file is a symbolic link?
0935      @throws  IOException     IO error while checking the file.
0936      */
0937     public static boolean isSymbolicLink(final File filethrows IOException {
0938         if (file == null) {
0939             throw new NullPointerException("File must not be null");
0940         }
0941         // is windows file system in use?
0942         if (File.separatorChar == '\\') {
0943             // we have no symbolic links
0944             return false;
0945         }
0946         File fileInCanonicalDir = null;
0947         if (file.getParent() == null) {
0948             fileInCanonicalDir = file;
0949         else {
0950             File canonicalDir = file.getParentFile().getCanonicalFile();
0951             fileInCanonicalDir = new File(canonicalDir, file.getName());
0952         }
0953         if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) {
0954             return false;
0955         }
0956         return true;
0957     }
0958 
0959     /**
0960      * Print current system properties to System.out.
0961      */
0962     public static void printAllSystemProperties() {
0963         Properties sysprops = System.getProperties();
0964         for (Enumeration e = sysprops.propertyNames(); e.hasMoreElements()) {
0965             String key = (Stringe.nextElement();
0966             String value = sysprops.getProperty(key);
0967             System.out.println(key + "=" + value);
0968         }
0969     }
0970 
0971     /**
0972      * Get home directory of user.
0973      *
0974      @return  Home directory of user.
0975      */
0976     public static File getUserHomeDirectory() {
0977         return new File((StringSystem.getProperties().get("user.home"));
0978     }
0979 
0980     /**
0981      * Creates necessary parent directories for a file.
0982      *
0983      @param   file    File.
0984      @throws  IOException Creation failed.
0985      */
0986     public static void createNecessaryDirectories(final File filethrows IOException {
0987         if (file != null && file.getParentFile() != null) {
0988             file.getParentFile().mkdirs();
0989             if (!file.getParentFile().exists()) {
0990                 throw new IOException("directory creation failed: " + file.getParent());
0991             }
0992         }
0993     }
0994 
0995     /**
0996      * Create relative address from <code>origin</code> to <code>next</code>.
0997      * The resulting file path has "/" as directory name separator.
0998      * If the resulting file path is the same as origin specifies, we return "".
0999      * 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 = (StringSystem.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((Integernumbers.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.Entryiterator.next();
1205             rowData[rowNum][0(Stringentry.getKey();
1206             rowData[rowNum][1(Stringentry.getValue();
1207             rowNum++;
1208         }
1209         return rowData;
1210     }
1211 
1212 }