All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mgmtp.perfload.perfalyzer.util.IoUtilities Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013-2015 mgm technology partners GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mgmtp.perfload.perfalyzer.util;

import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SystemUtils;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.get;
import static org.apache.commons.io.FileUtils.copyFile;
import static org.apache.commons.io.FilenameUtils.normalize;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.substringAfter;

/**
 * Utility class for IO operations.
 *
 * @author rnaegele, ctchinda
 */
public class IoUtilities {

	private static final int BUFFER_SIZE = 64 * 1024;

	private IoUtilities() {
	}

	/**
	 * Copies the content from one channel to another.
	 *
	 * @param srcChannel
	 * 		the source channel to copy from
	 * @param destChannel
	 * 		the destination channel to copy to
	 */
	public static void copy(final ReadableByteChannel srcChannel, final WritableByteChannel destChannel) throws IOException {
		final ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
		while (srcChannel.read(buffer) != -1) {
			// flip the buffer so it can be written to the destination channel
			buffer.flip();

			// write to the destination channel
			destChannel.write(buffer);

			// If partial transfer, shift remainder down so it does not get lost
			// If buffer is empty, this is the same as calling clear()
			buffer.compact();
		}

		// EOF will leave buffer in fill state
		buffer.flip();

		// make sure the buffer is fully drained
		while (buffer.hasRemaining()) {
			destChannel.write(buffer);
		}
	}

	/**
	 * Merges a list of files into a single one by appending the contents of each file. If
	 * {@code headerLines} is greater than zero, the header from the first file is written to the
	 * destination file. The same number of lines is skipped in all other file, i. e. all files are
	 * expected to have the same header.
	 *
	 * @param sourceFiles
	 * 		the list of source files
	 * @param destFile
	 * 		the destination file
	 * @param headerLines
	 * 		the number of header lines
	 * @param charset
	 * 		the character set to use
	 */
	public static void merge(final File sourceDir, final List sourceFiles, final File destFile,
			final int headerLines, final Charset charset) throws IOException {
		Files.createParentDirs(destFile);

		// simply copy the first file
		copyFile(new File(sourceDir, get(sourceFiles, 0).getFile().getPath()), destFile);

		if (sourceFiles.size() > 1) {
			// append all other files skipping headers
			try (Writer w = Files.newWriter(destFile, charset)) {
				for (PerfAlyzerFile paf : sourceFiles.subList(1, sourceFiles.size())) {
					try (BufferedReader br = Files.newReader(new File(sourceDir, paf.getFile().getPath()), charset)) {

						// skip headers
						for (int i = 0; i < headerLines; ++i) {
							br.readLine();
						}

						// copy the rest
						CharStreams.copy(br, w);
					}
				}
			}
		}
	}

	/**
	 * Unzips a zip file.
	 *
	 * @param zip
	 * 		the zip file
	 * @param destDir
	 * 		the destination directory (will be created if non-existent)
	 */
	public static void unzip(final ZipFile zip, final File destDir) throws IOException {
		if (!destDir.exists()) {
			destDir.mkdir();
		}

		for (Enumeration zipEntryEnum = zip.entries(); zipEntryEnum.hasMoreElements(); ) {
			ZipEntry zipEntry = zipEntryEnum.nextElement();
			extractEntry(zip, zipEntry, destDir);
		}
	}

	private static void extractEntry(final ZipFile zf, final ZipEntry entry, final File destDir) throws IOException {
		File file = new File(destDir, entry.getName());

		if (entry.isDirectory()) {
			file.mkdirs();
		} else {
			new File(file.getParent()).mkdirs();

			try (InputStream is = zf.getInputStream(entry); FileOutputStream os = new FileOutputStream(file)) {
				copy(Channels.newChannel(is), os.getChannel());
			}
			// preserve modification time; must be set after the stream is closed
			file.setLastModified(entry.getTime());
		}
	}

	public static void writeToChannel(final WritableByteChannel destChannel, final ByteBuffer buffer) {
		try {
			// write to the destination channel
			destChannel.write(buffer);

			// If partial transfer, shift remainder down so it does not get lost
			// If buffer is empty, this is the same as calling clear()
			buffer.compact();

			// EOF will leave buffer in fill state
			buffer.flip();

			// make sure the buffer is fully drained
			while (buffer.hasRemaining()) {
				destChannel.write(buffer);
			}
		} catch (IOException ex) {
			throw new UncheckedIOException(ex);
		}
	}

