view src/org/tmatesoft/hg/internal/FileUtils.java @ 713:661e77dc88ba tip

Mac support: respect Mac alternatives of command-line arguments for common unix tools
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Sun, 03 Aug 2014 18:09:00 +0200
parents 42b88709e41d
children
line wrap: on
line source
/*
 * Copyright (c) 2013 TMate Software Ltd
 *  
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * For information on how to redistribute this software under
 * the terms of a license other than GNU General Public License
 * contact TMate Software at support@hg4j.com
 */
package org.tmatesoft.hg.internal;

import static org.tmatesoft.hg.util.LogFacility.Severity.Debug;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.tmatesoft.hg.core.HgIOException;
import org.tmatesoft.hg.util.LogFacility;
import org.tmatesoft.hg.util.LogFacility.Severity;

/**
 * 
 * @author Artem Tikhomirov
 * @author TMate Software Ltd.
 */
public final class FileUtils {
	
	private final LogFacility log;
	private final Class<?> troublemaker;
	
	public static void copyFile(File from, File to) throws HgIOException {
		new FileUtils(new StreamLogFacility(Debug, true, System.err), FileUtils.class).copy(from, to);
	}

	public FileUtils(LogFacility logFacility, Object troubleSource) {
		log = logFacility;
		if (troubleSource == null) {
			troublemaker = null;
		} else {
			troublemaker = troubleSource instanceof Class ? (Class<?>) troubleSource : troubleSource.getClass();
		}
	}

	public void copy(File from, File to) throws HgIOException {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(from);
			fos = new FileOutputStream(to);
			FileChannel input = fis.getChannel();
			FileChannel output = fos.getChannel();
			long count = input.size();
			long pos = 0;
			int zeroCopied = 0; // flag to prevent hang-up
			do {
				long c = input.transferTo(pos, count, output);
				pos += c;
				count -= c;
				if (c == 0) {
					if (++zeroCopied == 3) {
						String m = String.format("Can't copy %s to %s, transferTo copies 0 bytes. Position: %d, bytes left:%d", from.getName(), to.getName(), pos, count);
						throw new IOException(m);
					}
				} else {
					// reset
					zeroCopied = 0;
				}
			} while (count > 0);
			fos.close();
			fos = null;
			fis.close();
			fis = null;
		} catch (IOException ex) {
			// not in finally because I don't want to loose exception from fos.close()
			closeQuietly(fis, from);
			closeQuietly(fos, to);
			String m = String.format("Failed to copy %s to %s", from.getName(), to.getName());
			throw new HgIOException(m, ex, from);
		}
		/* Copy of cpython's 00changelog.d, 20Mb+
		 * Linux&Windows: 300-400 ms,
		 * Windows uncached run: 1.6 seconds
		 */
	}
	
	public void write(InputStream is, File file) throws IOException {
		FileOutputStream fos = new FileOutputStream(file);
		int r;
		byte[] buf = new byte[8*1024];
		while ((r = is.read(buf)) != -1) {
			fos.write(buf, 0, r);
		}
		fos.flush();
		fos.close();
	}
	
	public void closeQuietly(Closeable stream) {
		closeQuietly(stream, null);
	}

	public void closeQuietly(Closeable stream, File f) {
		if (stream != null) {
			try {
				stream.close();
			} catch (IOException ex) {
				// ignore
				final String msg;
				if (f == null) {
					msg = "Exception while closing stream quietly";
				} else {
					msg = String.format("Failed to close %s", f);
				}
				log.dump(troublemaker == null ? getClass() : troublemaker, Severity.Warn, ex, msg);
			}
		}
	}

	// nothing special, just a single place with common prefix
	public static File createTempFile() throws IOException {
		return File.createTempFile("hg4j-", null);
	}

	public static void rmdir(File dest) throws IOException {
		if (!dest.isDirectory()) {
			return;
		}
		LinkedList<File> queue = new LinkedList<File>();
		queue.addAll(Arrays.asList(dest.listFiles()));
		while (!queue.isEmpty()) {
			File next = queue.removeFirst();
			if (next.isDirectory()) {
				List<File> files = Arrays.asList(next.listFiles());
				if (!files.isEmpty()) {
					queue.addAll(files);
					queue.add(next);
				}
				// fall through
			} 
			next.delete();
		}
		dest.delete();
	}

}