Package net.pms.util

Source Code of net.pms.util.FileUtil$FileLocation

package net.pms.util;

import net.pms.PMS;
import net.pms.dlna.DLNAMediaInfo;
import net.pms.dlna.DLNAMediaSubtitle;
import net.pms.formats.v2.SubtitleType;
import org.apache.commons.io.FilenameUtils;
import org.mozilla.universalchardet.UniversalDetector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import static org.apache.commons.lang3.StringUtils.endsWithIgnoreCase;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.mozilla.universalchardet.Constants.*;

public class FileUtil {
  private static final Logger logger = LoggerFactory.getLogger(FileUtil.class);
  private static Map<File, File[]> cache;
  // signal an invalid parameter in getFileLocation() without raising an exception or returning null
  private static final String DEFAULT_BASENAME = "NO_DEFAULT_BASENAME_SUPPLIED.conf";

  // this class is not instantiable
  private FileUtil() { }

  /**
   * A helper class used by {@link #getFileLocation(String, String, String)}
   * which provides access to a file's absolute path and that of its directory.
   *
   * @since 1.90.0
   */
  public static final class FileLocation {
    private String directoryPath;
    private String filePath;

    FileLocation(File directory, File file) {
      this.directoryPath = FilenameUtils.normalize(directory.getAbsolutePath());
      this.filePath = FilenameUtils.normalize(file.getAbsolutePath());
    }

    public String getDirectoryPath() {
      return directoryPath;
    }

    public String getFilePath() {
      return filePath;
    }
  }

  /**
   * Returns a {@link FileLocation} object which provides access to the directory
   * and file paths of the specified file as normalised, absolute paths.
   *
   * This determines the directory and file path of a file according to the rules
   * outlined here: http://www.ps3mediaserver.org/forum/viewtopic.php?f=6&amp;t=3507&amp;p=49895#pms-faq-custom-profiles-environment-variable
   *
   * @since 1.90.0
   * @param customPath an optional user-defined path for the resource
   * @param defaultDirectory a default directory path used if no custom path is provided
   * @param defaultBasename a default filename used if a) no custom path is provided
   * or b) the custom path is a directory
   * @return a {@link FileLocation} object providing access to the file's directory and file paths
   */
  // this is called from a static initialiser, where errors aren't clearly reported,
  // so do everything possible to return a valid reponse, even if the parameters
  // aren't sane
  static public FileLocation getFileLocation(
    String customPath,
    String defaultDirectory,
    String defaultBasename
  ) {
    File customFile = null;
    File directory = null;
    File file = null;

    if (isBlank(defaultBasename)) {
      // shouldn't get here
      defaultBasename = DEFAULT_BASENAME;
    }

    if (defaultDirectory == null) {
      defaultDirectory = ""; // current directory
    }

    if (customPath != null) {
      customFile = new File(customPath).getAbsoluteFile();
    }

    if (customFile != null) {
      if (customFile.exists()) {
        if (customFile.isDirectory()) {
          directory = customFile;
          file = new File(customFile, defaultBasename).getAbsoluteFile();
        } else {
          directory = customFile.getParentFile();
          file = customFile;
        }
      } else {
        File parentDirectoryFile = customFile.getParentFile();
        if (parentDirectoryFile != null && parentDirectoryFile.exists()) {
          // parent directory exists: the file can be created
          directory = parentDirectoryFile;
          file = customFile;
        }
      }
    }

    if (directory == null || file == null) {
      directory = new File(defaultDirectory).getAbsoluteFile();
      file = new File(directory, defaultBasename).getAbsoluteFile();
    }

    return new FileLocation(directory, file);
  }

  public static File isFileExists(String f, String ext) {
    return isFileExists(new File(f), ext);
  }

  /**
   * Returns the protocol of the supplied filename if it's a URI,
   * or <code>null</code> if it's not.
   *
   * @param filename the filename whose protocol is to be determined
   * @return the filename's protocol if it's a URI, or <code>null</code>
   * if it's not.
   */
  public static String getProtocol(String filename) {
    String protocol = null;

    if (filename != null) {
      try {
        URI uri = new URI(filename.toLowerCase());
        protocol = uri.getScheme();
      } catch (URISyntaxException use) { }
    }

    return protocol;
  }

