360 lines
13 KiB
Java
360 lines
13 KiB
Java
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You 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.frostwire.jlibtorrent;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import static org.junit.Assert.assertTrue;
|
|
|
|
public final class Utils {
|
|
|
|
/**
|
|
* The extension separator character.
|
|
*
|
|
* @since 1.4
|
|
*/
|
|
public static final char EXTENSION_SEPARATOR = '.';
|
|
/**
|
|
* The Unix separator character.
|
|
*/
|
|
private static final char UNIX_SEPARATOR = '/';
|
|
/**
|
|
* The Windows separator character.
|
|
*/
|
|
private static final char WINDOWS_SEPARATOR = '\\';
|
|
private static final int EOF = -1;
|
|
|
|
private Utils() {
|
|
}
|
|
|
|
/**
|
|
* Gets the name minus the path from a full filename.
|
|
* <p>
|
|
* This method will handle a file in either Unix or Windows format.
|
|
* The text after the last forward or backslash is returned.
|
|
* <pre>
|
|
* a/b/c.txt --> c.txt
|
|
* a.txt --> a.txt
|
|
* a/b/c --> c
|
|
* a/b/c/ --> ""
|
|
* </pre>
|
|
* <p>
|
|
* The output will be the same irrespective of the machine that the code is running on.
|
|
*
|
|
* @param filename the filename to query, null returns null
|
|
* @return the name of the file without the path, or an empty string if none exists
|
|
*/
|
|
public static String getName(String filename) {
|
|
if (filename == null) {
|
|
return null;
|
|
}
|
|
int index = indexOfLastSeparator(filename);
|
|
return filename.substring(index + 1);
|
|
}
|
|
|
|
/**
|
|
* Gets the base name, minus the full path and extension, from a full filename.
|
|
* <p>
|
|
* This method will handle a file in either Unix or Windows format.
|
|
* The text after the last forward or backslash and before the last dot is returned.
|
|
* <pre>
|
|
* a/b/c.txt --> c
|
|
* a.txt --> a
|
|
* a/b/c --> c
|
|
* a/b/c/ --> ""
|
|
* </pre>
|
|
* <p>
|
|
* The output will be the same irrespective of the machine that the code is running on.
|
|
*
|
|
* @param filename the filename to query, null returns null
|
|
* @return the name of the file without the path, or an empty string if none exists
|
|
*/
|
|
public static String getBaseName(String filename) {
|
|
return removeExtension(getName(filename));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Removes the extension from a filename.
|
|
* <p>
|
|
* This method returns the textual part of the filename before the last dot.
|
|
* There must be no directory separator after the dot.
|
|
* <pre>
|
|
* foo.txt --> foo
|
|
* a\b\c.jpg --> a\b\c
|
|
* a\b\c --> a\b\c
|
|
* a.b\c --> a.b\c
|
|
* </pre>
|
|
* <p>
|
|
* The output will be the same irrespective of the machine that the code is running on.
|
|
*
|
|
* @param filename the filename to query, null returns null
|
|
* @return the filename minus the extension
|
|
*/
|
|
public static String removeExtension(String filename) {
|
|
if (filename == null) {
|
|
return null;
|
|
}
|
|
int index = indexOfExtension(filename);
|
|
if (index == -1) {
|
|
return filename;
|
|
} else {
|
|
return filename.substring(0, index);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the last extension separator character, which is a dot.
|
|
* <p>
|
|
* This method also checks that there is no directory separator after the last dot.
|
|
* To do this it uses {@link #indexOfLastSeparator(String)} which will
|
|
* handle a file in either Unix or Windows format.
|
|
* <p>
|
|
* The output will be the same irrespective of the machine that the code is running on.
|
|
*
|
|
* @param filename the filename to find the last path separator in, null returns -1
|
|
* @return the index of the last separator character, or -1 if there
|
|
* is no such character
|
|
*/
|
|
public static int indexOfExtension(String filename) {
|
|
if (filename == null) {
|
|
return -1;
|
|
}
|
|
int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
|
|
int lastSeparator = indexOfLastSeparator(filename);
|
|
return lastSeparator > extensionPos ? -1 : extensionPos;
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the last directory separator character.
|
|
* <p>
|
|
* This method will handle a file in either Unix or Windows format.
|
|
* The position of the last forward or backslash is returned.
|
|
* <p>
|
|
* The output will be the same irrespective of the machine that the code is running on.
|
|
*
|
|
* @param filename the filename to find the last path separator in, null returns -1
|
|
* @return the index of the last separator character, or -1 if there
|
|
* is no such character
|
|
*/
|
|
public static int indexOfLastSeparator(String filename) {
|
|
if (filename == null) {
|
|
return -1;
|
|
}
|
|
int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
|
|
int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
|
|
return Math.max(lastUnixPos, lastWindowsPos);
|
|
}
|
|
|
|
/**
|
|
* Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
|
|
* Use this method instead of <code>toByteArray(InputStream)</code>
|
|
* when <code>InputStream</code> size is known
|
|
*
|
|
* @param input the <code>InputStream</code> to read from
|
|
* @param size the size of <code>InputStream</code>
|
|
* @return the requested byte array
|
|
* @throws java.io.IOException if an I/O error occurs or <code>InputStream</code> size differ from parameter size
|
|
* @throws IllegalArgumentException if size is less than zero
|
|
* @since 2.1
|
|
*/
|
|
public static byte[] toByteArray(InputStream input, int size) throws IOException {
|
|
|
|
if (size < 0) {
|
|
throw new IllegalArgumentException("Size must be equal or greater than zero: " + size);
|
|
}
|
|
|
|
if (size == 0) {
|
|
return new byte[0];
|
|
}
|
|
|
|
byte[] data = new byte[size];
|
|
int offset = 0;
|
|
int readed;
|
|
|
|
while (offset < size && (readed = input.read(data, offset, size - offset)) != EOF) {
|
|
offset += readed;
|
|
}
|
|
|
|
if (offset != size) {
|
|
throw new IOException("Unexpected readed size. current: " + offset + ", excepted: " + size);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Get contents of an <code>InputStream</code> as a <code>byte[]</code>.
|
|
* Use this method instead of <code>toByteArray(InputStream)</code>
|
|
* when <code>InputStream</code> size is known.
|
|
* <b>NOTE:</b> the method checks that the length can safely be cast to an int without truncation
|
|
* before using {@link Utils#toByteArray(java.io.InputStream, int)} to read into the byte array.
|
|
* (Arrays can have no more than Integer.MAX_VALUE entries anyway)
|
|
*
|
|
* @param input the <code>InputStream</code> to read from
|
|
* @param size the size of <code>InputStream</code>
|
|
* @return the requested byte array
|
|
* @throws IOException if an I/O error occurs or <code>InputStream</code> size differ from parameter size
|
|
* @throws IllegalArgumentException if size is less than zero or size is greater than Integer.MAX_VALUE
|
|
* @see Utils#toByteArray(java.io.InputStream, int)
|
|
* @since 2.1
|
|
*/
|
|
public static byte[] toByteArray(InputStream input, long size) throws IOException {
|
|
|
|
if (size > Integer.MAX_VALUE) {
|
|
throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + size);
|
|
}
|
|
|
|
return toByteArray(input, (int) size);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Opens a {@link java.io.FileInputStream} for the specified file, providing better
|
|
* error messages than simply calling <code>new FileInputStream(file)</code>.
|
|
* <p>
|
|
* At the end of the method either the stream will be successfully opened,
|
|
* or an exception will have been thrown.
|
|
* <p>
|
|
* An exception is thrown if the file does not exist.
|
|
* An exception is thrown if the file object exists but is a directory.
|
|
* An exception is thrown if the file exists but cannot be read.
|
|
*
|
|
* @param file the file to open for input, must not be {@code null}
|
|
* @return a new {@link java.io.FileInputStream} for the specified file
|
|
* @throws java.io.FileNotFoundException if the file does not exist
|
|
* @throws IOException if the file object is a directory
|
|
* @throws IOException if the file cannot be read
|
|
* @since 1.3
|
|
*/
|
|
public static FileInputStream openInputStream(File file) throws IOException {
|
|
if (file.exists()) {
|
|
if (file.isDirectory()) {
|
|
throw new IOException("File '" + file + "' exists but is a directory");
|
|
}
|
|
if (file.canRead() == false) {
|
|
throw new IOException("File '" + file + "' cannot be read");
|
|
}
|
|
} else {
|
|
throw new FileNotFoundException("File '" + file + "' does not exist");
|
|
}
|
|
return new FileInputStream(file);
|
|
}
|
|
|
|
/**
|
|
* Reads the contents of a file into a byte array.
|
|
* The file is always closed.
|
|
*
|
|
* @param file the file to read, must not be {@code null}
|
|
* @return the file contents, never {@code null}
|
|
* @throws IOException in case of an I/O error
|
|
* @since 1.1
|
|
*/
|
|
public static byte[] readFileToByteArray(File file) throws IOException {
|
|
return Files.bytes(file);
|
|
}
|
|
|
|
public static String toHex(byte[] bytes) {
|
|
return Hex.encode(bytes);
|
|
}
|
|
|
|
public static byte[] fromHex(String str) {
|
|
return Hex.decode(str);
|
|
}
|
|
|
|
public static FileOutputStream openOutputStream(File file, boolean append) throws IOException {
|
|
if (file.exists()) {
|
|
if (file.isDirectory()) {
|
|
throw new IOException("File '" + file + "' exists but is a directory");
|
|
}
|
|
if (file.canWrite() == false) {
|
|
throw new IOException("File '" + file + "' cannot be written to");
|
|
}
|
|
} else {
|
|
File parent = file.getParentFile();
|
|
if (parent != null) {
|
|
if (!parent.mkdirs() && !parent.isDirectory()) {
|
|
throw new IOException("Directory '" + parent + "' could not be created");
|
|
}
|
|
}
|
|
}
|
|
return new FileOutputStream(file, append);
|
|
}
|
|
|
|
public static void writeByteArrayToFile(File file, byte[] data, boolean append) throws IOException {
|
|
OutputStream out = null;
|
|
try {
|
|
out = openOutputStream(file, append);
|
|
out.write(data);
|
|
out.close(); // don't swallow close Exception if copy completes normally
|
|
} finally {
|
|
Files.closeQuietly(out);
|
|
}
|
|
}
|
|
|
|
public static byte[] resourceBytes(String path) throws IOException {
|
|
ClassLoader classLoader = Utils.class.getClassLoader();
|
|
InputStream input = classLoader.getResourceAsStream(path);
|
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
try {
|
|
int n = 0;
|
|
byte[] buffer = new byte[1024 * 4];
|
|
while (EOF != (n = input.read(buffer))) {
|
|
output.write(buffer, 0, n);
|
|
}
|
|
return output.toByteArray();
|
|
} finally {
|
|
Files.closeQuietly(input);
|
|
Files.closeQuietly(output);
|
|
}
|
|
}
|
|
|
|
public static File home(String path) {
|
|
return new File(System.getProperty("user.home"), path);
|
|
}
|
|
|
|
static void await(CountDownLatch s, String message, long timeout, TimeUnit unit) {
|
|
boolean r = false;
|
|
try {
|
|
r = s.await(timeout, unit);
|
|
} catch (InterruptedException e) {
|
|
// ignore
|
|
}
|
|
assertTrue(message, r);
|
|
}
|
|
|
|
static void awaitSeconds(CountDownLatch s, String message, long timeout) {
|
|
await(s, message, timeout, TimeUnit.SECONDS);
|
|
}
|
|
|
|
static void awaitMinutes(CountDownLatch s, String message, long timeout) {
|
|
await(s, message, timeout, TimeUnit.MINUTES);
|
|
}
|
|
}
|