content: /*
  * Copyright (c) 1997 - 2016
  * Actelion Pharmaceuticals Ltd.
  * Gewerbestrasse 16
  * CH-4123 Allschwil, Switzerland
  *
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of the the copyright holder nor the
  *    names of its contributors may be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
  
  package com.actelion.research.util;
  
  
  import java.io.*;
  import java.nio.ByteBuffer;
  import java.nio.channels.Channels;
  import java.nio.channels.FileChannel;
  import java.nio.charset.StandardCharsets;
  import java.nio.file.NotDirectoryException;
  import java.text.DateFormat;
  import java.text.DecimalFormat;
  import java.text.NumberFormat;
  import java.text.SimpleDateFormat;
  import java.util.*;
  
  import com.actelion.research.io.StringReadChannel;
  import com.actelion.research.util.datamodel.DoubleArray;
  import com.actelion.research.util.datamodel.IntArray;
  
  /**
   * IO
   * 2003 MvK: Start implementation
   */
  public class IO {
  
  	// private static final NumberFormat NF = new DecimalFormat("000");
  
  	private static final int LIMIT_FILES_DIR = 10000;
  
  	public static final String SEP = System.getProperty("file.separator");
  
  
  
  	public static boolean canWriteAndDeleteInPath(File dir) throws IOException {
  		boolean w = false;
  		File f = File.createTempFile("test", ".txt", dir);
  		if(f.canWrite()){
  			w=true;
  		}
  		w = f.delete();
  		return w;
  	}
  
  
  	/**
  	 * Do not forget to close BufferedReader
  	 * 
  	 * @param sAbsolutePathIn
  	 *            path
  	 * @return BufferedReader
  	 */
  	public static BufferedReader getBufferedReader(String sAbsolutePathIn) throws FileNotFoundException {
  		BufferedReader bufferedReader = null;
  		if (sAbsolutePathIn.length() > 0) {
  			FileInputStream fis = new FileInputStream(sAbsolutePathIn);
  			InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
  			bufferedReader = new BufferedReader(isr);
  		}
  		return bufferedReader;
  	}
  
  
  	/**
  	 * A number is added to the base name of the file
  	 * 
  	 * @param totalpath
  	 * @return
  	 */
  	public static String getUniqueFileName(String totalpath) {
  		File fi = new File(totalpath);
  		return getUniqueFileName(fi, fi.getParentFile(), Formatter.dfI3).getAbsolutePath();
  	}
  	
  	public static File getUniqueFileName(File fiIn) {
  		return getUniqueFileName(fiIn, fiIn.getParentFile());
  	}
  	
  	public static File getUniqueFileName(String sFilename, String sDirDestination) {
  		return getUniqueFileName(new File(sFilename), new File(sDirDestination), Formatter.dfI3);
  	}
  	
  	public static File getUniqueFileName(File file, File dirDestination) {
  		return getUniqueFileName(file, dirDestination, Formatter.dfI3);
  	}
  	
  	/**
  	 * If the file does not exists the input file is returned.
  	 * @param file
  	 * @param dirDestination
  	 * @param df
  	 * @return
  	 */
  	public static File getUniqueFileName(File file, File dirDestination, DecimalFormat df) {
  
  		String sFileName = file.getName();
  		File fiNew = new File(dirDestination, sFileName);
  		if(!fiNew.exists()){
  			return fiNew;
  		}
  		String sBaseName = getBaseName(file);
  		String sExtension = getExtension(file);
  		String sBaseNameNumber = sBaseName;
  		int cc = 1;
  		while (fiNew.isFile()) {
  			sBaseNameNumber = next(sBaseNameNumber, df);
  			sFileName = sBaseNameNumber + sExtension;
  			fiNew = new File(dirDestination, sFileName);
  			cc++;
  			if (cc > LIMIT_FILES_DIR) {
  				sFileName = null;
  				IOException ex = new IOException("To many files in this dir.");
  				ex.printStackTrace();
  				break;
  			}
  		}
  		
  		return fiNew;
  	}
  
  	public static File getUniqueUserDir() throws IOException {
      	String sWorkdir = System.getProperty("user.home");
      	File dirParent = new File(sWorkdir);
      	return getUniqueDateDir(dirParent);
  	}
  	
  	public static File getUniqueDateDir(File dirParent) throws IOException {
  		return getUniqueDateDir(dirParent, ""); 
  	}
  	
  	public static File getUniqueDateDir(String appendix) throws IOException {
      	String sWorkdir = System.getProperty("user.home");
      	File dirParent = new File(sWorkdir);
      	return getUniqueDateDir(dirParent, appendix);
  	}
  	public static File getUniqueDateDir(File dirParent, String appendix) throws IOException {
  		
  		Date d = new Date();
  		DateFormat df = new SimpleDateFormat("yyMMdd");
  		String sDate = df.format(d);
  		String name = sDate + appendix;
  		File dir = new File(dirParent, name);
  		int index = 1;
  		while(dir.exists()){
  			name = sDate + "_" + index + "_" + appendix;
  			index++;
  			dir = new File(dirParent, name);
  		}
  		if(!dir.mkdirs()){
  			String e = "Not possible to make dir " + dir.getAbsolutePath() + ".";
  			throw new IOException(e);
  		}
  		return dir;
  	}
  	
  	public static File getUniqueDir(File dirParent, String suffix) throws IOException {
  		String name = suffix;
  		File dir = new File(dirParent, name);
  		int index = 1;
  		while(dir.exists()){
  			name = suffix+index;
  			index++;
  			dir = new File(dirParent, name);
  		}
  		
  		if(!dir.mkdirs()){
  			String e = "Not possible to make dir " + dir.getAbsolutePath() + ".";
  			throw new IOException(e);
  		}
  		
  		return dir;
  	}
  
  	/**
  	 * Has to be the total path of the file or there will be errors.
  	 * @param totalpath
  	 * @return
  	 */
  	public static String getNextFileName(String totalpath) {
  		File fi = new File(totalpath);
  		return getUniqueFileName(fi, fi.getParentFile(), Formatter.dfI3).getAbsolutePath();
  	}
  	
  	public static String next(String txt) {
  		return next(txt, Formatter.dfI3);
  	}
  	
  	/**
  	 * 
  	 * @param txt String with an integer num at the end. (blabla567) 
  	 * @return Adds one to the last number (blabla568). 
  	 * If no number in text 001 is added. 
  	 */
  	public static String next(String txt, DecimalFormat dfExtern) {
  		String s = "";
  		int ind = -1;
  		for (int i = txt.length() - 1; i >= 0; i--) {
  			if (!Character.isDigit(txt.charAt(i))) {
  				ind = i;
  				break;
  			}
  		}
  		s = txt.substring(0, ind + 1);
  		String sN = txt.substring(ind + 1, txt.length());
  		NumberFormat nfName = dfExtern;
  		int num = 0;
  		if (sN.length() > 0) {
  			num = Integer.parseInt(sN);
  			String patFormat = "";
  			for (int i = 0; i < sN.length(); i++) {
  				patFormat += "0";
  			}
  			nfName = new DecimalFormat(patFormat);
  		}
  		num++;
  		s += nfName.format(num);
  		return s;
  	}
  
  	public static BufferedWriter getBuffWrite(String sAbsolutePathOut,
  			boolean bAppend) throws IOException {
  
  		BufferedWriter bufferedWriter = null;
  		if (sAbsolutePathOut.length() > 0) {
  			FileOutputStream fos = new FileOutputStream(sAbsolutePathOut, bAppend);
  			OutputStreamWriter isw = new OutputStreamWriter(fos);
  			bufferedWriter = new BufferedWriter(isw);
  		} else {
  			OutputStreamWriter isw = new OutputStreamWriter(System.out);
  			bufferedWriter = new BufferedWriter(isw);
  		}
  		return bufferedWriter;
  	}
  
  	/**
  	 * 
  	 * @param str
  	 * @return base name without extension.
  	 */
  	public static String getBaseName(String str) {
  		File fi = new File(str);
  		String name = fi.getName();
  		int iIndexStart = name.lastIndexOf('\\');
  		if(iIndexStart==-1){
  			iIndexStart = name.lastIndexOf(File.separator);
  		}
  		if(iIndexStart==-1){
  			iIndexStart = 0;
  		}
  		int iIndexEnd = name.lastIndexOf('.');
  		String sBaseName = "";
  		if(iIndexEnd == -1)
  			iIndexEnd = name.length();
  		sBaseName = name.substring(iIndexStart, iIndexEnd);
  		return sBaseName;
  	}
  
  	public static String getBaseName(File file) {
  		return getBaseName(file.getAbsolutePath());
  	}
  
  	/**
  	 * 
  	 * @param file
  	 * @return the part after the the last '.' inclusive the '.'. 
  	 * Returns String with length null when no extension found.
  	 */
  	public static String getExtension(File file) {
  		String sName = file.getName();
  		int iIndexStartName = sName.lastIndexOf(SEP) + 1;
  		int iIndexStartExtension = sName.lastIndexOf('.');
  		if(iIndexStartExtension==-1){
  			return "";
  		}
  		String sExtension = sName.substring(iIndexStartExtension);
  		return sExtension;
  	}
  
  	public static void mkdirs(String path) throws IOException {
  		mkdirs(new File(path));
  	}
  	
  	public static void mkdirs(File dir) throws NotDirectoryException {
  		if (!dir.exists()) {
  			if(!dir.mkdirs()){
  				throw new NotDirectoryException("Not possible to make dir " + dir.getAbsolutePath() + ".");
  			}
  		}
  	}
  	
  	public static void readBetweenTags(String sAbsolutePathIn,
  		String sTagStartRegEx, 
  		String sTagEndRegEx, 
  		Vector<String> vecStringContent) {
  		try {
  			StringReadChannel channel = new StringReadChannel(Channels.newChannel(new FileInputStream(new File(sAbsolutePathIn))));
  			boolean bTagStartFound = false;
  			boolean bTagEndFound = false;
  			while (channel.hasMoreLines()) {
  				String sLine = channel.readLine();
  
  				if (sLine != null) {
  					if (sLine.matches(sTagStartRegEx)) {
  						bTagStartFound = true;
  						break;
  					}
  				}
  			}
  			if (bTagStartFound == true) {
  				while (channel.hasMoreLines()) {
  					String sLine = channel.readLine();
  					if (sLine != null) {
  						if (sLine.matches(sTagEndRegEx)) {
  							bTagEndFound = true;
  							break;
  						} else {
  							vecStringContent.addElement(sLine);
  						}
  					}
  				}
  			} else {
  				System.err.println("Tag: " + sTagStartRegEx + " not found");
  				(new RuntimeException()).printStackTrace();
  			}
  			channel.close();
  			if (!bTagEndFound) {
  				System.err.println("Tag: " + sTagEndRegEx + " not found");
  				(new RuntimeException()).printStackTrace();
  			}
  		} catch (IOException e) {
  			System.err.println(e);
  		} catch (Exception e) {
  			System.err.println(e);
  		}
  
  	}
  
  	/**
  	 * Reads all lines after a given tag and stores the lines as Strings in a
  	 * vector object.
  	 * 
  	 * @param sAbsolutePathIn
  	 *            path of the input file
  	 * @param sTagRegEx
  	 *            the tag as regular expression
  	 * @param vecStringContent
  	 *            contains the result of the read in.
  	 */
  	public static void readFromTag(String sAbsolutePathIn, String sTagRegEx, Vector<String> vecStringContent) {
  		try {
  			StringReadChannel channel = new StringReadChannel(Channels.newChannel(new FileInputStream(new File(sAbsolutePathIn))));
  			boolean bTagFound = false;
  			String sLine = "";
  			while (channel.hasMoreLines()) {
  				sLine = channel.readLine();
  
  				if (sLine != null) {
  					if (sLine.matches(sTagRegEx)) {
  						bTagFound = true;
  						break;
  					}
  				}
  			}
  			if (bTagFound == true) {
  				while (channel.hasMoreLines()) {
  					sLine = channel.readLine();
  
  					if (sLine != null) {
  						if (sLine.length() < 1) {
  							break;
  						} else {
  							vecStringContent.addElement(sLine);
  						}
  					}
  				}
  			} else {
  				System.err.println("Tag: " + sTagRegEx + " not found");
  				(new RuntimeException()).printStackTrace();
  			}
  			channel.close();
  		} catch (IOException e) {
  			System.err.println(e);
  		} catch (Exception e) {
  			System.err.println(e);
  		}
  	}
  	
      public static void skipUntilLineMatchesRegEx(InputStream in, String regex)throws Exception {
      	int limit = 10000;
      	int cc=0;
      	String line = readLine(in);
      	while(!line.matches(regex)){
      		line = readLine(in);
      		if(cc==limit) {
      			throw new Exception("Limit of " + limit + " lines exceeded.");
      		}
      		cc++;
      	}
      }
  
  	public static String read(InputStream is) throws IOException {
  		BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
  		String line=null;
  		StringBuilder sb = new StringBuilder();
  		while ((line = reader.readLine())!=null) {
  			if(sb.length()>0)
  				sb.append("\n");
  			sb.append(line);
  		}
  		reader.close();
  		return sb.toString();
  	}
  	
  	public static List<Integer> readListInteger(File fiTxt) throws IOException {
  		Scanner scanner = new Scanner(fiTxt);
  	    List<Integer> li = new ArrayList<Integer>();
  	    while (scanner.hasNextInt()) {
  	        li.add(scanner.nextInt());
  	    }
  	    scanner.close();
  	    return li;
  	}
  
  	/**
  	 * Reads a file that contains one int per line.
  	 * @param fiIntLineWise
  	 * @return
  	 * @throws IOException
  	 */
  	public static int [] readLines2IntArray(File fiIntLineWise) throws IOException{
  		IntArray intArray = new IntArray();
  		BufferedReader br = new BufferedReader(new FileReader(fiIntLineWise));
  		String line = "";
  		while((line=br.readLine()) != null){
  			int val = Integer.parseInt(line);
  			intArray.add(val);
  		}
  		br.close();
  		return intArray.get();
  	}
  
  	public static double [] readLines2DoubleArray(File fiIntLineWise) throws IOException{
  		DoubleArray doubleArray = new DoubleArray();
  		BufferedReader br = new BufferedReader(new FileReader(fiIntLineWise));
  		String line = "";
  		while((line=br.readLine()) != null){
  			double v = Double.parseDouble(line);
  			doubleArray.add(v);
  		}
  		br.close();
  		return doubleArray.get();
  	}
  
  	public static String readLine(InputStream is) throws IOException {
  		StringBuilder sb = new StringBuilder();
  		int c = -1;
  		while((c = is.read()) != '\n'){
  			if(c==-1)
  				break;
  			sb.append((char)c);
  		}
      	String str = StringFunctions.removeCharacter(sb, '\r');
      	return str;
  	}
  
  
  	public static String read(File file) throws IOException {
  		BufferedReader reader = new BufferedReader(new FileReader(file));
  		String line=null;
  		StringBuilder sb = new StringBuilder();
  		while ((line = reader.readLine())!=null) {
  			if(sb.length()>0)
  				sb.append("\n");
  			sb.append(line);
  		}
  		reader.close();
  		return sb.toString();
  	}
  
      public static String readLine(Reader is) throws IOException {
  		StringBuffer b = new StringBuffer();
  		int c=0;
  		c=is.read();
  		while((c != -1)&&(c != '\n')) {
  			b.append((char)c);
  			c=is.read();
  		}
  		return b.toString();
  	}
  
  	public static String readLine(FileChannel fc) throws IOException {
  		ByteBuffer buf = ByteBuffer.allocate(1000);
  		StringBuilder sb = new StringBuilder();
  		boolean bEOF=false;
  		boolean bEOLine=false;
  		long posFCPos = fc.position();
  		while (!bEOF){
  			buf.position(0);
  			int size = fc.read(buf);
  			if(size==-1){
  				break;
  			}
  			for (int i = 0; i < size; i++) {
  				int c = buf.get(i);
  				posFCPos++;
  				if(c==-1) {
  					bEOF=true;
  				} else {
  					if(c=='\n') {
  						fc.position(posFCPos);
  						bEOLine=true;
  						break;
  					} else {
  						sb.append((char)c);
  					}
  				}
  			}
  			if(bEOLine)
  				break;
  		}
  		return sb.toString();
  	}
  
  	public static String readFirstLine(File file) throws IOException {
  		BufferedReader reader = new BufferedReader(new FileReader(file));
  		String line = reader.readLine();
  		reader.close();
  		return line;
  	}
  	public static List<String> readLines2List(File file) throws IOException {
  		List<String> li = new ArrayList<String>();
  		BufferedReader reader = new BufferedReader(new FileReader(file));
  		String line = null;
  		while ((line = reader.readLine())!=null) {
  			li.add(line);
  		}
  		reader.close();
  		return li;
  	}
  
  	/**
  	 * The stream is not closed.
  	 * @param is
  	 * @return
  	 * @throws IOException
  	 */
  	public static List<String> readLines2List(InputStream is) throws IOException {
  		List<String> li = new ArrayList<String>();
  		BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
  		String line = null;
  		while ((line = reader.readLine())!=null) {
  			li.add(line);
  		}
  		return li;
  	}
  
  	public static List<String> readLines2List(List<File> liFile) throws IOException {
  		List<String> li = new ArrayList<String>();
  		for (File fi : liFile) {
  			li.addAll(readLines2List(fi));
  		}
  		return li;
  	}
  
  	public static void write(String sAbsolutePathOut, String sContent) {
  		write(sAbsolutePathOut, sContent, true);
  	}
  
  	public static void write(String sAbsolutePathOut, String sContent, boolean bAppend) {
  		write(new File(sAbsolutePathOut), sContent, bAppend);
  	}
  	
  	public static void write(File file, String sContent) {
  		write(file, sContent, false);
  	}
  
  	public static void write(File file, String sContent, boolean bAppend) {
  		try {
  			BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, bAppend));
  			bufferedWriter.write(sContent);
  			bufferedWriter.close();
  		} catch (IOException ex) {
  			throw new RuntimeException(ex);
  		}
  	}
  	
  	public void write2Channel(FileChannel fc, String str) throws IOException {
      	ByteBuffer buf = ByteBuffer.allocate(str.length());
          byte[] bytes = str.getBytes();
          buf.put(bytes);
          buf.flip();
          fc.write(buf);
      }
  
  	/**
  	 * Writes each string in a separate line
  	 * @param file
  	 * @param li
  	 * @throws IOException
  	 */
  	public static void write(File file, List<String> li) throws IOException {
  		FileWriter fw = new FileWriter(file);
  		for (int i = 0; i < li.size(); i++) {
  			fw.write(li.get(i));
  			if(i<li.size()-1)
  				fw.write("\n");	
  		}
  		fw.close();
  	}
  	
  	public static void writeIntegerList(File file, List<Integer> li) throws IOException {
  		FileWriter fw = new FileWriter(file);
  		for (int i = 0; i < li.size(); i++) {
  			fw.write(li.get(i).toString());
  			if(i<li.size()-1)
  				fw.write("\n");	
  		}
  		fw.close();
  	}
  
  	public static void write(File file, int [] arr) throws IOException {
  		BufferedWriter fw = new BufferedWriter(new FileWriter(file));
  		for (int i = 0; i < arr.length; i++) {
  			fw.write(Integer.toString(arr[i]));
  			if(i<arr.length-1)
  				fw.write("\n");
  		}
  		fw.close();
  	}
  
  	public static void write(File file, double [] arr) throws IOException {
  		BufferedWriter fw = new BufferedWriter(new FileWriter(file));
  		for (int i = 0; i < arr.length; i++) {
  			fw.write(Double.toString(arr[i]));
  			if(i<arr.length-1)
  				fw.write("\n");
  		}
  		fw.close();
  	}
  
  	public static void saveObject(String file, Object o) throws IOException {
  		FileOutputStream out = new FileOutputStream(file);
  		ObjectOutputStream s = new ObjectOutputStream(out);
  		s.writeObject(o);
  		out.close();
  	}
  
  	public static Object loadObject(String file) throws IOException, ClassNotFoundException {
  		FileInputStream in = new FileInputStream(file);
  		ObjectInputStream s = new ObjectInputStream(in);
  		Object o = s.readObject();
  		in.close();
  		return o;
  	}
  
  	public static String readerToString(Reader reader) throws IOException {
  		return  readerToString(reader, Integer.MAX_VALUE);
  	}
  
  	public static String readerToString(Reader reader, int maxSize) throws IOException {
  		if(maxSize<=0) maxSize = Integer.MAX_VALUE;
  		char[] buf = new char[512];
  		int c;
  		StringBuilder sb = new StringBuilder();
  		while(sb.length()<maxSize && ( c = reader.read(buf, 0, Math.min(buf.length, maxSize-sb.length()))) > 0) {
  			sb.append(buf, 0, c);
  		}
  		return sb.toString();
  	}
  
  	public static String streamToString(InputStream is) throws IOException {
  		byte[] buf = new byte[512];
  		int c;
  		StringBuilder sb = new StringBuilder();
  		while(( c = is.read(buf)) > 0) {
  			sb.append(new String(buf, 0, c));
  		}
  		return sb.toString();
  	}
  
  	public static byte[] getBytes(File f) throws IOException {
  		FileInputStream is = new FileInputStream(f);
  		byte[] res = new byte[(int) f.length()];
  		is.read(res, 0, res.length);
  		is.close();
  		return res;
  	}
  
  	public static void bytesToFile(byte[] bytes, File f) throws IOException {
  		FileOutputStream os = new FileOutputStream(f);
  		os.write(bytes);
  		os.close();
  	}
  
  	public static void stringToFile(String s, File f) throws IOException {
  		try (FileWriter os = new FileWriter(f)) {
  			os.write(s);
  		}
  	}
  
  	public static String fileToString(File f) throws IOException {
  		return fileToString(f, Integer.MAX_VALUE);
  	}
  
  	public static String fileToString(File f, int maxSize) throws IOException {
  		try (FileReader reader = new FileReader(f)) {
  			return readerToString(reader, maxSize);
  		}
  	}
  
  	/**
  	 * Redirects the streams, without closing them
  	 * @param is
  	 * @param os
  	 * @throws IOException
  	 */
  	public static void redirect(InputStream is, OutputStream os) throws IOException {
  		byte[] buf = new byte[512];
  		int c;
  		while((c=is.read(buf))>0) {
  			os.write(buf, 0, c);
  		}
  		is.close();
  	}
  
  	/**
  	 * Redirects the reader to the writer, without closing them
  	 * @param is
  	 * @param os
  	 * @throws IOException
  	 */
  	public static void redirect(Reader is, Writer os) throws IOException {
  		char[] buf = new char[512];
  		int c;
  		while((c=is.read(buf))>0) {
  			os.write(buf, 0, c);
  		}
  		is.close();
  	}
  
  	public static void copy(File src, File dest) throws IOException {
  		InputStream is = null;
  		OutputStream os = null;
  		try {
  			is = new BufferedInputStream(new FileInputStream(src));
  			os = new BufferedOutputStream(new FileOutputStream(dest));
  			redirect(is, os);
  
  		} finally {
  			try {if(is!=null) is.close();}catch(Exception e){}
  			try {if(os!=null) os.close();}catch(Exception e){}
  		}
  	}
  
  	/**
  	 * Debug instruction to view the relevant stacktrace
  	 * @throws IOException
  	 */
  	public static void dumpStack() {
  		Exception e = new Exception("StackTrace");
  		Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
  		dejaVu.add(e);
  
  		synchronized (System.err) {
  			StackTraceElement[] trace = e.getStackTrace();
  			System.err.println("Exception "+e.getMessage());
  			for (StackTraceElement traceElement : trace) {
  				if(traceElement.getClassName().contains("com.actelion.research.util.IOUtils")) continue;
  				if(traceElement.getClassName().contains("com.actelion")) {
  					System.err.println("\tat " + traceElement);
  				}
  			}
  			if(e.getCause()!=null) {
  				System.err.println("Caused by "+e.getCause());
  				for (StackTraceElement traceElement : trace) {
  					if(traceElement.getClassName().contains("com.actelion.research.util.IOUtils.dumpStack")) continue;
  					if(traceElement.getClassName().contains("com.actelion")) {
  						System.err.println("\tat " + traceElement);
  					}
  				}
  			}
  		}
  	}
  }