  /**
   * @deprecated use {@link FilenameUtils#getExtension(String filePath)} instead.
   */
  @Deprecated
  public static String getExtension(final String filePath) {
    return FilenameUtils.getExtension(filePath);
  }

  /**
   * @deprecated use {@link FilenameUtils#getBaseName(String filePath)} instead.
   */
  @Deprecated
  public static String getFileNameWithoutExtension(final String filePath) {
    return FilenameUtils.getBaseName(filePath);
  }

  public static String getFileNameWithRewriting(String f) {
    String formattedName;
    int point = f.lastIndexOf(".");

    if (point == -1) {
      point = f.length();
    }

    // Remove file extension
    formattedName = f.substring(0, point);

    String commonFileEnds = "[\\s\\.]AC3.*|[\\s\\.]REPACK.*|[\\s\\.]480p.*|[\\s\\.]720p.*|[\\s\\.]m-720p.*|[\\s\\.]900p.*|[\\s\\.]1080p.*|[\\s\\.]HDTV.*|[\\s\\.]DSR.*|[\\s\\.]PDTV.*|[\\s\\.]WS.*|[\\s\\.]HQ.*|[\\s\\.]DVDRip.*|[\\s\\.]TVRiP.*|[\\s\\.]BDRip.*|[\\s\\.]BluRay.*|[\\s\\.]SUBBED.*|[\\s\\.]x264.*|[\\s\\.]Dual[\\s\\.]Audio.*|[\\s\\.]HSBS.*|[\\s\\.]H-SBS.*";
    String commonFileEndsMatch = ".*[\\s\\.]AC3.*|.*[\\s\\.]REPACK.*|.*[\\s\\.]480p.*|.*[\\s\\.]720p.*|.*[\\s\\.]m-720p.*|.*[\\s\\.]900p.*|.*[\\s\\.]1080p.*|.*[\\s\\.]HDTV.*|.*[\\s\\.]DSR.*|.*[\\s\\.]PDTV.*|.*[\\s\\.]WS.*|.*[\\s\\.]HQ.*|.*[\\s\\.]DVDRip.*|.*[\\s\\.]TVRiP.*|.*[\\s\\.]BDRip.*|.*[\\s\\.]BluRay.*|.*[\\s\\.]SUBBED.*|.*[\\s\\.]x264.*|.*[\\s\\.]Dual[\\s\\.]Audio.*|.*[\\s\\.]HSBS.*|.*[\\s\\.]H-SBS.*";
    String commonFileEndsCaseSensitive = "[\\s\\.]PROPER.*|[\\s\\.]iNTERNAL.*|[\\s\\.]LIMITED.*|[\\s\\.]FESTiVAL.*|[\\s\\.]NORDIC.*";

    if (formattedName.matches(".*[sS]0\\d[eE]\\d\\d.*")) {
      // This matches scene and most p2p TV episodes within the first 9 seasons

      // Rename the season/episode numbers. For example, "S01E01" changes to " - 101"
      // Then strip the end of the episode if it does not have the episode name in the title
      formattedName = formattedName.replaceAll("(?i)[\\s\\.]S0(\\d)E(\\d)(\\d)(" + commonFileEnds + ")", " - $1$2$3");

      // If it matches this then it didn't match the previous one, which means there is probably an episode title in the filename
      formattedName = formattedName.replaceAll("(?i)[\\s\\.]S0(\\d)E(\\d)(\\d)[\\s\\.]", " - $1$2$3 - ");

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");

      // Replace periods with spaces
      formattedName = formattedName.replaceAll("\\.", " ");
    } else if (formattedName.matches(".*[sS][1-9]\\d[eE]\\d\\d.*")) {
      // This matches scene and most p2p TV episodes after their first 9 seasons

      // Rename the season/episode numbers. For example, "S11E01" changes to " - 1101"
      formattedName = formattedName.replaceAll("(?i)[\\s\\.]S([1-9]\\d)E(\\d)(\\d)(" + commonFileEnds + ")", " - $1$2$3");

      // If it matches this then it didn't match the previous one, which means there is probably an episode title in the filename
      formattedName = formattedName.replaceAll("(?i)[\\s\\.]S([1-9]\\d)E(\\d)(\\d)[\\s\\.]", " - $1$2$3 - ");

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");

      // Replace periods with spaces
      formattedName = formattedName.replaceAll("\\.", " ");
    } else if (formattedName.matches(".*\\.(19|20)\\d\\d\\.[0-1]\\d\\.[0-3]\\d\\..*")) {
      // This matches scene and most p2p TV episodes that release several times per week

      // Rename the date. For example, "2013.03.18" changes to " - 2013/03/18"
      formattedName = formattedName.replaceAll("(?i)\\.(19|20)(\\d\\d)\\.([0-1]\\d)\\.([0-3]\\d)(" + commonFileEnds + ")", " - $1$2/$3/$4");

      // If it matches this then it didn't match the previous one, which means there is probably an episode title in the filename
      formattedName = formattedName.replaceAll("(?i)\\.(19|20)(\\d\\d)\\.([0-1]\\d)\\.([0-3]\\d)\\.", " - $1$2/$3/$4 - ");

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");

      // Replace periods with spaces
      formattedName = formattedName.replaceAll("\\.", " ");
    } else if (formattedName.matches(".*\\.(19|20)\\d\\d\\..*")) {
      // This matches scene and most p2p movies

      // Rename the year. For example, "2013" changes to " (2013)"
      formattedName = formattedName.replaceAll("\\.(19|20)(\\d\\d)", " ($1$2)");

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");

      // Change "3D" to " (3D)", "CD1" to "(1)", etc.
      formattedName = formattedName.replaceAll("(?i)\\.(CD[1-3])\\.|\\.(3D)\\.|\\.(Special.Edition)\\.|\\.(Unrated)\\.|\\.(Final.Cut)\\.|\\.(Remastered)\\.|\\.(Extended.Cut)\\.", " ($1)");

      // Replace periods with spaces
      formattedName = formattedName.replaceAll("\\.", " ");
    } else if (formattedName.matches(commonFileEndsMatch)) {
      // This matches files that partially follow the scene format

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");

      // Change "3D" to " (3D)", "CD1" to "(1)", etc.
      formattedName = formattedName.replaceAll("(?i)\\.(CD[1-3])\\.|\\.(3D)\\.|\\.(Special.Edition)\\.|\\.(Unrated)\\.|\\.(Final.Cut)\\.|\\.(Remastered)\\.|\\.(Extended.Cut)\\.", " ($1)");

      // Replace periods with spaces
      formattedName = formattedName.replaceAll("\\.", " ");
    } else if (formattedName.matches(".*\\[(19|20)\\d\\d\\].*")) {
      // This matches rarer types of movies

      // Rename the year. For example, "2013" changes to " (2013)"
      formattedName = formattedName.replaceAll("(?i)\\[(19|20)(\\d\\d)\\].*", " ($1$2)");

      // Replace periods with spaces
      formattedName = formattedName.replaceAll("\\.", " ");
    } else if (formattedName.matches(".*\\((19|20)\\d\\d\\).*")) {
      // This matches rarer types of movies

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");
    } else if (formattedName.matches(".*\\((19|20)\\d\\d\\).*")) {
      // This matches rarer types of movies

      // Remove stuff at the end of the filename like release group, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)" + commonFileEnds, "");
      formattedName = formattedName.replaceAll(commonFileEndsCaseSensitive, "");
    } else if (formattedName.matches(".*\\[[0-9a-zA-Z]{8}\\]$")) {
      // This matches anime with a hash at the end of the name

      // Remove underscores
      formattedName = formattedName.replaceAll("_", " ");

      // Remove stuff at the end of the filename like hash, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)\\s\\(1280x720.*|\\s\\(1920x1080.*|\\s\\(720x400.*|\\[720p.*|\\[1080p.*|\\[480p.*|\\s\\(BD.*|\\s\\[Blu-Ray.*|\\s\\[DVD.*|\\.DVD.*|\\[[0-9a-zA-Z]{8}\\]$|\\[h264.*|R1DVD.*|\\[BD.*", "");

      // Remove group name from the beginning of the filename
      if (formattedName.substring(0, 1).matches("\\[")) {
        int closingBracketIndex = formattedName.indexOf("]");
        if (closingBracketIndex != -1) {
          formattedName = formattedName.substring(closingBracketIndex + 1);
        }

        if (formattedName.substring(0, 1).matches("\\s")) {
          formattedName = formattedName.substring(1);
        }
      }
    } else if (formattedName.matches(".*\\[BD\\].*|.*\\[720p\\].*|.*\\[1080p\\].*|.*\\[480p\\].*|.*\\[Blu-Ray.*|.*\\[h264.*")) {
      // This matches anime without a hash in the name

      // Remove underscores
      formattedName = formattedName.replaceAll("_", " ");

      // Remove stuff at the end of the filename like hash, quality, source, etc.
      formattedName = formattedName.replaceAll("(?i)\\[BD\\].*|\\[720p.*|\\[1080p.*|\\[480p.*|\\[Blu-Ray.*\\[h264.*", "");

      // Remove group name from the beginning of the filename
      if (formattedName.substring(0, 1).matches("\\[")) {
        int closingBracketIndex = formattedName.indexOf("]");
        if (closingBracketIndex != -1) {
          formattedName = formattedName.substring(closingBracketIndex + 1);
        }

        if (formattedName.substring(0, 1).matches("\\s")) {
          formattedName = formattedName.substring(1);
        }
      }
    }

    return formattedName;
  }

