/* * 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.*; public final class Utils { private 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; /** * 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); } /** * Unconditionally close a <code>Closeable</code>. * <p/> * Equivalent to {@link java.io.Closeable#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * <p/> * Example code: * <pre> * Closeable closeable = null; * try { * closeable = new FileReader("foo.txt"); * // process closeable * closeable.close(); * } catch (Exception e) { * // error handling * } finally { * IOUtils.closeQuietly(closeable); * } * </pre> * * @param closeable the object to close, may be null or already closed * @since 2.0 */ public static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException ioe) { // ignore } } /** * Unconditionally close an <code>InputStream</code>. * <p/> * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * <p/> * Example code: * <pre> * byte[] data = new byte[1024]; * InputStream in = null; * try { * in = new FileInputStream("foo.txt"); * in.read(data); * in.close(); //close errors are handled * } catch (Exception e) { * // error handling * } finally { * IOUtils.closeQuietly(in); * } * </pre> * * @param input the InputStream to close, may be null or already closed */ public static void closeQuietly(InputStream input) { closeQuietly((Closeable) input); } //----------------------------------------------------------------------- /** * 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 { InputStream in = null; try { in = openInputStream(file); return toByteArray(in, file.length()); } finally { closeQuietly(in); } } /** * Writes a byte array to a file creating the file if it does not exist. * <p> * NOTE: As from v1.3, the parent directories of the file will be created * if they do not exist. * * @param file the file to write to * @param data the content to write to the file * @throws IOException in case of an I/O error * @since 1.1 */ public static void writeByteArrayToFile(File file, byte[] data) throws IOException { writeByteArrayToFile(file, data, false); } /** * Writes a byte array to a file creating the file if it does not exist. * * @param file the file to write to * @param data the content to write to the file * @param append if {@code true}, then bytes will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @since IO 2.1 */ 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 { closeQuietly(out); } } //----------------------------------------------------------------------- /** * Opens a {@link FileOutputStream} for the specified file, checking and * creating the parent directory if it does not exist. * <p> * At the end of the method either the stream will be successfully opened, * or an exception will have been thrown. * <p> * The parent directory will be created if it does not exist. * The file will be created if it 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 written to. * An exception is thrown if the parent directory cannot be created. * * @param file the file to open for output, must not be {@code null} * @return a new {@link FileOutputStream} for the specified file * @throws IOException if the file object is a directory * @throws IOException if the file cannot be written to * @throws IOException if a parent directory needs creating but that fails * @since 1.3 */ public static FileOutputStream openOutputStream(File file) throws IOException { return openOutputStream(file, false); } /** * Opens a {@link FileOutputStream} for the specified file, checking and * creating the parent directory if it does not exist. * <p> * At the end of the method either the stream will be successfully opened, * or an exception will have been thrown. * <p> * The parent directory will be created if it does not exist. * The file will be created if it 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 written to. * An exception is thrown if the parent directory cannot be created. * * @param file the file to open for output, must not be {@code null} * @param append if {@code true}, then bytes will be added to the * end of the file rather than overwriting * @return a new {@link FileOutputStream} for the specified file * @throws IOException if the file object is a directory * @throws IOException if the file cannot be written to * @throws IOException if a parent directory needs creating but that fails * @since 2.1 */ 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); } }