IoUtility.java
0001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
0002  *
0003  * Copyright 2000-2013,  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         FileOutputStream out = null;
0365         try {
0366             out = new FileOutputStream(file);
0367             final byte[] data = new byte[1024];
0368             int length;
0369             while ((length = in.read(data)) != -1) {
0370                 out.write(data, 0, length);
0371             }
0372         finally {
0373             close(in);
0374             close(out);
0375         }
0376     }
0377 
0378     /**
0379      * Convert String into a {@link Reader}.
0380      *
0381      * <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4094886">
0382      * Bug ID: 4094886</a>
0383      *
0384      @param   data    Convert this.
0385      @return  Resulting reader.
0386      */
0387     public static final Reader stringToReader(final String data) {
0388         try {
0389             return new InputStreamReader(new ByteArrayInputStream(data.getBytes("ISO-8859-1")));
0390         catch (UnsupportedEncodingException e) {
0391             // should never occur
0392             throw new RuntimeException(e);
0393         }
0394     }
0395 
0396     /**
0397      * Saves a <code>String</code> into a file. Existing files are overwritten.
0398      *
0399      @param   filename    Name of the file (could include path).
0400      @param   text        Data to save in the file.
0401      @throws  IOException File exception occurred.
0402      *
0403      @deprecated  Use {@link #saveFile(File, String, String)} that has an encoding.
0404      */
0405     public static void saveFile(final String filename, final String text)
0406             throws IOException {
0407         saveFile(new File(filename), text);
0408     }
0409 
0410     /**
0411      * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten.
0412      *
0413      @param   filename    Name of the file (could include path).
0414      @param   text        Data to save in the file.
0415      @throws  IOException File exception occurred.
0416      *
0417      @deprecated  Use {@link #saveFile(File, StringBuffer, String)} that has an encoding.
0418      */
0419     public static void saveFile(final String filename, final StringBuffer text)
0420             throws IOException {
0421         saveFile(new File(filename), text.toString());
0422     }
0423 
0424     /**
0425      * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten.
0426      *
0427      @param   file        File to save into.
0428      @param   text        Data to save in the file.
0429      @throws  IOException File exception occurred.
0430      *
0431      @deprecated  Use {@link #saveFile(File, StringBuffer, String)} that has an encoding
0432      * parameter.
0433      */
0434     public static void saveFile(final File file, final StringBuffer text)
0435             throws IOException {
0436         saveFile(file, text.toString());
0437     }
0438 
0439     /**
0440      * Saves a <code>String</code> in a file. Uses default encoding.  Existing files are
0441      * overwritten.
0442      *
0443      @param   file        File to save the data in.
0444      @param   text        Data to save in the file.
0445      @throws  IOException File exception occurred.
0446      *
0447      @deprecated  Use {@link #saveFile(File, String, String)} that has an encoding parameter.
0448      */
0449     public static void saveFile(final File file, final String text)
0450             throws IOException {
0451         BufferedWriter out = null;
0452         try {
0453             out = new BufferedWriter(new FileWriter(file));
0454             out.write(text);
0455         finally {
0456             close(out);
0457         }
0458     }
0459 
0460     /**
0461      * Saves a <code>String</code> in a file.  Existing files are overwritten.
0462      *
0463      @param   file        File to save the data in.
0464      @param   text        Data to save in the file.
0465      @param   encoding    Use this encoding.
0466      @throws  IOException File exception occurred.
0467      */
0468     public static void saveFile(final File file, final StringBuffer text, final String encoding)
0469             throws IOException {
0470         saveFile(file, text.toString(), encoding);
0471     }
0472 
0473     /**
0474      * Saves a <code>String</code> in a file.
0475      *
0476      @param   file        File to save the data in.
0477      @param   text        Data to save in the file.
0478      @param   encoding    Use this encoding.
0479      @throws  IOException File exception occurred.
0480      */
0481     public static void saveFile(final File file, final String text, final String encoding)
0482             throws IOException {
0483         BufferedWriter out = new BufferedWriter(
0484             new OutputStreamWriter(new FileOutputStream(file), encoding));
0485         try {
0486             out.write(text);
0487         finally {
0488             out.close();
0489         }
0490     }
0491 
0492     /**
0493      * Saves a <code>data</code> in a file. Existing files are overwritten.
0494      *
0495      @param   file        File to save the data in.
0496      @param   data        Data to save in the file.
0497      @throws  IOException File exception occurred.
0498      */
0499     public static void saveFileBinary(final File file, final byte[] data)
0500             throws IOException {
0501         BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
0502         try {
0503             out.write(data);
0504         finally {
0505             out.close();
0506         }
0507     }
0508 
0509     /**
0510      * Copies a file to a different location.
0511      *
0512      @param   from    Copy source.
0513      @param   to      Copy destination.
0514      @throws  IOException File exception occurred.
0515      */
0516     public static void copyFile(final File from, final File to)
0517             throws IOException {
0518 
0519         if (from.getCanonicalFile().equals(to.getCanonicalFile())) {
0520             return;
0521         }
0522         createNecessaryDirectories(to);
0523         FileInputStream in = null;
0524         FileOutputStream out = null;
0525         try {
0526             in = new FileInputStream(from);
0527             out = new FileOutputStream(to);
0528 
0529             byte[] data = new byte[1024];
0530             int length;
0531             while ((length = in.read(data)) != -1) {
0532                 out.write(data, 0, length);
0533             }
0534         finally {
0535             close(in);
0536             close(out);
0537         }
0538     }
0539 
0540     /**
0541      * Copy one file or directory to another location.
0542      * If targetLocation does not exist, it will be created.
0543      *
0544      @param   sourceLocation  Copy from here. This can be a file or a directory.
0545      @param   targetLocation  Copy to this location. If source is a file this must be a file too.
0546      @throws  IOException     Something went wrong.
0547      */
0548     public static void copy(final String sourceLocation, final String targetLocation)
0549             throws IOException {
0550         copy(new File(sourceLocation)new File(targetLocation));
0551     }
0552 
0553     /**
0554      * Copy one directory to another location.
0555      * If targetLocation does not exist, it will be created.
0556      *
0557      @param   sourceLocation  Copy from here.
0558      @param   targetLocation  Copy to this location
0559      @throws  IOException     Something went wrong.
0560      */
0561     public static void copy(final File sourceLocation, final File targetLocation)
0562             throws IOException {
0563 
0564         if (sourceLocation.isDirectory()) {
0565             if (!targetLocation.exists()) {
0566                 targetLocation.mkdir();
0567             }
0568             String[] children = sourceLocation.list();
0569             for (int i = 0; i < children.length; i++) { // recursive call for all children
0570                 copy(new File(sourceLocation, children[i]),
0571                         new File(targetLocation, children[i]));
0572             }
0573         else {    // copy file
0574             copyFile(sourceLocation, targetLocation);
0575         }
0576     }
0577 
0578     /**
0579      * List all matching files. Searches all matching sub directories recursively.
0580      * Remember to return <code>true</code> for <code>accept(File pathname)</code> if
0581      <code>pathname</code> is a directory if you want to search all sub directories!
0582      * If <code>sourceLocation</code> is a single file, this is the only file that will
0583      * be in the resulting list.
0584      *
0585      @param   sourceLocation  Check all files in this directory. (Or add this single file.)
0586      @param   filter          Accept only these directories and files.
0587      @return  List of matching files. Contains no directories.
0588      @throws  IOException     Something went wrong.
0589      */
0590     public static List listFilesRecursively(final File sourceLocation, final FileFilter filter)
0591             throws IOException {
0592         final List result = new ArrayList();
0593         if (sourceLocation.isDirectory()) {
0594             final File[] children = sourceLocation.listFiles();
0595             for (int i = 0; i < children.length; i++) { // recursive call for all children
0596                 result.addAll(listFilesRecursivelyIntern(children[i], filter));
0597             }
0598         else {
0599             result.add(sourceLocation);
0600         }
0601         return result;
0602     }
0603 
0604     /**
0605      * List all matching files. Searches all matching sub directories recursively.
0606      * Remember to return <code>true</code> for <code>accept(File pathname)</code> if
0607      <code>pathname</code> is a directory if you want to search all sub directories!
0608      *
0609      @param   sourceLocation  Check all files in this directory.
0610      @param   filter          Accept only these directories and files.
0611      @return  List of matching files. Contains no directories.
0612      @throws  IOException     Something went wrong.
0613      */
0614     private static List listFilesRecursivelyIntern(final File sourceLocation,
0615             final FileFilter filterthrows IOException {
0616         final List result = new ArrayList();
0617         if (filter.accept(sourceLocation)) {
0618             if (sourceLocation.isDirectory()) {
0619                 File[] children = sourceLocation.listFiles();
0620                 for (int i = 0; i < children.length; i++) { // recursive call for all children
0621                     result.addAll(listFilesRecursivelyIntern(children[i], filter));
0622                 }
0623             else {
0624                 result.add(sourceLocation);
0625             }
0626         }
0627         return result;
0628     }
0629 
0630     /**
0631      * Compare two files binary.
0632      *
0633      @param   from    Compare source. This file must be <code>null</code> or be an existing file.
0634      @param   with    Compare with this file. This file must be <code>null</code> or be an
0635      *                  existing file.
0636      @return  Is the contents of the two files binary equal?
0637      @throws  IOException File exception occurred.
0638      */
0639     public static boolean compareFilesBinary(final File from, final File with)
0640             throws IOException {
0641         if (from == null && with == null) {
0642             return true;
0643         }
0644         if (from == null || with == null) {
0645             return false;
0646         }
0647         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0648             return true;
0649         }
0650         if (from.length() != with.length()) {
0651             return false;
0652         }
0653         byte[] dataOne = new byte[1024];
0654         byte[] dataTwo = new byte[1024];
0655         int length;
0656 
0657         FileInputStream one = null;
0658         FileInputStream two = null;
0659         try {
0660             one = new FileInputStream(from);
0661             two = new FileInputStream(with);
0662 
0663             while ((length = one.read(dataOne)) != -1) {
0664                 if (length != two.read(dataTwo)) {
0665                     return false;
0666                 }
0667                 if (!Arrays.equals(dataOne, dataTwo)) {
0668                     return false;
0669                 }
0670             }
0671             return true;
0672         finally {
0673             close(one);
0674             close(two);
0675         }
0676     }
0677 
0678     /**
0679      * Compare two text files. Ignores different line separators. As there are:
0680      * LF, CR, CR + LF, NEL, FF, LS, PS.
0681      *
0682      @param   from        Compare source.
0683      @param   with        Compare with this file.
0684      @param   encoding    Use this character encoding. Must not be <code>null</code>.
0685      @return  Is the contents of the two text files equal?
0686      @throws  IOException File exception occurred or encoding is not supported.
0687      @throws  NullPointerException    Is encoding different from <code>null</code>?
0688      */
0689     public static boolean compareTextFiles(final File from, final File with, final String encoding)
0690             throws IOException {
0691         if (from == null && with == null) {
0692             return true;
0693         }
0694         if (from == null || with == null) {
0695             return false;
0696         }
0697         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0698             return true;
0699         }
0700 
0701         BufferedReader one = null;
0702         BufferedReader two = null;
0703         FileInputStream fromIn = null;
0704         FileInputStream withIn = null;
0705         try {
0706             fromIn = new FileInputStream(from);
0707             one = new BufferedReader(new InputStreamReader(fromIn, encoding));
0708             withIn = new FileInputStream(with);
0709             two = new BufferedReader(new InputStreamReader(withIn, encoding));
0710 
0711             boolean crOne = false;
0712             boolean crTwo = false;
0713             do {
0714                 int readOne = one.read();
0715                 int readTwo = two.read();
0716                 if (readOne == readTwo) {
0717                     if (readOne < 0) {
0718                         break;
0719                     }
0720                 else {
0721                     crOne = readOne == 0x0D;
0722                     crTwo = readTwo == 0x0D;
0723                     if (crOne) {
0724                         readOne = one.read();
0725                     }
0726                     if (crTwo) {
0727                         readTwo = two.read();
0728                     }
0729                     if (crOne && readOne != 0x0A && isCr(readTwo)) {
0730                         readTwo = two.read();
0731                     }
0732                     if (crTwo && readTwo != 0x0A && isCr(readOne)) {
0733                         readOne = one.read();
0734                     }
0735                     if (readOne != readTwo && (!isCr(readOne&& !isCr(readTwo))) {
0736                         return false;
0737                     }
0738                 }
0739             while (true);
0740             return true;
0741         finally {
0742             close(fromIn);
0743             close(one);
0744             close(two);
0745             close(withIn);
0746         }
0747     }
0748 
0749     /**
0750      * Compare two text files. Ignores different line separators. As there are:
0751      * LF, CR, CR + LF
0752      *
0753      @param   from        Compare source.
0754      @param   with        Compare with this file.
0755      @param   startAtLine Start comparing at this line (beginning with 0).
0756      @param   encoding    Use this character encoding. Must not be <code>null</code>.
0757      @return  Is the contents of the two text files equal?
0758      @throws  IOException File exception occurred or encoding is not supported.
0759      @throws  NullPointerException    Is encoding different from <code>null</code>?
0760      */
0761     public static boolean compareTextFiles(final File from, final File with, final int startAtLine,
0762             final String encodingthrows IOException {
0763 
0764         if (from == null && with == null) {
0765             return true;
0766         }
0767         if (from == null || with == null) {
0768             return false;
0769         }
0770         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0771             return true;
0772         }
0773         if (startAtLine < 0) {
0774             return true;
0775         }
0776         BufferedReader one = null;
0777         BufferedReader two = null;
0778         FileInputStream fromIn = null;
0779         FileInputStream withIn = null;
0780         try {
0781             fromIn = new FileInputStream(from);
0782             one = new BufferedReader(new InputStreamReader(fromIn, encoding));
0783             withIn = new FileInputStream(with);
0784             two = new BufferedReader(new InputStreamReader(withIn, encoding));
0785             int pos = 0;
0786             do {
0787                 String lineOne = one.readLine();
0788                 String lineTwo = two.readLine();
0789                 if (lineOne == null) {
0790                     if (lineTwo == null) {
0791                         break;
0792                     }
0793                     return false;
0794                 }
0795                 if (pos++ >= startAtLine && !lineOne.equals(lineTwo)) {
0796                     return false;
0797                 }
0798             while (true);
0799             return true;
0800         finally {
0801             close(fromIn);
0802             close(one);
0803             close(two);
0804             close(withIn);
0805         }
0806     }
0807 
0808     /**
0809      * Test if character is LF, CR, NEL, FF, LS, PS.
0810      @param   c   Character to test.
0811      @return  Is character a line terminator?
0812      */
0813     private static boolean isCr(final int c) {
0814         return c == 0x0A || c == 0x0D || c == 0x85 || c == 0x0C || c == 0x2028 || c == 0x2029;
0815     }
0816 
0817     /**
0818      * Delete file directory recursive.
0819      *
0820      @param   directory   Directory to delete. Must not be a symbolic link.
0821      @param   deleteDir   Delete directory itself too?
0822      @return  Was deletion successful?
0823      */
0824     public static boolean deleteDir(final File directory, final boolean deleteDir) {
0825 
0826         // first we check if the file is a symbolic link
0827         try {
0828             if (isSymbolicLink(directory)) {
0829                 return false;
0830             }
0831         catch (IOException e) {
0832             return false;
0833         }
0834         final File candir;
0835         try {
0836             candir = directory.getCanonicalFile();
0837         catch (IOException e) {
0838             return false;
0839         }
0840 
0841         // now we go through all of the files and subdirectories in the
0842         // directory and delete them one by one
0843         boolean success = true;
0844         File[] files = candir.listFiles();
0845         if (files != null) {
0846             for (int i = 0; i < files.length; i++) {
0847                 File file = files[i];
0848 
0849                 // in case this directory is actually a symbolic link, or it's
0850                 // empty, we want to try to delete the link before we try
0851                 // anything
0852                 boolean deleted = file.delete();
0853                 if (!deleted) {
0854                     // deleting the file failed, so maybe it's a non-empty
0855                     // directory
0856                     if (file.isDirectory()) {
0857                         deleted = deleteDir(file, true);
0858                     }
0859 
0860                     // otherwise, there's nothing else we can do
0861                 }
0862                 success = success && deleted;
0863             }
0864         }
0865 
0866         // now that we tried to clear the directory out, we can try to delete it
0867         if (deleteDir && directory.exists()) {
0868             return directory.delete();
0869         }
0870         return success;
0871     }
0872 
0873     /**
0874      * Delete directory contents for all files that match the filter. The main directory itself is
0875      * not deleted.
0876      *
0877      @param   directory   Directory to scan for files to delete.
0878      @param   filter      Filter files (and directories) to delete.
0879      @return  Was deletion successful?
0880      */
0881     public static boolean deleteDir(final File directory, final FileFilter filter) {
0882         // first we check if the file is a symbolic link
0883         try {
0884             if (isSymbolicLink(directory)) {
0885                 return false;
0886             }
0887         catch (IOException e) {
0888             return false;
0889         }
0890         final File candir;
0891         try {
0892             candir = directory.getCanonicalFile();
0893         catch (IOException e) {
0894             return false;
0895         }
0896 
0897         // now we go through all of the files and subdirectories in the
0898         // directory and delete them one by one
0899         boolean success = true;
0900         File[] files = candir.listFiles(filter);
0901         if (files != null) {
0902             for (int i = 0; i < files.length; i++) {
0903                 File file = files[i];
0904 
0905                 // in case this directory is actually a symbolic link, or it's
0906                 // empty, we want to try to delete the link before we try
0907                 // anything
0908                 boolean deleted = file.delete();
0909                 if (!deleted) {
0910                     // deleting the file failed, so maybe it's a non-empty
0911                     // directory
0912                     if (file.isDirectory()) {
0913                         deleted = deleteDir(file, true);
0914                     }
0915 
0916                     // otherwise, there's nothing else we can do
0917                 }
0918                 success = success && deleted;
0919             }
0920         }
0921 
0922         return success;
0923     }
0924 
0925     /**
0926      * Determines whether the specified file is a symbolic link rather than an actual file.
0927      * See {@link
0928      * https://svn.apache.org/repos/asf/commons/proper/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java}.
0929      @param   file    File to check.
0930      @return  Is the file is a symbolic link?
0931      @throws  IOException     IO error while checking the file.
0932      */
0933     public static boolean isSymbolicLink(final File filethrows IOException {
0934         if (file == null) {
0935             throw new NullPointerException("File must not be null");
0936         }
0937         // is windows file system in use?
0938         if (File.separatorChar == '\\') {
0939             // we have no symbolic links
0940             return false;
0941         }
0942         File fileInCanonicalDir = null;
0943         if (file.getParent() == null) {
0944             fileInCanonicalDir = file;
0945         else {
0946             File canonicalDir = file.getParentFile().getCanonicalFile();
0947             fileInCanonicalDir = new File(canonicalDir, file.getName());
0948         }
0949         if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) {
0950             return false;
0951         }
0952         return true;
0953     }
0954 
0955     /**
0956      * Print current system properties to System.out.
0957      */
0958     public static void printAllSystemProperties() {
0959         Properties sysprops = System.getProperties();
0960         for (Enumeration e = sysprops.propertyNames(); e.hasMoreElements()) {
0961             String key = (Stringe.nextElement();
0962             String value = sysprops.getProperty(key);
0963             System.out.println(key + "=" + value);
0964         }
0965     }
0966 
0967     /**
0968      * Get home directory of user.
0969      *
0970      @return  Home directory of user.
0971      */
0972     public static File getUserHomeDirectory() {
0973         return new File((StringSystem.getProperties().get("user.home"));
0974     }
0975 
0976     /**
0977      * Creates necessary parent directories for a file.
0978      *
0979      @param   file    File.
0980      @throws  IOException Creation failed.
0981      */
0982     public static void createNecessaryDirectories(final File filethrows IOException {
0983         if (file != null && file.getParentFile() != null) {
0984             file.getParentFile().mkdirs();
0985             if (!file.getParentFile().exists()) {
0986                 throw new IOException("directory creation failed: " + file.getParent());
0987             }
0988         }
0989     }
0990 
0991     /**
0992      * Create relative address from <code>origin</code> to <code>next</code>.
0993      * The resulting file path has "/" as directory name separator.
0994      * If the resulting file path is the same as origin specifies, we return "".
0995      * Otherwise the result will always have an "/" as last character.
0996      *
0997      @param   origin  This is the original location. Must be a directory.
0998      @param   next    This should be the next location. Must also be a directory.
0999      @return  Relative (or if necessary absolute) file path.
1000      */
1001     public static final String createRelativePath(final File origin, final File next) {
1002         if (origin.equals(next)) {
1003             return "";
1004         }
1005         final Path org = new Path(origin.getPath().replace(File.separatorChar, '/')"");
1006         final Path ne = new Path(next.getPath().replace(File.separatorChar, '/')"");
1007         return org.createRelative(ne.toString()).toString();
1008     }
1009 
1010     /**
1011      * Waits until a '\n' was read from System.in.
1012      */
1013     public static void waitln() {
1014         System.out.println("\n..press <return> to continue");
1015         try {
1016             (new java.io.BufferedReader(new java.io.InputStreamReader(
1017                 System.in))).readLine();
1018         catch (IOException e) {
1019             // ignore
1020         }
1021     }
1022 
1023     /**
1024      * Closes input stream without exception.
1025      *
1026      @param   in  Input stream, maybe <code>null</code>.
1027      */
1028     public static void close(final InputStream in) {
1029         if (in != null) {
1030             try {
1031                 in.close();
1032             catch (Exception e) {
1033                 // ignore
1034             }
1035         }
1036     }
1037 
1038     /**
1039      * Closes writer without exception.
1040      *
1041      @param   writer  Writer, maybe <code>null</code>.
1042      */
1043     public static void close(final Writer writer) {
1044         if (writer != null) {
1045             try {
1046                 writer.close();
1047             catch (Exception e) {
1048                 // ignore
1049             }
1050         }
1051     }
1052 
1053     /**
1054      * Closes out stream without exception.
1055      *
1056      @param   out Output stream, maybe <code>null</code>.
1057      */
1058     public static void close(final OutputStream out) {
1059         if (out != null) {
1060             try {
1061                 out.close();
1062             catch (Exception e) {
1063                 // ignore
1064             }
1065         }
1066     }
1067 
1068     /**
1069      * Closes input reader without exception.
1070      *
1071      @param   reader  Reader, maybe <code>null</code>.
1072      */
1073     public static void close(final Reader reader) {
1074         if (reader != null) {
1075             try {
1076                 reader.close();
1077             catch (Exception e) {
1078                 // ignore
1079             }
1080         }
1081     }
1082 
1083     /**
1084      * Get start directory for application. Within the start directory all newly created data is
1085      * stored in. If this is no Java Webstart version the result is
1086      <code>new File(".")</code>. Otherwise the start directory is the subdirectory
1087      * "." concatenated <code>application</code> within <code>user.home</code>.
1088      *
1089      @param   application Application name, used for Java Webstart version. Should
1090      *          be written in lowercase letters. A "." is automatically appended at
1091      *          the beginning.
1092      @return  Start directory for application.
1093      */
1094     public static final File getStartDirectory(final String application) {
1095         final File startDirectory;
1096         if (isWebStarted()) {
1097             startDirectory = new File(getUserHomeDirectory()"." + application);
1098         else {
1099             startDirectory = new File(".");
1100         }
1101         return startDirectory;
1102     }
1103 
1104     /**
1105      * Was the application started by Java Webstart?
1106      *
1107      @return  Was the application started by Java Webstart.
1108      */
1109     public static final boolean isWebStarted() {
1110         final String webStart = (StringSystem.getProperties().get("javawebstart.version");
1111         return webStart != null;
1112     }
1113 
1114     /**
1115      * Loads a property file from given URL.
1116      *
1117      @param   url     URL to load properties from. Must not be <code>null</code>.
1118      @return  Loaded properties.
1119      @throws  IOException             Reading error.
1120      */
1121     public static Properties loadProperties(final URL url)
1122            throws IOException {
1123         Properties newprops = new Properties();
1124         InputStream in = null;
1125         try {
1126             in = url.openStream();
1127             newprops.load(in);
1128         finally {
1129             close(in);
1130         }
1131         return newprops;
1132     }
1133 
1134     /**
1135      * Sleep my little class.
1136      *
1137      @param   ms  Milliseconds to wait.
1138      */
1139     public static void sleep(final int ms) {
1140         final Object monitor = new Object();
1141         synchronized (monitor) {
1142             try {
1143                 monitor.wait(ms);
1144             catch (InterruptedException e) {
1145             }
1146         }
1147     }
1148 
1149     /**
1150      * Get currently running java version and subversion numbers. This is the running JRE version.
1151      * If no version could be identified <code>null</code> is returned.
1152      *
1153      @return  Array of version and subversion numbers.
1154      */
1155     public static int[] getJavaVersion() {
1156         final String version = System.getProperty("java.version");
1157         final List numbers = new ArrayList();
1158         final StringTokenizer tokenizer = new StringTokenizer(version, ".");
1159         while (tokenizer.hasMoreElements()) {
1160             String sub = (Stringtokenizer.nextToken();
1161             for (int i = 0; i < sub.length(); i++) {
1162                 if (!Character.isDigit(sub.charAt(i))) {
1163                     sub = sub.substring(0, i);
1164                     break;
1165                 }
1166             }
1167             try {
1168                 numbers.add(new Integer(Integer.parseInt(sub)));
1169             catch (Exception e) {
1170                 e.printStackTrace();
1171                 break;
1172             }
1173         }
1174         if (numbers.size() == 0) {
1175             return null;
1176         }
1177         final int[] result = new int[numbers.size()];
1178         for (int i = 0; i < numbers.size(); i++) {
1179             result[i((Integernumbers.get(i)).intValue();
1180         }
1181         return result;
1182     }
1183 
1184     /**
1185      * Get key sorted list of all System Properties.
1186      *
1187      @return  Array with the two columns key and value.
1188      */
1189     public static String[][] getSortedSystemProperties() {
1190         final Map map = new TreeMap(System.getProperties());
1191         String[][] rowData = new String[map.size()][2];
1192         int rowNum = 0;
1193         final Iterator iterator = map.entrySet().iterator();
1194         while (iterator.hasNext()) {
1195             Map.Entry entry = (Map.Entryiterator.next();
1196             rowData[rowNum][0(Stringentry.getKey();
1197             rowData[rowNum][1(Stringentry.getValue();
1198             rowNum++;
1199         }
1200         return rowData;
1201     }
1202 
1203 }