  public static File getFileNameWithNewExtension(File parent, File file, String ext) {
    return isFileExists(new File(parent, file.getName()), ext);
  }

  /**
   * @deprecated Use {@link #getFileNameWithNewExtension(File, File, String)}.
   */
  @Deprecated
  public static File getFileNameWitNewExtension(File parent, File f, String ext) {
    return getFileNameWithNewExtension(parent, f, ext);
  }

  public static File getFileNameWithAddedExtension(File parent, File f, String ext) {
    File ff = new File(parent, f.getName() + ext);

    if (ff.exists()) {
      return ff;
    }

    return null;
  }

  /**
   * @deprecated Use {@link #getFileNameWithAddedExtension(File, File, String)}.
   */
  @Deprecated
  public static File getFileNameWitAddedExtension(File parent, File file, String ext) {
    return getFileNameWithAddedExtension(parent, file, ext);
  }

  public static File isFileExists(File f, String ext) {
    int point = f.getName().lastIndexOf(".");

    if (point == -1) {
      point = f.getName().length();
    }

    File lowerCasedFile = new File(f.getParentFile(), f.getName().substring(0, point) + "." + ext.toLowerCase());
    if (lowerCasedFile.exists()) {
      return lowerCasedFile;
    }

    File upperCasedFile = new File(f.getParentFile(), f.getName().substring(0, point) + "." + ext.toUpperCase());
    if (upperCasedFile.exists()) {
      return upperCasedFile;
    }

    return null;
  }

