content: /*
   * 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 org.libtorrent4j;
  
  import java.io.*;
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.TimeUnit;
  
  import static org.junit.Assert.assertTrue;
  
  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);
      }
  
      //-----------------------------------------------------------------------
  
      /**
       * 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);
      }
  }
  