/**
   * Forces this monitor to update current folder information. This method should be called when a
   * folder has been manually refreshed, so that this monitor doesn't detect changes and try to
   * refresh the table again.
   *
   * @param folder the new current folder
   */
  private void updateFolderInfo(AbstractFile folder) {
    this.currentFolder = folder;
    this.currentFolderDate = currentFolder.getDate();

    // Reset time average
    totalCheckTime = 0;
    nbSamples = 0;
  }
Example #2
0
  public Vector<String> getPossibleCompletions(String path) {
    Vector<String> result = new Vector<String>();
    int index = Math.max(path.lastIndexOf('\\'), path.lastIndexOf('/'));
    if (index != -1) {
      String currentDirectoryName = path.substring(0, index + 1);

      AbstractFile currentDirectory = FileFactory.getFile(currentDirectoryName);
      if (currentDirectory != null && currentDirectory.exists()) {
        long currentDirectoryDate = currentDirectory.getDate();
        if (cachedDirectoryName == null
            || !cachedDirectoryName.equals(currentDirectoryName)
            || currentDirectoryDate != cachedDirectoryDate) {
          AbstractFile[] currentDirectoryFiles;
          try {
            currentDirectoryFiles = getFiles(currentDirectory);
          } catch (IOException e) {
            LOGGER.debug("Caught exception", e);
            return new Vector<String>();
          }

          int nbCurrentDirectoryFiles = currentDirectoryFiles.length;
          cachedDirectoryFileNames = new String[nbCurrentDirectoryFiles];

          for (int i = 0; i < nbCurrentDirectoryFiles; i++) {
            AbstractFile abstractFileI = currentDirectoryFiles[i];
            cachedDirectoryFileNames[i] =
                abstractFileI.getName()
                    + (abstractFileI.isDirectory() ? abstractFileI.getSeparator() : "");
          }

          Arrays.sort(cachedDirectoryFileNames, String.CASE_INSENSITIVE_ORDER);

          cachedDirectoryName =
              currentDirectory.getAbsolutePath()
                  + (currentDirectory.isDirectory() ? "" : currentDirectory.getSeparator());
          cachedDirectoryDate = currentDirectoryDate;
        }

        final String prefix =
            index == path.length() - 1 ? null : path.substring(index + 1).toLowerCase();
        result = PrefixFilter.createPrefixFilter(prefix).filter(cachedDirectoryFileNames);
      }
    }
    return result;
  }
  /**
   * Checks if current file table's folder has changed and if it hasn't, checks if current folder's
   * date has changed and if it has, refresh the file table.
   *
   * @return <code>true</code> if the folder was refreshed.
   */
  private synchronized boolean checkAndRefresh() {
    if (paused || disableAutoRefreshFilter.match(currentFolder)) {
      return false;
    }

    // Update time average next loop
    long timeStamp = System.currentTimeMillis();

    // Check folder's date
    long date = currentFolder.getDate();

    totalCheckTime += System.currentTimeMillis() - timeStamp;
    nbSamples++;

    // Has date changed ?
    // Note that date will be 0 if the folder is no longer available, and thus yield a refresh: this
    // is exactly
    // what we want (the folder will be changed to a 'workable' folder).
    if (date != currentFolderDate) {
      LOGGER.debug(
          this
              + " ("
              + currentFolder.getName()
              + ") Detected changes in current folder, refreshing table!");

      // Try and refresh current folder in a separate thread as to not lock monitor thread
      folderPanel.tryRefreshCurrentFolder();
    }

    if (!forceRefreshFilePath.isEmpty()) {
      synchronized (forceRefreshFilePath) {
        String folderPath = currentFolder.getAbsolutePath();
        for (String path : forceRefreshFilePath) {
          if (path.startsWith(folderPath)) {
            forceRefreshFilePath.remove(path);
            folderPanel.tryRefreshCurrentFolder();
            break;
          }
        }
      }
    }

    return false;
  }
  public FolderChangeMonitor(FolderPanel folderPanel) {
    this.folderPanel = folderPanel;

    // Listen to folder changes to know when a folder is being / has been changed
    folderPanel.getLocationManager().addLocationListener(this);

    this.currentFolder = folderPanel.getCurrentFolder();
    this.currentFolderDate = currentFolder.getDate();

    // Folder contents is up-to-date let's wait before checking it for changes
    this.lastCheckTimestamp = System.currentTimeMillis();
    this.waitBeforeCheckTime = waitAfterRefresh;

    folderPanel.getMainFrame().addWindowListener(this);

    instances.add(this);

    // create and start the monitor thread on first FolderChangeMonitor instance
    if (monitorThread == null && checkPeriod >= 0) {
      monitorThread = new Thread(this, getClass().getName());
      monitorThread.setDaemon(true);
      monitorThread.start();
    }
  }
Example #5
0
  /**
   * Copies recursively the given file or folder.
   *
   * @param file the file or folder to move
   * @param recurseParams destination folder where the given file will be copied (null for top level
   *     files)
   * @return <code>true</code> if the file has been copied.
   */
  @Override
  protected boolean processFile(AbstractFile file, Object recurseParams) {
    // Stop if interrupted
    if (getState() == INTERRUPTED) return false;

    // Destination folder
    AbstractFile destFolder = recurseParams == null ? baseDestFolder : (AbstractFile) recurseParams;

    // Is current file in base folder ?
    boolean isFileInBaseFolder = files.indexOf(file) != -1;

    // Determine filename in destination
    String destFileName;
    if (isFileInBaseFolder && newName != null) destFileName = newName;
    else destFileName = file.getName();

    // Create destination AbstractFile instance
    AbstractFile destFile = createDestinationFile(destFolder, destFileName);
    if (destFile == null) return false;
    currentDestFile = destFile;

    // Do nothing if file is a symlink (skip file and return)
    if (file.isSymlink()) return true;

    destFile = checkForCollision(file, destFolder, destFile, false);
    if (destFile == null) return false;

    // Copy directory recursively
    if (file.isDirectory()) {
      // Create the folder in the destination folder if it doesn't exist
      if (!(destFile.exists() && destFile.isDirectory())) {
        // Loop for retry
        do {
          try {
            destFile.mkdir();
          } catch (IOException e) {
            // Unable to create folder
            int ret =
                showErrorDialog(
                    errorDialogTitle, Translator.get("cannot_create_folder", destFileName));
            // Retry loops
            if (ret == RETRY_ACTION) continue;
            // Cancel or close dialog return false
            return false;
            // Skip continues
          }
          break;
        } while (true);
      }

      // and copy each file in this folder recursively
      do { // Loop for retry
        try {
          // for each file in folder...
          AbstractFile subFiles[] = file.ls();
          // filesDiscovered(subFiles);
          for (int i = 0; i < subFiles.length && getState() != INTERRUPTED; i++) {
            // Notify job that we're starting to process this file (needed for recursive calls to
            // processFile)
            nextFile(subFiles[i]);
            processFile(subFiles[i], destFile);
          }

          // Set currentDestFile back to the enclosing folder in case an overridden processFile
          // method
          // needs to work with the folder after calling super.processFile.
          currentDestFile = destFile;

          // Only when finished with folder, set destination folder's date to match the original
          // folder one
          if (destFile.isFileOperationSupported(FileOperation.CHANGE_DATE)) {
            try {
              destFile.changeDate(file.getDate());
            } catch (IOException e) {
              AppLogger.fine("failed to change the date of " + destFile, e);
              // Fail silently
            }
          }

          return true;
        } catch (IOException e) {
          // file.ls() failed
          int ret =
              showErrorDialog(
                  errorDialogTitle, Translator.get("cannot_read_folder", file.getName()));
          // Retry loops
          if (ret == RETRY_ACTION) continue;
          // Cancel, skip or close dialog returns false
          return false;
        }
      } while (true);
    }
    // File is a regular file, copy it
    else {
      // Copy the file
      return tryCopyFile(file, destFile, append, errorDialogTitle);
    }
  }
 @Override
 public long getDate() {
   return file.getDate();
 }
