// 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 | SecurityException ioe) {
        }
      }
    }

    return isWritable;
  }
  /**
   * 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];
    final UniversalDetector universalDetector;
    try (BufferedInputStream bufferedInputStream =
        new BufferedInputStream(new FileInputStream(file))) {
      universalDetector = new UniversalDetector(null);
      int numberOfBytesRead;
      while ((numberOfBytesRead = bufferedInputStream.read(buf)) > 0
          && !universalDetector.isDone()) {
        universalDetector.handleData(buf, 0, numberOfBytesRead);
      }
    }
    universalDetector.dataEnd();
    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;
  }
  public static boolean isFolderRelevant(
      File f, PmsConfiguration configuration, Set<String> ignoreFiles) {
    if (f.isDirectory() && configuration.isHideEmptyFolders()) {
      File[] children = f.listFiles();

      /**
       * listFiles() returns null if "this abstract pathname does not denote a directory, or if an
       * I/O error occurs". in this case (since we've already confirmed that it's a directory), this
       * seems to mean the directory is non-readable
       * http://www.ps3mediaserver.org/forum/viewtopic.php?f=6&t=15135
       * http://stackoverflow.com/questions/3228147/retrieving-the-underlying-error-when-file-listfiles-return-null
       */
      if (children == null) {
        LOGGER.warn("Can't list files in non-readable directory: {}", f.getAbsolutePath());
      } else {
        for (File child : children) {
          if (ignoreFiles.contains(child.getAbsolutePath())) {
            continue;
          }

          if (child.isFile()) {
            if (FormatFactory.getAssociatedFormat(child.getName()) != null
                || isFileRelevant(child, configuration)) {
              return true;
            }
          } else {
            if (isFolderRelevant(child, configuration, ignoreFiles)) {
              return true;
            }
          }
        }
      }
    }
    return false;
  }
  // 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 | SecurityException ioe) {
        }
      }
    }

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