view src/org/tmatesoft/hg/internal/FileUtils.java @ 678:8625cba0a5a8

Towards better blame of merge revisions: refactor merge handling strategy
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 19 Jul 2013 15:36:29 +0200
parents 12a4f60ea972
children 24f4efedc9d5
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.nio.channels.FileChannel;

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 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);
			}
		}
	}
}