  /**
   * @deprecated Use {@link #isSubtitlesExists(File file, DLNAMediaInfo media)} instead.
   */
  @Deprecated
  public static boolean doesSubtitlesExists(File file, DLNAMediaInfo media) {
    return isSubtitlesExists(file, media);
  }

  public static boolean isSubtitlesExists(File file, DLNAMediaInfo media) {
    return isSubtitlesExists(file, media, true);
  }

  /**
   * @deprecated Use {@link #isSubtitlesExists(File file, DLNAMediaInfo media, boolean usecache)} instead.
   */
  @Deprecated
  public static boolean doesSubtitlesExists(File file, DLNAMediaInfo media, boolean usecache) {
    return isSubtitlesExists(file, media, usecache);
  }

  public static boolean isSubtitlesExists(File file, DLNAMediaInfo media, boolean usecache) {
    boolean found = browseFolderForSubtitles(file.getParentFile(), file, media, usecache);
    String alternate = PMS.getConfiguration().getAlternateSubtitlesFolder();

    if (isNotBlank(alternate)) { // https://code.google.com/p/ps3mediaserver/issues/detail?id=737#c5
      File subFolder = new File(alternate);

      if (!subFolder.isAbsolute()) {
        subFolder = new File(file.getParent() + "/" + alternate);
        try {
          subFolder = subFolder.getCanonicalFile();
        } catch (IOException e) {
          logger.debug("Caught exception", e);
        }
      }

      if (subFolder.exists()) {
        found = found || browseFolderForSubtitles(subFolder, file, media, usecache);
      }
    }

    return found;
  }