	public static void writeLineToChannel(final WritableByteChannel destChannel, final String line, final Charset charset) {
		try {
			String tmpLine = line.endsWith(SystemUtils.LINE_SEPARATOR) ? line : line + SystemUtils.LINE_SEPARATOR;
			CharBuffer buffer = CharBuffer.wrap(tmpLine);
			CharsetEncoder encoder = charset.newEncoder();
			ByteBuffer bb = encoder.encode(buffer);
			writeToChannel(destChannel, bb);
		} catch (IOException ex) {
			throw new UncheckedIOException(ex);
		}
	}

	/**
	 * Reads the last line of the specified file.
	 *
	 * @param file
	 * 		the file
	 * @param charset
	 * 		the charset
	 */
	public static String readLastLine(final File file, final Charset charset) {
		try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
			long length = raf.length() - 1;

			ByteArrayOutputStream baos = new ByteArrayOutputStream(80);

			for (long pos = length; pos != -1; --pos) { // we start from the end of the file
				raf.seek(pos);

				int readByte = raf.readByte();

				if (readByte == 10) { // this is the case if the file ends with a line-break
					if (pos == length) {
						continue;
					}
					break;
				} else if (readByte == 13) { // this is the case if the file ends with a line-break (Windows only)
					if (pos == length - 1) {
						continue;
					}
					break;
				}

				baos.write(readByte);
			}

			byte[] bytes = baos.toByteArray();

			// reverse array because it was filled backwards
			ArrayUtils.reverse(bytes);

			// turn into string respecting the charset
			return new String(bytes, charset);
		} catch (IOException ex) {
			throw new UncheckedIOException(ex);
		}
	}

	/**
	 * Normalizes the specified file using {@link FilenameUtils#normalize(String)}. If {@code file}
	 * is a directory, the normalized result always ends with the file separator.
	 *
	 * @param file
	 * 		the file to normalize
	 * @return the normalized file path
	 */
	public static String computeNormalizedPath(final File file) throws IOException {
		String normalizedPath = normalize(file.getCanonicalPath());
		if (file.isDirectory()) {
			normalizedPath = ensureEndsWithFileSeparator(normalizedPath);
		}
		return normalizedPath;
	}

	/**
	 * Returns the specified path appending a file separator is necessary.
	 *
	 * @param path
	 * 		the path
	 * @return the path ending with a file separator
	 */
	public static String ensureEndsWithFileSeparator(final String path) {
		if (!path.endsWith(SystemUtils.FILE_SEPARATOR)) {
			return path + SystemUtils.FILE_SEPARATOR;
		}
		return path;
	}

	/**
	 * Turns the specified file into one relative to the specified parent directory.
	 *
	 * @param parentDir
	 * 		the parent directory
	 * @param file
	 * 		the file to make relative
	 * @return the file relative to {@code parentDir}
	 * @throws IllegalStateException
	 * 		if {@code parentDir} is neither a directory nor a parent directory of
	 * 		{@code file}
	 */
	public static File makeRelative(final File parentDir, final File file) {
		try {
			checkState(parentDir.isDirectory(), "'%s' is not a directory", parentDir);

			String normalizedBaseDirPath = computeNormalizedPath(parentDir);
			String normalizedFilePath = computeNormalizedPath(file);
			String relativeFilePath = substringAfter(normalizedFilePath, normalizedBaseDirPath);
			checkState(isNotEmpty(relativeFilePath), "'%s' is not the parent directory of '%s'", parentDir, file);

			return new File(relativeFilePath);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Creates a temporary directory named with a random UUID in the directory specified by the
	 * system property {@coe java.io.tmpdir}.
	 *
	 * @return the directory
	 */
	public static File createTempDir() {
		File file = new File(SystemUtils.JAVA_IO_TMPDIR, UUID.randomUUID().toString());
		checkState(file.mkdir(), "Could not create temporary directory %s", file);
		return file;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy