Package org.syncany.operations.down

Source Code of org.syncany.operations.down.FileSystemActionComparator

/*
* Syncany, www.syncany.org
* Copyright (C) 2011-2014 Philipp C. Heckel <philipp.heckel@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.syncany.operations.down;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.syncany.database.FileVersion.FileType;
import org.syncany.operations.down.actions.DeleteFileSystemAction;
import org.syncany.operations.down.actions.FileSystemAction;
import org.syncany.operations.down.actions.NewFileSystemAction;
import org.syncany.operations.down.actions.RenameFileSystemAction;

/**
* Sorts file system actions according to their natural order to prevent scenarios
* in which a non-empty directory is deleted, ...
*
* Sorting necessary:
* 1. Delete file/symlink actions must happen first
* 2. New folder actions must happen after delete file/symlink actions, and must be sorted by path, shortest first (within new actions)
* 3. New file/symlink actions must happen after new folder actions
* 4. Rename file/symlink actions must happen after new folder actions
* 5. Delete folder actions must happen last, sorted by path, longest first (within delete actions)
*    a. except if the deleted folder has a name clash with another action (rename/new)
*       in that case, it must happen before the other action
*
* No sorting necessary:
* - Change file/symlink actions can happen anytime
* - Set attributes actions can happen anytime
*
* Cannot happen:
* - Rename folder
* - Change folder
*
*
* TODO [medium] NewSymlinkFileSystemAction --> NewFile (has no content)
*/
public class FileSystemActionComparator  {
  private static final Logger logger = Logger.getLogger(FileSystemActionComparator.class.getSimpleName());
 
  private static final Object[][] TARGET_ORDER =  new Object[][] {
    new Object[] { DeleteFileSystemAction.class, FileType.FILE },
    new Object[] { DeleteFileSystemAction.class, FileType.SYMLINK },
    new Object[] { NewFileSystemAction.class, FileType.FOLDER },
    new Object[] { NewFileSystemAction.class, FileType.FILE },
    new Object[] { NewFileSystemAction.class, FileType.SYMLINK },
    new Object[] { RenameFileSystemAction.class, FileType.FILE },
    new Object[] { RenameFileSystemAction.class, FileType.SYMLINK },
    new Object[] { DeleteFileSystemAction.class, FileType.FOLDER }
  };
 
  private InternalFileSystemActionComparator internalComparator = new InternalFileSystemActionComparator();
     
  public void sort(List<FileSystemAction> actions) {
    Collections.sort(actions, internalComparator);
    postCompareSort(actions);
   
    if (logger.isLoggable(Level.INFO)) {
      logger.log(Level.INFO, "   Sorted actions:");
     
      for (FileSystemAction action : actions) {
        logger.log(Level.INFO, "   + "+action);
      }
    }
  }
 
  /**
   * Fixes the case in which a folder has been swapped with a file (case 5a, see above)
   *
   * New/Renamed file system actions must happen *after* the file was deleted. This method
   * moves new/renamed actions below deleted folder actions.
   *
   * DEL  FILE     file1.jpg     
   * NEW  FOLDER   folder1
   * NEW  FILE     folder1/fileinfolder1.jpg                       < No Issue, but needs to be checked
   * NEW  FILE     folder2                                         <<< Issue, because folder2 is deleted below!
   * NEW  SYMLINK  folder1/symlinkinfolder1.jpg                    < No Issue, but needs to be checked
   * REN  FILE     folder2/fileinfolder2.jpg -> folder1/x2.jpg
   * REN  SYMLINK  folder2/symlinkinfolder2.jpg -> folder1/sym2
   * DEL  FOLDER   folder2
   *                                                               <<< New position of "NEW FILE folder2"!
   */
  public void postCompareSort(List<FileSystemAction> actions) {
    List<FileSystemAction> fixedActions = new ArrayList<FileSystemAction>(actions);
   
    int i = 0;
   
    while (i < fixedActions.size()) {
      FileSystemAction currentAction = fixedActions.get(i);
     
      if (currentAction instanceof DeleteFileSystemAction && currentAction.getType() == FileType.FOLDER) {
        break;
      }
     
      //System.out.println("testing "+currentAction);
      if ((currentAction instanceof NewFileSystemAction || currentAction instanceof RenameFileSystemAction)
          && (currentAction.getType() == FileType.FILE || currentAction.getType() == FileType.SYMLINK)) {
         
        int conflictingDeleteActionIndex = getPathConflictingWithDeleteActionIndex(currentAction, fixedActions);
       
        if (conflictingDeleteActionIndex >= 0) {
          logger.log(Level.INFO, "     --> match, conflict ["+i+"]: "+currentAction);
          logger.log(Level.INFO, "                    with ["+conflictingDeleteActionIndex+"]: "+fixedActions.get(conflictingDeleteActionIndex));
         
          fixedActions.remove(i);
          fixedActions.add(conflictingDeleteActionIndex, currentAction);
         
          i--; // fix counter!
        }
      }
     
      i++;
    }   
   
    // Replace
    actions.clear();
    actions.addAll(fixedActions);
  }
     
  private int getPathConflictingWithDeleteActionIndex(FileSystemAction currentAction, List<FileSystemAction> deleteFolderActions) {
    for (int i=0; i<deleteFolderActions.size(); i++) {
      FileSystemAction action = deleteFolderActions.get(i);

      if (action instanceof DeleteFileSystemAction && action.getType() == FileType.FOLDER) {     
        if (action.getFile2().getPath().equals(currentAction.getFile2().getPath())) {
          return i;
        }
      }
    }
   
    return -1;
  }

  private class InternalFileSystemActionComparator implements Comparator<FileSystemAction> {
    @Override
    public int compare(FileSystemAction a1, FileSystemAction a2) {
      int a1Position = determinePosition(a1);
      int a2Position = determinePosition(a2);
     
      if (a1Position > a2Position) {
        return 1;
      }
      else if (a1Position < a2Position) {
        return -1;
      }
     
      return compareByFullName(a1, a2);
    }
       
    private int compareByFullName(FileSystemAction a1, FileSystemAction a2) {
      // For deleted, do the longest path first
      if (a1.getClass().equals(DeleteFileSystemAction.class)) {
        return -1 * a1.getFile2().getPath().compareTo(a2.getFile2().getPath());
      }
     
      // For new folder actions, do the shortest path first
      else if (a1.getClass().equals(NewFileSystemAction.class)) {
        return a1.getFile2().getPath().compareTo(a2.getFile2().getPath());
      }
     
      return 0;
    }

    private int determinePosition(FileSystemAction a) {
      for (int i=0; i<TARGET_ORDER.length; i++) {
        Class<?> targetClass = (Class<?>) TARGET_ORDER[i][0];
        FileType targetFileType = (FileType) TARGET_ORDER[i][1];
       
        if (a.getClass().equals(targetClass) && a.getType() == targetFileType) {         
          return i;
        }
      }
     
      return -1;
    }
  }
 
}
TOP

Related Classes of org.syncany.operations.down.FileSystemActionComparator

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.