  private synchronized static boolean browseFolderForSubtitles(File subFolder, File file, DLNAMediaInfo media, boolean usecache) {
    boolean found = false;

    if (!usecache) {
      cache = null;
    }

    if (cache == null) {
      cache = new HashMap<File, File[]>();
    }

    File[] allSubs = cache.get(subFolder);
    if (allSubs == null) {
      allSubs = subFolder.listFiles();

      if (allSubs != null) {
        cache.put(subFolder, allSubs);
      }
    }

    String fileName = FilenameUtils.getBaseName(file.getName()).toLowerCase();
    if (allSubs != null) {
      for (File f : allSubs) {
        if (f.isFile() && !f.isHidden()) {
          String fName = f.getName().toLowerCase();
          for (String ext : SubtitleType.getSupportedFileExtensions()) {
            if (fName.length() > ext.length() && fName.startsWith(fileName) && endsWithIgnoreCase(fName, "." + ext)) {
              int a = fileName.length();
              int b = fName.length() - ext.length() - 1;
              String code = "";

              if (a <= b) { // handling case with several dots: <video>..<extension>
                code = fName.substring(a, b);
              }

              if (code.startsWith(".")) {
                code = code.substring(1);
              }

              boolean exists = false;
              if (media != null) {
                for (DLNAMediaSubtitle sub : media.getSubtitleTracksList()) {
                  if (f.equals(sub.getExternalFile())) {
                    exists = true;
                  } else if (equalsIgnoreCase(ext, "idx") && sub.getType() == SubtitleType.MICRODVD) { // sub+idx => VOBSUB
                    sub.setType(SubtitleType.VOBSUB);
                    exists = true;
                  } else if (equalsIgnoreCase(ext, "sub") && sub.getType() == SubtitleType.VOBSUB) { // VOBSUB
                    try {
                      sub.setExternalFile(f);
                    } catch (FileNotFoundException ex) {
                      logger.warn("Exception during external subtitles scan.", ex);
                    }

                    exists = true;
                  }
                }
              }

              if (!exists) {
                DLNAMediaSubtitle sub = new DLNAMediaSubtitle();
                sub.setId(100 + (media == null ? 0 : media.getSubtitleTracksList().size())); // fake id, not used
                if (code.length() == 0 || !Iso639.getCodeList().contains(code)) {
                  sub.setLang(DLNAMediaSubtitle.UND);
                  sub.setType(SubtitleType.valueOfFileExtension(ext));
                  if (code.length() > 0) {
                    sub.setFlavor(code);
                    if (sub.getFlavor().contains("-")) {
                      String flavorLang = sub.getFlavor().substring(0, sub.getFlavor().indexOf("-"));
                      String flavorTitle = sub.getFlavor().substring(sub.getFlavor().indexOf("-") + 1);
                      if (Iso639.getCodeList().contains(flavorLang)) {
                        sub.setLang(flavorLang);
                        sub.setFlavor(flavorTitle);
                      }
                    }
                  }
                } else {
                  sub.setLang(code);
                  sub.setType(SubtitleType.valueOfFileExtension(ext));
                }

                try {
                  sub.setExternalFile(f);
                } catch (FileNotFoundException ex) {
                  logger.warn("Exception during external subtitles scan.", ex);
                }

                found = true;
                if (media != null) {
                  media.getSubtitleTracksList().add(sub);
                }
              }
            }
          }
        }
      }
    }

    return found;
  }

  /**
   * Detects charset/encoding for given file. Not 100% accurate for
   * non-Unicode files.
   *
   * @param file File to detect charset/encoding
   * @return file's charset {@link org.mozilla.universalchardet.Constants}
   *         or null if not detected
   * @throws IOException
   */
  public static String getFileCharset(File file) throws IOException {
    byte[] buf = new byte[4096];
    BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
    final UniversalDetector universalDetector = new UniversalDetector(null);

    int numberOfBytesRead;
    while ((numberOfBytesRead = bufferedInputStream.read(buf)) > 0 && !universalDetector.isDone()) {
      universalDetector.handleData(buf, 0, numberOfBytesRead);
    }

    universalDetector.dataEnd();
    bufferedInputStream.close();
    String encoding = universalDetector.getDetectedCharset();

    if (encoding != null) {
      logger.debug("Detected encoding for {} is {}.", file.getAbsolutePath(), encoding);
    } else {
      logger.debug("No encoding detected for {}.", file.getAbsolutePath());
    }

    universalDetector.reset();

    return encoding;
  }

