IoUtility.java
0001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
0002  *
0003  * Copyright 2000-2011,  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.lang.reflect.Field;
0037 import java.net.MalformedURLException;
0038 import java.net.URL;
0039 import java.net.URLDecoder;
0040 import java.nio.charset.Charset;
0041 import java.util.ArrayList;
0042 import java.util.Arrays;
0043 import java.util.Enumeration;
0044 import java.util.Iterator;
0045 import java.util.List;
0046 import java.util.Map;
0047 import java.util.Properties;
0048 import java.util.StringTokenizer;
0049 import java.util.TreeMap;
0050 
0051 import org.apache.commons.lang.SystemUtils;
0052 
0053 
0054 /**
0055  * A collection of useful static methods for input and output.
0056  *
0057  * LATER mime 20070101: use StringBuilder instead of StringBuffer if working under JDK 1.5
0058  *
0059  @author  Michael Meyling
0060  */
0061 public final class IoUtility {
0062 
0063     /**
0064      * Constructor, should never be called.
0065      */
0066     private IoUtility() {
0067         // don't call me
0068     }
0069 
0070     /**
0071      * Get default encoding for this system.
0072      *
0073      @return  Default encoding for this system.
0074      */
0075     public static String getDefaultEncoding() {
0076         return SystemUtils.FILE_ENCODING;
0077 // mime 20090630: under ubuntu the following gave the encoding ASCII:
0078 //        return new InputStreamReader(
0079 //              new ByteArrayInputStream(new byte[0])).getEncoding();
0080 // but it was: file.encoding="ANSI_X3.41968"
0081     }
0082 
0083     /**
0084      * Get working Java encoding.
0085      *
0086      @param   encoding    Try this encoding.
0087      @return              This is <code>encoding</code> if it is supported. Or an other
0088      *                      encoding that is supported by this system.
0089      */
0090     public static String getWorkingEncoding(final String encoding) {
0091         if (encoding != null) {
0092             try {
0093                 if (Charset.isSupported(encoding)
0094                         && Charset.forName(encoding).canEncode()) {
0095                     return encoding;
0096                 }
0097             catch (RuntimeException e) {
0098                 // ignore
0099             }
0100         }
0101         // we must inform someone, but using
0102         // Trace within this class is not wise, because it is used
0103         // before the Trace is initialized.
0104         System.err.println("not supported encoding: " + encoding);
0105         return "ISO-8859-1";    // every system must support this
0106     }
0107 
0108     /**
0109      * Reads a file and returns the contents as a <code>String</code>.
0110      *
0111      @param   filename    Name of the file (could include path).
0112      @param   encoding    Take this encoding.
0113      @return  Contents of file.
0114      @throws  IOException File exception occurred.
0115      */
0116     public static String loadFile(final String filename, final String encoding)
0117             throws IOException {
0118 
0119         final StringBuffer buffer = new StringBuffer();
0120         loadFile(filename, buffer, encoding);
0121         return buffer.toString();
0122     }
0123 
0124     /**
0125      * Reads contents of a file into a string buffer.
0126      *
0127      @param   filename    Name of the file (could include path).
0128      @param   buffer      Buffer to fill with file contents.
0129      @param   encoding    Take this encoding.
0130      @throws  IOException File exception occurred.
0131      */
0132     public static void loadFile(final String filename,
0133             final StringBuffer buffer, final String encoding)
0134             throws IOException {
0135         loadFile(new File(filename), buffer, encoding);
0136     }
0137 
0138     /**
0139      * Reads contents of a stream into a string buffer. Stream is not closed.
0140      *
0141      @param   in          This stream will be loaded.
0142      @param   buffer      Buffer to fill with file contents.
0143      @throws  IOException File exception occurred.
0144      *
0145      @deprecated  Use {@link #loadReader(Reader, StringBuffer)}.
0146      */
0147     public static void loadStream(final InputStream in, final StringBuffer buffer)
0148             throws IOException {
0149 
0150         buffer.setLength(0);
0151         int c;
0152         while ((c = in.read()) >= 0) {
0153             buffer.append((charc);
0154         }
0155     }
0156 
0157     /**
0158      * Returns contents of a stream into a string, respecting a maximum length.
0159      * No exceptions are thrown. Stream is not closed.
0160      *
0161      @param   in          This stream will be loaded.
0162      @param   maxLength   This length is not exceeded.
0163      @return  readData    Data read, is not <code>null</code>.
0164      */
0165     public static String loadStreamWithoutException(final InputStream in, final int maxLength) {
0166 
0167         if (in == null) {
0168             return "";
0169         }
0170         final StringBuffer buffer = new StringBuffer();
0171         buffer.setLength(0);
0172         try {
0173             int counter = 0;
0174             int c;
0175             while (counter++ < maxLength) {
0176                 c = in.read();
0177                 if (c < 0) {
0178                     break;
0179                 }
0180                 buffer.append((charc);
0181             }
0182         catch (IOException e) {
0183             // ignored
0184         catch (RuntimeException e) {
0185             // ignored
0186         }
0187         return buffer.toString();
0188     }
0189 
0190     /**
0191      * Reads contents of a {@link Reader} into a string buffer. Reader is not closed.
0192      *
0193      @param   in          This reader will be loaded.
0194      @param   buffer      Buffer to fill with file contents.
0195      @throws  IOException File exception occurred.
0196      */
0197     public static void loadReader(final Reader in, final StringBuffer buffer)
0198             throws IOException {
0199 
0200         buffer.setLength(0);
0201         int c;
0202         while ((c = in.read()) >= 0) {
0203             buffer.append((charc);
0204         }
0205     }
0206 
0207     /**
0208      * Reads contents of a file into a string buffer. Uses default encoding.
0209      *
0210      @param   file        This file will be loaded.
0211      @param   buffer      Buffer to fill with file contents.
0212      @throws  IOException File exception occurred.
0213      *
0214      @deprecated  Use {@link #loadFile(File, StringBuffer, String)}.
0215      */
0216     public static void loadFile(final File file,
0217             final StringBuffer buffer)
0218             throws IOException {
0219 
0220         final int size = (intfile.length();
0221         final char[] data = new char[size];
0222         buffer.setLength(0);
0223         FileReader in = null;
0224         try {
0225             in = new FileReader(file);
0226             int charsread = 0;
0227             while (charsread < size) {
0228                 charsread += in.read(data, charsread, size - charsread);
0229             }
0230         finally {
0231             close(in);
0232         }
0233         buffer.insert(0, data);
0234     }
0235 
0236     /**
0237      * Reads contents of a file into a string buffer.
0238      *
0239      @param   file        This file will be loaded.
0240      @param   buffer      Buffer to fill with file contents.
0241      @param   encoding    Take this encoding.
0242      @throws  IOException File exception occurred.
0243      */
0244     public static void loadFile(final File file,
0245             final StringBuffer buffer, final String encoding)
0246             throws IOException {
0247 
0248         buffer.setLength((intfile.length());    // ensure capacity
0249         buffer.setLength(0);
0250         final InputStreamReader in = new InputStreamReader(new FileInputStream(file), encoding);
0251         final char[] data = new char[10 1024];
0252 
0253         try {
0254             int charsread = 0;
0255             while ((charsread = in.read(data, 0, data.length))) {
0256                 buffer.append(data, 0, charsread);
0257             }
0258         finally {
0259             in.close();
0260         }
0261     }
0262 
0263     /**
0264      * Reads a file and returns the contents as a <code>String</code>.
0265      *
0266      @param   file        File to load from.
0267      @return  Contents of file.
0268      @throws  IOException File exception occurred.
0269      */
0270     public static final byte[] loadFileBinary(final File filethrows IOException {
0271         final int size = (intfile.length();
0272         final FileInputStream in = new FileInputStream(file);
0273         try {
0274             final byte[] data = new byte[size];
0275             int charsread = 0;
0276             while (charsread < size) {
0277                 final int read = in.read(data, charsread, size - charsread);
0278                 if (read == -1) {
0279                     final byte[] result = new byte[charsread];
0280                     System.arraycopy(data, 0, result, 0, charsread);
0281                     return result;
0282                 }
0283                 charsread += read;
0284             }
0285             in.close();
0286             return data;
0287         finally {
0288             close(in);
0289         }
0290     }
0291 
0292 
0293     /**
0294      * Reads contents of an URL into a string buffer. The filling is character set dependent.
0295      * Content is added to the end of buffer. (Existing data is not cleared.)
0296      <p>
0297      * All parameters should not be <code>null</code>.
0298      @param   url         This URL will be loaded.
0299      @param   buffer      Buffer to fill with file contents.
0300      @throws  IOException Reading failed.
0301      *
0302      @deprecated  Choose correct encoding.
0303      */
0304     public static void loadFile(final URL url, final StringBuffer bufferthrows IOException {
0305         InputStream in = null;
0306         BufferedReader dis = null;
0307         try {
0308             in = url.openStream();
0309             dis = new BufferedReader(new InputStreamReader(in));
0310             int i;
0311             while ((i = dis.read()) != -1) {
0312                 buffer.append((chari);
0313             }
0314         finally {
0315             close(in);
0316             close(dis);
0317         }
0318     }
0319 
0320     /**
0321      * Reads contents of an URL into a StringBuffer. The filling is character set dependent. The
0322      * buffer is not cleared, contents is just added.
0323      <p>
0324      * All parameters should not be <code>null</code>.
0325      @param   url         This URL will be loaded.
0326      @param   buffer      Buffer to fill with file contents.
0327      @param   encoding    Take this encoding.
0328      @throws  IOException Reading failed.
0329      */
0330     public static void loadFile(final URL url, final StringBuffer buffer, final String encoding)
0331             throws IOException {
0332         InputStream in = null;
0333         BufferedReader dis = null;
0334         try {
0335             in = url.openStream();
0336             dis = new BufferedReader(new InputStreamReader(in, encoding));
0337             int i;
0338             while ((i = dis.read()) != -1) {
0339                 buffer.append((chari);
0340             }
0341         finally {
0342             close(in);
0343             close(dis);
0344         }
0345     }
0346 
0347     /**
0348      * Save binary contents of an URL into a file. Existing files are overwritten.
0349      *
0350      @param   url     This URL will be loaded.
0351      @param   file    Write into this file.
0352      @throws  IOException Reading or writing failed.
0353      */
0354     public static void saveFile(final URL url, final File filethrows IOException {
0355         saveFile(url.openStream(), file);
0356     }
0357 
0358     /**
0359      * Save binary contents of an input stream into a file. The input stream is closed even
0360      * if exceptions occur.  Existing files are overwritten.
0361      @param   in      Read this stream.
0362      @param   file    Write into this file.
0363      *
0364      @throws  IOException Reading or writing failed.
0365      */
0366     public static void saveFile(final InputStream in, final File filethrows IOException {
0367         FileOutputStream out = null;
0368         try {
0369             out = new FileOutputStream(file);
0370             final byte[] data = new byte[1024];
0371             int length;
0372             while ((length = in.read(data)) != -1) {
0373                 out.write(data, 0, length);
0374             }
0375         finally {
0376             close(in);
0377             close(out);
0378         }
0379     }
0380 
0381     /**
0382      * Convert String into a {@link Reader}.
0383      *
0384      * <a href="http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4094886">
0385      * Bug ID: 4094886</a>
0386      *
0387      @param   data    Convert this.
0388      @return  Resulting reader.
0389      */
0390     public static final Reader stringToReader(final String data) {
0391         try {
0392             return new InputStreamReader(new ByteArrayInputStream(data.getBytes("ISO-8859-1")));
0393         catch (UnsupportedEncodingException e) {
0394             throw new RuntimeException(e);
0395         }
0396     }
0397 
0398     /**
0399      * Saves a <code>String</code> into a file. Existing files are overwritten.
0400      *
0401      @param   filename    Name of the file (could include path).
0402      @param   text        Data to save in the file.
0403      @throws  IOException File exception occurred.
0404      *
0405      @deprecated  Use {@link #saveFile(File, String, String)} that has an encoding.
0406      */
0407     public static void saveFile(final String filename, final String text)
0408             throws IOException {
0409         saveFile(new File(filename), text);
0410     }
0411 
0412     /**
0413      * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten.
0414      *
0415      @param   filename    Name of the file (could include path).
0416      @param   text        Data to save in the file.
0417      @throws  IOException File exception occurred.
0418      *
0419      @deprecated  Use {@link #saveFile(File, StringBuffer, String)} that has an encoding.
0420      */
0421     public static void saveFile(final String filename, final StringBuffer text)
0422             throws IOException {
0423         saveFile(new File(filename), text.toString());
0424     }
0425 
0426     /**
0427      * Saves a <code>StringBuffer</code> in a file. Existing files are overwritten.
0428      *
0429      @param   file        File to save into.
0430      @param   text        Data to save in the file.
0431      @throws  IOException File exception occurred.
0432      *
0433      @deprecated  Use {@link #saveFile(File, StringBuffer, String)} that has an encoding
0434      * parameter.
0435      */
0436     public static void saveFile(final File file, final StringBuffer text)
0437             throws IOException {
0438         saveFile(file, text.toString());
0439     }
0440 
0441     /**
0442      * Saves a <code>String</code> in a file. Uses default encoding.  Existing files are
0443      * overwritten.
0444      *
0445      @param   file        File to save the data in.
0446      @param   text        Data to save in the file.
0447      @throws  IOException File exception occurred.
0448      *
0449      @deprecated  Use {@link #saveFile(File, String, String)} that has an encoding parameter.
0450      */
0451     public static void saveFile(final File file, final String text)
0452             throws IOException {
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         BufferedWriter out = new BufferedWriter(
0486             new OutputStreamWriter(new FileOutputStream(file), encoding));
0487         try {
0488             out.write(text);
0489         finally {
0490             out.close();
0491         }
0492     }
0493 
0494     /**
0495      * Saves a <code>data</code> in a file. Existing files are overwritten.
0496      *
0497      @param   file        File to save the data in.
0498      @param   data        Data to save in the file.
0499      @throws  IOException File exception occurred.
0500      */
0501     public static void saveFileBinary(final File file, final byte[] data)
0502             throws IOException {
0503         BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
0504         try {
0505             out.write(data);
0506         finally {
0507             out.close();
0508         }
0509     }
0510 
0511     /**
0512      * Copies a file to a different location.
0513      *
0514      @param   from    Copy source.
0515      @param   to      Copy destination.
0516      @throws  IOException File exception occurred.
0517      */
0518     public static void copyFile(final File from, final File to)
0519             throws IOException {
0520 
0521         if (from.getCanonicalFile().equals(to.getCanonicalFile())) {
0522             return;
0523         }
0524         createNecessaryDirectories(to);
0525         FileInputStream in = null;
0526         FileOutputStream out = null;
0527         try {
0528             in = new FileInputStream(from);
0529             out = new FileOutputStream(to);
0530 
0531             byte[] data = new byte[1024];
0532             int length;
0533             while ((length = in.read(data)) != -1) {
0534                 out.write(data, 0, length);
0535             }
0536         finally {
0537             close(in);
0538             close(out);
0539         }
0540     }
0541 
0542     /**
0543      * Copy one directory to another location.
0544      * If targetLocation does not exist, it will be created.
0545      *
0546      @param   sourceLocation  Copy from here.
0547      @param   targetLocation  Copy to this location
0548      @throws  IOException     Something went wrong.
0549      */
0550     public static void copy(final String sourceLocation, final String targetLocation)
0551             throws IOException {
0552         copyDirectory(new File(sourceLocation)new File(targetLocation));
0553     }
0554 
0555     /**
0556      * Copy one directory to another location.
0557      * If targetLocation does not exist, it will be created.
0558      *
0559      @param   sourceLocation  Copy from here.
0560      @param   targetLocation  Copy to this location
0561      @throws  IOException     Something went wrong.
0562      */
0563     public static void copyDirectory(final File sourceLocation, final File targetLocation)
0564             throws IOException {
0565 
0566         if (sourceLocation.isDirectory()) {
0567             if (!targetLocation.exists()) {
0568                 targetLocation.mkdir();
0569             }
0570             String[] children = sourceLocation.list();
0571             for (int i = 0; i < children.length; i++) { // recursive call for all children
0572                 copyDirectory(new File(sourceLocation, children[i]),
0573                         new File(targetLocation, children[i]));
0574             }
0575         else {    // copy file
0576             copyFile(sourceLocation, targetLocation);
0577         }
0578     }
0579 
0580     /**
0581      * List all matching files. Includes all matching sub directories recursively.
0582      *
0583      @param   sourceLocation  Check all files.
0584      @param   filter          Accept only these files.
0585      @return  List of matching files.
0586      @throws  IOException     Something went wrong.
0587      */
0588     public static List listFilesRecursively(final File sourceLocation, final FileFilter filter)
0589             throws IOException {
0590         final List result = new ArrayList();
0591         if (filter.accept(sourceLocation)) {
0592             if (sourceLocation.isDirectory()) {
0593                 File[] children = sourceLocation.listFiles();
0594                 for (int i = 0; i < children.length; i++) { // recursive call for all children
0595                     result.addAll(listFilesRecursively(children[i], filter));
0596                 }
0597             else {
0598                 result.add(sourceLocation);
0599             }
0600         }
0601         return result;
0602     }
0603 
0604     /**
0605      * Compare two files binary.
0606      *
0607      @param   from    Compare source. This file must be <code>null</code> or be an existing file.
0608      @param   with    Compare with this file. This file must be <code>null</code> or be an
0609      *                  existing file.
0610      @return  Is the contents of the two files binary equal?
0611      @throws  IOException File exception occurred.
0612      */
0613     public static boolean compareFilesBinary(final File from, final File with)
0614             throws IOException {
0615         if (from == null && with == null) {
0616             return true;
0617         }
0618         if (from == null || with == null) {
0619             return false;
0620         }
0621         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0622             return true;
0623         }
0624         if (from.length() != with.length()) {
0625             return false;
0626         }
0627         byte[] dataOne = new byte[1024];
0628         byte[] dataTwo = new byte[1024];
0629         int length;
0630 
0631         FileInputStream one = null;
0632         FileInputStream two = null;
0633         try {
0634             one = new FileInputStream(from);
0635             two = new FileInputStream(with);
0636 
0637             while ((length = one.read(dataOne)) != -1) {
0638                 if (length != two.read(dataTwo)) {
0639                     return false;
0640                 }
0641                 if (!Arrays.equals(dataOne, dataTwo)) {
0642                     return false;
0643                 }
0644             }
0645             return true;
0646         finally {
0647             close(one);
0648             close(two);
0649         }
0650     }
0651 
0652     /**
0653      * Compare two text files. Ignores different line separators. As there are:
0654      * LF, CR, CR + LF, NEL, FF, LS, PS.
0655      *
0656      @param   from        Compare source.
0657      @param   with        Compare with this file.
0658      @param   encoding    Use this character encoding. Must not be <code>null</code>.
0659      @return  Is the contents of the two text files equal?
0660      @throws  IOException File exception occurred or encoding is not supported.
0661      @throws  NullPointerException    Is encoding different from <code>null</code>?
0662      */
0663     public static boolean compareTextFiles(final File from, final File with, final String encoding)
0664             throws IOException {
0665         if (from == null && with == null) {
0666             return true;
0667         }
0668         if (from == null || with == null) {
0669             return false;
0670         }
0671         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0672             return true;
0673         }
0674 
0675         BufferedReader one = null;
0676         BufferedReader two = null;
0677         FileInputStream fromIn = null;
0678         FileInputStream withIn = null;
0679         try {
0680             fromIn = new FileInputStream(from);
0681             one = new BufferedReader(new InputStreamReader(fromIn, encoding));
0682             withIn = new FileInputStream(with);
0683             two = new BufferedReader(new InputStreamReader(withIn, encoding));
0684 
0685             boolean crOne = false;
0686             boolean crTwo = false;
0687             do {
0688                 int readOne = one.read();
0689                 int readTwo = two.read();
0690                 if (readOne == readTwo) {
0691                     if (readOne < 0) {
0692                         break;
0693                     }
0694                 else {
0695                     crOne = readOne == 0x0D;
0696                     crTwo = readTwo == 0x0D;
0697                     if (crOne) {
0698                         readOne = one.read();
0699                     }
0700                     if (crTwo) {
0701                         readTwo = two.read();
0702                     }
0703                     if (crOne && readOne != 0x0A && isCr(readTwo)) {
0704                         readTwo = two.read();
0705                     }
0706                     if (crTwo && readTwo != 0x0A && isCr(readOne)) {
0707                         readOne = one.read();
0708                     }
0709                     if (readOne != readTwo && (!isCr(readOne&& !isCr(readTwo))) {
0710                         return false;
0711                     }
0712                 }
0713             while (true);
0714             return true;
0715         finally {
0716             close(fromIn);
0717             close(one);
0718             close(two);
0719             close(withIn);
0720         }
0721     }
0722 
0723     /**
0724      * Compare two text files. Ignores different line separators. As there are:
0725      * LF, CR, CR + LF, NEL, FF, LS, PS.
0726      *
0727      @param   from        Compare source.
0728      @param   with        Compare with this file.
0729      @param   startAtLine Start comparing at this line (beginning with 0).
0730      @param   encoding    Use this character encoding. Must not be <code>null</code>.
0731      @return  Is the contents of the two text files equal?
0732      @throws  IOException File exception occurred or encoding is not supported.
0733      @throws  NullPointerException    Is encoding different from <code>null</code>?
0734      */
0735     public static boolean compareTextFiles(final File from, final File with, final int startAtLine,
0736             final String encodingthrows IOException {
0737 
0738         if (from == null && with == null) {
0739             return true;
0740         }
0741         if (from == null || with == null) {
0742             return false;
0743         }
0744         if (from.getAbsoluteFile().equals(with.getAbsoluteFile())) {
0745             return true;
0746         }
0747 
0748         BufferedReader one = null;
0749         BufferedReader two = null;
0750         FileInputStream fromIn = null;
0751         FileInputStream withIn = null;
0752         try {
0753             fromIn = new FileInputStream(from);
0754             one = new BufferedReader(new InputStreamReader(fromIn, encoding));
0755             withIn = new FileInputStream(with);
0756             two = new BufferedReader(new InputStreamReader(withIn, encoding));
0757             int pos = 0;
0758             do {
0759                 String lineOne = one.readLine();
0760                 String lineTwo = two.readLine();
0761                 if (lineOne == null) {
0762                     if (lineTwo == null) {
0763                         break;
0764                     }
0765                     return false;
0766                 }
0767                 if (pos++ >= startAtLine && !lineOne.equals(lineTwo)) {
0768                     return false;
0769                 }
0770             while (true);
0771             return true;
0772         finally {
0773             close(fromIn);
0774             close(one);
0775             close(two);
0776             close(withIn);
0777         }
0778     }
0779 
0780     /**
0781      * Test if character is LF, CR, NEL, FF, LS, PS.
0782      @param   c   Character to test.
0783      @return  Is character a line terminator?
0784      */
0785     private static boolean isCr(final int c) {
0786         return c == 0x0A || c == 0x0D || c == 0x85 || c == 0x0C || c == 0x2028 || c == 0x2029;
0787     }
0788 
0789     /**
0790      * Delete file directory recursive.
0791      *
0792      @param   directory   Directory to delete.
0793      @param   deleteDir   Delete directory itself too?
0794      @return  Was deletion successful?
0795      */
0796     public static boolean deleteDir(final File directory, final boolean deleteDir) {
0797         // to see if this directory is actually a symbolic link to a directory,
0798         // we want to get its canonical path - that is, we follow the link to
0799         // the file it's actually linked to
0800         File candir;
0801         try {
0802             candir = directory.getCanonicalFile();
0803         catch (IOException e) {
0804             return false;
0805         }
0806 
0807         // a symbolic link has a different canonical path than its actual path,
0808         // unless it's a link to itself
0809         if (!candir.equals(directory.getAbsoluteFile())) {
0810             // this file is a symbolic link, and there's no reason for us to
0811             // follow it, because then we might be deleting something outside of
0812             // the directory we were told to delete
0813             return false;
0814         }
0815 
0816         // now we go through all of the files and subdirectories in the
0817         // directory and delete them one by one
0818         boolean success = true;
0819         File[] files = candir.listFiles();
0820         if (files != null) {
0821             for (int i = 0; i < files.length; i++) {
0822                 File file = files[i];
0823 
0824                 // in case this directory is actually a symbolic link, or it's
0825                 // empty, we want to try to delete the link before we try
0826                 // anything
0827                 boolean deleted = file.delete();
0828                 if (!deleted) {
0829                     // deleting the file failed, so maybe it's a non-empty
0830                     // directory
0831                     if (file.isDirectory()) {
0832                         deleted = deleteDir(file, true);
0833                     }
0834 
0835                     // otherwise, there's nothing else we can do
0836                 }
0837                 success = success && deleted;
0838             }
0839         }
0840 
0841         // now that we tried to clear the directory out, we can try to delete it
0842         // again
0843         if (deleteDir) {
0844             return directory.delete();
0845         }
0846         return success;
0847     }
0848 
0849     /**
0850      * Delete directory contents for all files that match the filter. The main directory itself is
0851      * not deleted.
0852      *
0853      @param   directory   Directory to scan for files to delete.
0854      @param   filter      Filter files (and directories) to delete.
0855      @return  Was deletion successful?
0856      */
0857     public static boolean deleteDir(final File directory, final FileFilter filter) {
0858         // to see if this directory is actually a symbolic link to a directory,
0859         // we want to get its canonical path - that is, we follow the link to
0860         // the file it's actually linked to
0861         File candir;
0862         try {
0863             candir = directory.getCanonicalFile();
0864         catch (IOException e) {
0865             return false;
0866         }
0867 
0868         // a symbolic link has a different canonical path than its actual path,
0869         // unless it's a link to itself
0870         if (!candir.equals(directory.getAbsoluteFile())) {
0871             // this file is a symbolic link, and there's no reason for us to
0872             // follow it, because then we might be deleting something outside of
0873             // the directory we were told to delete
0874             return false;
0875         }
0876 
0877         // now we go through all of the files and subdirectories in the
0878         // directory and delete them one by one
0879         boolean success = true;
0880         File[] files = candir.listFiles(filter);
0881         if (files != null) {
0882             for (int i = 0; i < files.length; i++) {
0883                 File file = files[i];
0884 
0885                 // in case this directory is actually a symbolic link, or it's
0886                 // empty, we want to try to delete the link before we try
0887                 // anything
0888                 boolean deleted = file.delete();
0889                 if (!deleted) {
0890                     // deleting the file failed, so maybe it's a non-empty
0891                     // directory
0892                     if (file.isDirectory()) {
0893                         deleted = deleteDir(file, true);
0894                     }
0895 
0896                     // otherwise, there's nothing else we can do
0897                 }
0898                 success = success && deleted;
0899             }
0900         }
0901 
0902         return success;
0903     }
0904 
0905     /**
0906      * Print current system properties to System.out.
0907      */
0908     public static void printAllSystemProperties() {
0909         Properties sysprops = System.getProperties();
0910         for (Enumeration e = sysprops.propertyNames(); e.hasMoreElements()) {
0911             String key = (Stringe.nextElement();
0912             String value = sysprops.getProperty(key);
0913             System.out.println(key + "=" + value);
0914         }
0915     }
0916 
0917     /**
0918      * Get home directory of user.
0919      *
0920      @return  Home directory of user.
0921      */
0922     public static File getUserHomeDirectory() {
0923         return new File((StringSystem.getProperties().get("user.home"));
0924     }
0925 
0926     /**
0927      * Convert file in URL.
0928      *
0929      @param   file    File.
0930      @return  URL.
0931      */
0932     public static URL toUrl(final File file) {
0933         try {
0934             return file.toURI().toURL();
0935         catch (MalformedURLException e) { // should only happen if there is a bug in the JDK
0936             throw new RuntimeException(e);
0937         }
0938     }
0939 
0940     /**
0941      * Convert URL path in file. Call this method with the value of URL.getQuery().
0942      *
0943      @param   path    Convert this URL path.
0944      @return  File.
0945      */
0946     public static File toFile(final String path) {
0947         try {
0948             return new File(URLDecoder.decode(path, "UTF-8"));
0949         catch (UnsupportedEncodingException e) {
0950             throw new RuntimeException(e);
0951         }
0952     }
0953 
0954     /**
0955      * Convert file in URL string.
0956      *
0957      @param   file    File.
0958      @return  URL string.
0959      */
0960     public static String toUrlString(final File file) {
0961         try {
0962             return file.toURI().toURL().toString();
0963         catch (MalformedURLException e) { // should only happen if there is a bug in the JDK
0964             throw new RuntimeException(e);
0965         }
0966     }
0967 
0968     /**
0969      * Creates necessary parent directories for a file.
0970      *
0971      @param   file    File.
0972      @throws  IOException Creation failed.
0973      */
0974     public static void createNecessaryDirectories(final File filethrows  IOException {
0975         if (file.getParentFile() != null) {
0976             file.getParentFile().mkdirs();
0977             if (!file.getParentFile().exists()) {
0978                 throw new IOException("creation of directory failed: " + file.getParent());
0979             }
0980         }
0981     }
0982 
0983     /**
0984      * Create relative address from <code>orgin</code> to <code>next</code>.
0985      *
0986      @param   orgin   this is the original location
0987      @param   next    this should be the next location
0988      @return  relative (or if necessary absolute) file path
0989      */
0990     public static final String createRelativePath(final File orgin, final File next) {
0991         try {
0992             if (orgin.equals(next)) {
0993                 return "";
0994             }
0995             try {
0996                 String org = orgin.getCanonicalPath().replace('\\''/');
0997                 if (!org.endsWith("/")) {
0998                     org += ('/');
0999                 }
1000                 String nex = next.getCanonicalPath().replace('\\''/');
1001                 if (!nex.endsWith("/")) {
1002                     nex += ('/');
1003                 }
1004                 if (org.equals(nex)) {
1005                     return "";
1006                 }
1007                 int i = -1// position of next '/'
1008                 int j = 0;  // position of last '/'
1009                 while (<= (i = org.indexOf("/", j))) {
1010                     if (i >= && nex.length() > i
1011                             && org.substring(j, i).equals(
1012                             nex.substring(j, i))) {
1013                         j = i + 1;
1014                     else {
1015                         break;
1016                     }
1017                 }
1018                 if (j > 0) {
1019                     i = j;
1020                     final StringBuffer result = new StringBuffer(nex.length());
1021                     while (<= (i = org.indexOf("/", i))) {
1022                         i++;
1023                         result.append("../");
1024                     }
1025                     result.append(nex.substring(j, nex.length() 1));
1026                     return result.toString();
1027                 }
1028                 return nex.substring(0, nex.length() 1);
1029             catch (RuntimeException e) {
1030                 return next.toString();
1031             }
1032         catch (IOException e) {
1033             return new File(orgin, next.getPath()).getPath();
1034         }
1035     }
1036 
1037     /**
1038      * Waits until a '\n' was read from System.in.
1039      */
1040     public static void waitln() {
1041         System.out.println("\n..press <return> to continue");
1042         try {
1043             (new java.io.BufferedReader(new java.io.InputStreamReader(
1044                 System.in))).readLine();
1045         catch (IOException e) {
1046             // ignore
1047         }
1048     }
1049 
1050     /**
1051      * Closes input stream without exception.
1052      *
1053      @param   in  Input stream, maybe <code>null</code>.
1054      */
1055     public static void close(final InputStream in) {
1056         if (in != null) {
1057             try {
1058                 in.close();
1059             catch (Exception e) {
1060                 // ignore
1061             }
1062         }
1063     }
1064 
1065     /**
1066      * Closes writer without exception.
1067      *
1068      @param   writer  Writer, maybe <code>null</code>.
1069      */
1070     public static void close(final Writer writer) {
1071         if (writer != null) {
1072             try {
1073                 writer.close();
1074             catch (Exception e) {
1075                 // ignore
1076             }
1077         }
1078     }
1079 
1080     /**
1081      * Closes out stream without exception.
1082      *
1083      @param   out Output stream, maybe <code>null</code>.
1084      */
1085     public static void close(final OutputStream out) {
1086         if (out != null) {
1087             try {
1088                 out.close();
1089             catch (Exception e) {
1090                 // ignore
1091             }
1092         }
1093     }
1094 
1095     /**
1096      * Closes input reader without exception.
1097      *
1098      @param   reader  Reader, maybe <code>null</code>.
1099      */
1100     public static void close(final Reader reader) {
1101         if (reader != null) {
1102             try {
1103                 reader.close();
1104             catch (Exception e) {
1105                 // ignore
1106             }
1107         }
1108     }
1109 
1110     /**
1111      * Get start directory for application. Within the start directory all newly created data is
1112      * stored in. If this is no Java Webstart version the result is
1113      <code>new File(".")</code>. Otherwise the start directory is the subdirectory
1114      * "." concatenated <code>application</code> within <code>user.home</code>.
1115      *
1116      @param   application Application name, used for Java Webstart version. Should
1117      *          be written in lowercase letters. A "." is automatically appended at
1118      *          the beginning.
1119      @return  Start directory for application.
1120      */
1121     public static final File getStartDirectory(final String application) {
1122         final File startDirectory;
1123         if (isWebStarted()) {
1124             final String userHome = System.getProperty("user.home"".");
1125             startDirectory = new File(new File(userHome)"." + application);
1126         else {
1127             startDirectory = new File(".");
1128         }
1129         return startDirectory;
1130     }
1131 
1132     /**
1133      * Was the application started by Java Webstart?
1134      *
1135      @return  Was the application started by Java Webstart.
1136      */
1137     public static final boolean isWebStarted() {
1138         final String webStart = (StringSystem.getProperties().get("javawebstart.version");
1139         return webStart != null;
1140     }
1141 
1142     /**
1143      * Loads a property file from given URL.
1144      *
1145      @param   url     URL to load properties from.
1146      @return  Loaded properties.
1147      @throws  IOException             Reading error.
1148      */
1149     public static Properties loadProperties(final URL url)
1150            throws IOException {
1151         Properties newprops = new Properties();
1152         InputStream in = null;
1153         try {
1154             in = url.openStream();
1155             newprops.load(in);
1156         finally {
1157             close(in);
1158         }
1159         return newprops;
1160     }
1161 
1162     /**
1163      * This method returns the contents of an object variable (even if it is private).
1164      *
1165      @param   obj     Object.
1166      @param   name    Variable name
1167      @return  Contents of variable.
1168      */
1169     public static Object getFieldContent(final Object obj, final String name) {
1170         final Field field;
1171         try {
1172             field = obj.getClass().getDeclaredField(name);
1173             field.setAccessible(true);
1174         catch (SecurityException e) {
1175             throw new RuntimeException(e);
1176         catch (NoSuchFieldException e) {
1177             throw new RuntimeException(e);
1178         }
1179         try {
1180             return field.get(obj);
1181         catch (IllegalArgumentException e) {
1182             throw new RuntimeException(e);
1183         catch (IllegalAccessException e) {
1184             throw new RuntimeException(e);
1185         }
1186     }
1187 
1188     /**
1189      * This method returns the contents of an object variable (even if it is private).
1190      *
1191      @param   obj     Object.
1192      @param   name    Variable name
1193      @return  Contents of variable.
1194      */
1195     public static Object getFieldContentSuper(final Object obj, final String name) {
1196         Field field = null;
1197         try {
1198             Class cl = obj.getClass();
1199             while (!Object.class.equals(cl)) {
1200                 try {
1201                     field = cl.getDeclaredField(name);
1202                     break;
1203                 catch (NoSuchFieldException ex) {
1204                     cl = cl.getSuperclass();
1205                 }
1206             }
1207             if (field == null) {
1208                 throw new NullPointerException("field not found: " + name);
1209             }
1210             field.setAccessible(true);
1211         catch (SecurityException e) {
1212             throw new RuntimeException(e);
1213         }
1214         try {
1215             return field.get(obj);
1216         catch (IllegalArgumentException e) {
1217             throw new RuntimeException(e);
1218         catch (IllegalAccessException e) {
1219             throw new RuntimeException(e);
1220         }
1221     }
1222 
1223     /**
1224      * This method sets the contents of an object variable (even if it is private).
1225      *
1226      @param   obj     Object.
1227      @param   name    Variable name.
1228      @param   value   Value to set.
1229      */
1230     public static void setFieldContent(final Object obj, final String name, final Object value) {
1231         final Field field;
1232         try {
1233             field = obj.getClass().getDeclaredField(name);
1234             field.setAccessible(true);
1235         catch (SecurityException e) {
1236             throw new RuntimeException(e);
1237         catch (NoSuchFieldException e) {
1238             throw new RuntimeException(e);
1239         }
1240         try {
1241             field.set(obj, value);
1242         catch (IllegalArgumentException e) {
1243             throw new RuntimeException(e);
1244         catch (IllegalAccessException e) {
1245             throw new RuntimeException(e);
1246         }
1247     }
1248 
1249     /**
1250      * Sleep my little class.
1251      *
1252      @param   ms  Milliseconds to wait.
1253      */
1254     public static void sleep(final int ms) {
1255         final Object monitor = new Object();
1256         synchronized (monitor) {
1257             try {
1258                 monitor.wait(ms);
1259             catch (InterruptedException e) {
1260             }
1261         }
1262     }
1263 
1264     /**
1265      * Get currently running java version and subversion numbers. This is the running JRE version.
1266      * If no version could be identified <code>null</code> is returned.
1267      *
1268      @return  Array of version and subversion numbers.
1269      */
1270     public static int[] getJavaVersion() {
1271         final String version = System.getProperty("java.version");
1272         final List numbers = new ArrayList();
1273         final StringTokenizer tokenizer = new StringTokenizer(version, ".");
1274         while (tokenizer.hasMoreElements()) {
1275             String sub = (Stringtokenizer.nextToken();
1276             for (int i = 0; i < sub.length(); i++) {
1277                 if (!Character.isDigit(sub.charAt(i))) {
1278                     sub = sub.substring(0, i);
1279                     break;
1280                 }
1281             }
1282             try {
1283                 numbers.add(new Integer(Integer.parseInt(sub)));
1284             catch (Exception e) {
1285                 e.printStackTrace();
1286                 break;
1287             }
1288         }
1289         if (numbers.size() == 0) {
1290             return null;
1291         }
1292         final int[] result = new int[numbers.size()];
1293         for (int i = 0; i < numbers.size(); i++) {
1294             result[i((Integernumbers.get(i)).intValue();
1295         }
1296         return result;
1297     }
1298 
1299     /**
1300      * Simplify file URL by returning a file path.
1301      *
1302      @param   url     URL to simplify.
1303      @return  File path (if protocol is "file"). Otherwise just return <code>url</code>.
1304      */
1305     public static String easyUrl(final String url) {
1306         String result = url;
1307         try {
1308             final URL u = new URL(url);
1309             // is this a file URL?
1310             if (u.getProtocol().equalsIgnoreCase("file")) {
1311                 return toFile(u.getFile()).getCanonicalPath();
1312             }
1313         catch (RuntimeException e) {
1314             //  ignore
1315         catch (IOException e) {
1316             //  ignore
1317         }
1318         return result;
1319     }
1320 
1321     /**
1322      * Get key sorted list of all System Properties.
1323      *
1324      @return  Array with the two columns key and value.
1325      */
1326     public static String[][] getSortedSystemProperties() {
1327         final Map map = new TreeMap(System.getProperties());
1328         String[][] rowData = new String[map.size()][2];
1329         int rowNum = 0;
1330         final Iterator iterator = map.entrySet().iterator();
1331         while (iterator.hasNext()) {
1332             Map.Entry entry = (Map.Entryiterator.next();
1333             rowData[rowNum][0(Stringentry.getKey();
1334             rowData[rowNum][1(Stringentry.getValue();
1335             rowNum++;
1336         }
1337         return rowData;
1338     }
1339 
1340 }