Example #7
0
  /**
   * Moves recursively the given file or folder.
   *
   * @param file the file or folder to move
   * @param recurseParams destination folder where the given file will be moved (null for top level
   *     files)
   * @return <code>true</code> if the file has been moved completly (copied + deleted).
   */
  @Override
  protected boolean processFile(AbstractFile file, Object recurseParams) {
    // Stop if interrupted
    if (getState() == State.INTERRUPTED) return false;

    // Destination folder
    AbstractFile destFolder = recurseParams == null ? baseDestFolder : (AbstractFile) recurseParams;

    // Is current file at the base folder level ?
    boolean isFileInBaseFolder = files.contains(file);

    // Determine filename in destination
    String originalName = file.getName();
    String destFileName = isFileInBaseFolder && newName != null ? newName : originalName;

    // create destination AbstractFile instance
    AbstractFile destFile = createDestinationFile(destFolder, destFileName);
    if (destFile == null) return false;

    // Do not follow symlink, simply delete it and return
    if (file.isSymlink()) {
      do { // Loop for retry
        try {
          file.delete();
          return true;
        } catch (IOException e) {
          LOGGER.debug("IOException caught", e);

          int ret =
              showErrorDialog(
                  errorDialogTitle, Translator.get("cannot_delete_file", file.getAbsolutePath()));
          // Retry loops
          if (ret == RETRY_ACTION) {
            continue;
          }
          // Cancel, skip or close dialog returns false
          return false;
        }
      } while (true);
    }

    destFile = checkForCollision(file, destFolder, destFile, renameMode);
    if (destFile == null) return false;

    // Let's try to rename the file using AbstractFile#renameTo() whenever possible, as it is more
    // efficient
    // than moving the file manually.
    //
    // Do not attempt to rename the file in the following cases:
    // - destination has to be appended
    // - file schemes do not match (at the time of writing, no filesystem supports mixed scheme
    // renaming)
    // - if the 'rename' operation is not supported
    // Note: we want to avoid calling AbstractFile#renameTo when we know it will fail, as it
    // performs some costly
    // I/O bound checks and ends up throwing an exception which also comes at a cost.
    if (!append
        && file.getURL().schemeEquals(destFile.getURL())
        && file.isFileOperationSupported(FileOperation.RENAME)) {
      try {
        file.renameTo(destFile);
        return true;
      } catch (IOException e) {
        // Fail silently: renameTo might fail under normal conditions, for instance for local files
        // which are
        // not located on the same volume.
        LOGGER.debug(
            "Failed to rename " + file + " into " + destFile + " (not necessarily an error)", e);
      }
    }
    // Rename couldn't be used or didn't succeed: move the file manually

    // Move the directory and all its children recursively, by copying files to the destination and
    // then deleting them.
    if (file.isDirectory()) {
      // create the destination folder if it doesn't exist
      if (!(destFile.exists() && destFile.isDirectory())) {
        do { // Loop for retry
          try {
            destFile.mkdir();
          } catch (IOException e) {
            int ret =
                showErrorDialog(
                    errorDialogTitle,
                    Translator.get("cannot_create_folder", destFile.getAbsolutePath()));
            // Retry loops
            if (ret == RETRY_ACTION) {
              continue;
            }
            // Cancel, skip or close dialog returns false
            return false;
          }
          break;
        } while (true);
      }

      // move each file in this folder recursively
      do { // Loop for retry
        try {
          AbstractFile subFiles[] = file.ls();
          boolean isFolderEmpty = true;
          for (AbstractFile subFile : subFiles) {
            // Return now if the job was interrupted, so that we do not attempt to delete this
            // folder
            if (getState() == State.INTERRUPTED) return false;

            // Notify job that we're starting to process this file (needed for recursive calls to
            // processFile)
            nextFile(subFile);
            if (!processFile(subFile, destFile)) isFolderEmpty = false;
          }

          // Only when finished with folder, set destination folder's date to match the original
          // folder one
          if (destFile.isFileOperationSupported(FileOperation.CHANGE_DATE)) {
            try {
              destFile.changeDate(file.getDate());
            } catch (IOException e) {
              LOGGER.debug("failed to change the date of " + destFile, e);
              // Fail silently
            }
          }

          // If one file failed to be moved, return false (failure) since this folder could not be
          // moved totally
          if (!isFolderEmpty) return false;
        } catch (IOException e) {
          // file.ls() failed
          int ret =
              showErrorDialog(
                  errorDialogTitle, Translator.get("cannot_read_folder", file.getName()));
          // Retry loops
          if (ret == RETRY_ACTION) continue;
          // Cancel, skip or close dialog returns false
          return false;
        }
        break;
      } while (true);

      // Return now if the job was interrupted, so that we do not attempt to delete this folder
      if (getState() == State.INTERRUPTED) return false;

      // finally, delete the empty folder
      do { // Loop for retry
        try {
          file.delete();
          return true;
        } catch (IOException e) {
          int ret =
              showErrorDialog(
                  errorDialogTitle, Translator.get("cannot_delete_folder", file.getAbsolutePath()));
          // Retry loops
          if (ret == RETRY_ACTION) continue;
          // Cancel, skip or close dialog returns false
          return false;
        }
      } while (true);
    }
    // File is a regular file, move it by copying it to the destination and then deleting it
    else {
      // if renameTo() was not supported or failed, or if it wasn't possible because of 'append',
      // try the hard way by copying the file first, and then deleting the source file.
      if (tryCopyFile(file, destFile, append, errorDialogTitle)
          && getState() != State.INTERRUPTED) {
        // Delete the source file
        do { // Loop for retry
          try {
            file.delete();
            // All OK
            return true;
          } catch (IOException e) {
            LOGGER.debug("IOException caught", e);

            int ret =
                showErrorDialog(
                    errorDialogTitle, Translator.get("cannot_delete_file", file.getAbsolutePath()));
            // Retry loops
            if (ret == RETRY_ACTION) continue;
            // Cancel, skip or close dialog returns false
            return false;
          }
        } while (true);
      }

      return false;
    }
  }
  public int compare(AbstractFile f1, AbstractFile f2) {
    long diff;

    boolean is1Directory = f1.isDirectory();
    boolean is2Directory = f2.isDirectory();

    if (directoriesFirst) {
      if (is1Directory && !is2Directory)
        return -1; // ascending has no effect on the result (a directory is always first) so let's
      // return
      else if (is2Directory && !is1Directory)
        return 1; // ascending has no effect on the result (a directory is always first) so let's
      // return
      // At this point, either both files are directories or none of them are
    }

    if (criterion == SIZE_CRITERION) {
      // Consider that directories have a size of 0
      long fileSize1 = is1Directory ? 0 : f1.getSize();
      long fileSize2 = is2Directory ? 0 : f2.getSize();

      // Returns file1 size - file2 size, file size of -1 (unavailable) is considered as enormous
      // (max long value)
      diff =
          (fileSize1 == -1 ? Long.MAX_VALUE : fileSize1)
              - (fileSize2 == -1 ? Long.MAX_VALUE : fileSize2);
    } else if (criterion == DATE_CRITERION) {
      diff = f1.getDate() - f2.getDate();
    } else if (criterion == PERMISSIONS_CRITERION) {
      diff = f1.getPermissions().getIntValue() - f2.getPermissions().getIntValue();
    } else if (criterion == EXTENSION_CRITERION) {
      diff = compareStrings(f1.getExtension(), f2.getExtension(), true, true);
    } else if (criterion == OWNER_CRITERION) {
      diff = compareStrings(f1.getOwner(), f2.getOwner(), true, true);
    } else if (criterion == GROUP_CRITERION) {
      diff = compareStrings(f1.getGroup(), f2.getGroup(), true, true);
    } else { // criterion == NAME_CRITERION
      diff = compareStrings(f1.getName(), f2.getName(), true);
      if (diff == 0) {
        // This should never happen unless the current filesystem allows a directory to have
        // several files with different case variations of the same name.
        // AFAIK, no OS/filesystem allows this, but just to be safe.

        // Case-sensitive name comparison
        diff = compareStrings(f1.getName(), f2.getName(), false);
      }
    }

    if (criterion != NAME_CRITERION
        && diff == 0) // If both files have the same criterion's value, compare names
    diff = compareStrings(f1.getName(), f2.getName(), true, false);

    // Cast long value to int, without overflowing the int if the long value exceeds the min or max
    // int value
    int intValue;

    if (diff > Integer.MAX_VALUE) intValue = Integer.MAX_VALUE; // 2147483647
    else if (diff
        < Integer.MIN_VALUE
            + 1) // Need that +1 so that the int is not overflowed if ascending order is enabled
      // (i.e. int is negated)
      intValue = Integer.MIN_VALUE + 1; // 2147483647
    else intValue = (int) diff;

    return ascending
        ? intValue
        : -intValue; // Note: ascending is used more often, more efficient to negate for descending
  }