  /**
   * Tests if file is UTF-8 encoded with or without BOM.
   *
   * @param file File to test
   * @return true if file is UTF-8 encoded with or without BOM, false otherwise.
   * @throws IOException
   */
  public static boolean isFileUTF8(File file) throws IOException {
    return isCharsetUTF8(getFileCharset(file));
  }

  /**
   * Tests if charset is UTF-8 encoded with or without BOM.
   *
   * @param charset Charset to test
   * @return true if charset is UTF-8 encoded with or without BOM, false otherwise.
   */
  public static boolean isCharsetUTF8(String charset) {
    return equalsIgnoreCase(charset, CHARSET_UTF_8);
  }

  /**
   * Tests if file is UTF-16 encoded LE or BE.
   * @param file File to test
   * @return true if file is UTF-16 encoded LE or BE, false otherwise.
   * @throws IOException
   */
  public static boolean isFileUTF16(File file) throws IOException {
    return isCharsetUTF16(getFileCharset(file));
  }

  /**
   * Tests if charset is UTF-16 encoded LE or BE.
   * @param charset Charset to test
   * @return true if charset is UTF-16 encoded LE or BE, false otherwise.
   */
  public static boolean isCharsetUTF16(String charset) {
    return (equalsIgnoreCase(charset, CHARSET_UTF_16LE) || equalsIgnoreCase(charset, CHARSET_UTF_16BE));
  }

  /**
   * Tests if charset is UTF-32 encoded LE or BE.
   * @param charset Charset to test
   * @return true if charset is UTF-32 encoded LE or BE, false otherwise.
   */
  public static boolean isCharsetUTF32(String charset) {
    return (equalsIgnoreCase(charset, CHARSET_UTF_32LE) || equalsIgnoreCase(charset, CHARSET_UTF_32BE));
  }

  /**
   * Converts UTF-16 inputFile to UTF-8 outputFile. Does not overwrite existing outputFile file.
   * @param inputFile UTF-16 file
   * @param outputFile UTF-8 file after conversion
   * @throws IOException
   */
  public static void convertFileFromUtf16ToUtf8(File inputFile, File outputFile) throws IOException {
    String charset;
    if (inputFile == null || !inputFile.canRead()) {
      throw new FileNotFoundException("Can't read inputFile.");
    }

    try {
      charset = getFileCharset(inputFile);
    } catch (IOException ex) {
      logger.debug("Exception during charset detection.", ex);
      throw new IllegalArgumentException("Can't confirm inputFile is UTF-16.");
    }

    if (isCharsetUTF16(charset)) {
      if (!outputFile.exists()) {
        BufferedReader reader = null;

        try {
          if (equalsIgnoreCase(charset, CHARSET_UTF_16LE)) {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-16"));
          } else {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-16BE"));
          }
        } catch (UnsupportedEncodingException ex) {
          logger.warn("Unsupported exception.", ex);
          throw ex;
        }

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8"));
        int c;

        while ((c = reader.read()) != -1) {
          writer.write(c);
        }

        writer.close();
        reader.close();
      }
    } else {
      throw new IllegalArgumentException("File is not UTF-16");
    }
  }

  /**
   * Determine whether a file is readable by trying to read it. This works around JDK bugs which
   * return the wrong results for {@link java.io.File#canRead()} on Windows and, in some cases, on Unix.
   * <p>
   * Note: since this method accesses the filesystem, it should not be used in contexts in which performance is critical.
   * Note: this method changes the file access time.
   *
   * @since 1.71.0
   * @param file the File whose permissions are to be determined
   * @return <code>true</code> if the file is not null, exists, is a file and can be read, <code>false</code> otherwise
   */
  // based on the workaround posted here:
  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4993360
  // XXX why isn't this in Apache Commons?
  public static boolean isFileReadable(File file) {
    boolean isReadable = false;

    if ((file != null) && file.isFile()) {
      try {
        new FileInputStream(file).close();
        isReadable = true;
      } catch (IOException ioe) { }
    }

    return isReadable;
  }

  /**
   * Determine whether a file is writable by trying to write it. This works around JDK bugs which
   * return the wrong results for {@link java.io.File#canWrite()} on Windows and, in some cases, on Unix.
   * <p>
   * Note: since this method accesses the filesystem, it should not be used in contexts in which performance is critical.
   * Note: this method changes the file access time and may change the file modification time.
   *
   * @since 1.71.0
   * @param file the File whose permissions are to be determined
   * @return <code>true</code> if the file is not null and either a) exists, is a file and can be written to or b) doesn't
   * exist and can be created; otherwise returns <code>false</code>
   */
  // Loosely based on the workaround posted here:
  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4993360
  // XXX why isn't this in Apache Commons?
  public static boolean isFileWritable(File file) {
    boolean isWritable = false;

    if (file != null) {
      boolean fileAlreadyExists = file.isFile(); // i.e. exists and is a File

      if (fileAlreadyExists || !file.exists()) {
        try {
          // true: open for append: make sure the open
          // doesn't clobber the file
          new FileOutputStream(file, true).close();
          isWritable = true;

          if (!fileAlreadyExists) { // a new file has been "touch"ed; try to remove it
            try {
              if (!file.delete()) {
                logger.warn("Can't delete temporary test file: {}", file.getAbsolutePath());
              }
            } catch (SecurityException se) {
              logger.error("Error deleting temporary test file: " + file.getAbsolutePath(), se);
            }
          }
        } catch (IOException ioe) {
        } catch (SecurityException se) { }
      }
    }

    return isWritable;
  }

  /**
   * Determines whether the supplied directory is readable by trying to
   * read its contents.
   * This works around JDK bugs which return the wrong results for
   * {@link java.io.File#canRead()} on Windows and possibly on Unix.
   *
   * Note: since this method accesses the filesystem, it should not be
   * used in contexts in which performance is critical.
   * Note: this method changes the file access time.
   *
   * @since 1.71.0
   * @param dir the File whose permissions are to be determined
   * @return <code>true</code> if the File is not null, exists, is a
   *         directory and can be read, <code>false</code> otherwise
   */
  // XXX dir.canRead() has issues on Windows, so verify it directly:
  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6203387
  public static boolean isDirectoryReadable(File dir) {
    boolean isReadable = false;

    if (dir != null) {
      // new File("").isDirectory() is false, even though getAbsolutePath() returns the right path.
      // this resolves it
      dir = dir.getAbsoluteFile();

      if (dir.isDirectory()) {
        try {
          File[] files = dir.listFiles(); // null if an I/O error occurs
          isReadable = files != null;
        } catch (SecurityException se) { }
      }
    }

    return isReadable;
  }

  /**
   * Determines whether the supplied directory is writable by trying to
   * write a file to it.
   * This works around JDK bugs which return the wrong results for
   * {@link java.io.File#canWrite()} on Windows and possibly on Unix.
   *
   * Note: since this method accesses the filesystem, it should not be
   * used in contexts in which performance is critical.
   * Note: this method changes the file access time and may change the
   * file modification time.
   *
   * @since 1.71.0
   * @param dir the File whose permissions are to be determined
   * @return <code>true</code> if the File is not null, exists, is a
   *         directory and can be written to, <code>false</code> otherwise
   */
  // XXX dir.canWrite() has issues on Windows, so verify it directly:
  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6203387
  public static boolean isDirectoryWritable(File dir) {
    boolean isWritable = false;

    if (dir != null) {
      // new File("").isDirectory() is false, even though getAbsolutePath() returns the right path.
      // this resolves it
      dir = dir.getAbsoluteFile();

      if (dir.isDirectory()) {
        File file = new File(
          dir,
          String.format(
            "pms_directory_write_test_%d_%d.tmp",
            System.currentTimeMillis(),
            Thread.currentThread().getId()
          )
        );

        try {
          if (file.createNewFile()) {
            if (isFileWritable(file)) {
              isWritable = true;
            }

            if (!file.delete()) {
              logger.warn("Can't delete temporary test file: {}", file.getAbsolutePath());
            }
          }
        } catch (IOException ioe) {
        } catch (SecurityException se) { }
      }
    }

    return isWritable;
  }
}
TOP

Related Classes of net.pms.util.FileUtil$FileLocation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.