Package org.hive2hive.core.processes.implementations.files.util

Source Code of org.hive2hive.core.processes.implementations.files.util.FileRecursionUtil

package org.hive2hive.core.processes.implementations.files.util;

import java.io.File;
import java.nio.file.Path;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hive2hive.core.exceptions.NoPeerConnectionException;
import org.hive2hive.core.exceptions.NoSessionException;
import org.hive2hive.core.file.FileUtil;
import org.hive2hive.core.model.FileIndex;
import org.hive2hive.core.model.FolderIndex;
import org.hive2hive.core.model.Index;
import org.hive2hive.core.network.NetworkManager;
import org.hive2hive.core.processes.ProcessFactory;
import org.hive2hive.core.processes.framework.abstracts.ProcessComponent;
import org.hive2hive.core.processes.framework.concretes.SequentialProcess;
import org.hive2hive.core.processes.framework.decorators.AsyncComponent;
import org.hive2hive.core.processes.framework.interfaces.IProcessComponent;

public class FileRecursionUtil {

  private FileRecursionUtil() {
    // only static methods
  }

  public enum FileProcessAction {
    NEW_FILE,
    MODIFY_FILE,
  }

  /**
   * Creates an upload process. The order of the files does not depend.
   *
   * @param files a list of files to upload
   * @param action whether the files are for updating or as new files
   * @param networkManager the network manager with a session
   * @return the root process (containing multiple async components) that manages the upload correctly
   * @throws NoSessionException
   * @throws NoPeerConnectionException
   */
  public static ProcessComponent buildUploadProcess(List<Path> files, FileProcessAction action,
      NetworkManager networkManager) throws NoSessionException, NoPeerConnectionException {
    // the sequential root process
    SequentialProcess rootProcess = new SequentialProcess();

    // key idea: Find the children with the same parents and add them to a sequential process. They need
    // to be sequential because the parent meta file must be adapted. If they would run in parallel, they
    // would modify the parent meta folder simultaneously.
    Map<Path, SequentialProcess> sameParents = new HashMap<Path, SequentialProcess>();
    for (Path file : files) {
      // create the process which uploads or updates the file
      ProcessComponent uploadProcess;
      if (action == FileProcessAction.NEW_FILE)
        uploadProcess = ProcessFactory.instance().createNewFileProcess(file.toFile(), networkManager);
      else
        uploadProcess = ProcessFactory.instance().createUpdateFileProcess(file.toFile(),
            networkManager);

      Path parentFile = file.getParent();
      if (sameParents.containsKey(parentFile)) {
        // a sibling exists that already created a sequential process
        SequentialProcess sequentialProcess = sameParents.get(parentFile);
        sequentialProcess.add(uploadProcess);
      } else {
        // first file with this parent; create new sequential process
        SequentialProcess sequentialProcess = new SequentialProcess();
        sequentialProcess.add(uploadProcess);
        sameParents.put(parentFile, sequentialProcess);
      }
    }

    // the children are now grouped together. Next, we need to link the parent files.
    for (Path parent : sameParents.keySet()) {
      AsyncComponent asyncChain = new AsyncComponent(sameParents.get(parent));
      Path parentOfParent = parent.getParent();
      if (sameParents.containsKey(parentOfParent)) {
        // parent exists, we add this sub-process (sequential) to it. It can be async here
        SequentialProcess sequentialProcess = sameParents.get(parentOfParent);
        sequentialProcess.add(asyncChain);
      } else {
        // parent does not exist --> attach the sub-tree to the root
        rootProcess.add(asyncChain);
      }
    }

    return rootProcess;
  }

  /**
   * Creates a process chain to delete all files in the list. Note that the list must be in pre-order, else
   * there will occur errors.
   *
   * @param files list of files to delete in preorder
   * @param networkManager the network manager with a session
   * @return the (async) root process component
   * @throws NoSessionException
   * @throws NoPeerConnectionException
   */
  public static ProcessComponent buildDeletionProcess(List<Path> files, NetworkManager networkManager)
      throws NoSessionException, NoPeerConnectionException {
    // the sequential root process
    SequentialProcess rootProcess = new SequentialProcess();

    // deletion must happen in reverse tree order. Since this is very complicated when it contains
    // asynchronous components, we simply delete them all in the same thread (reverse preorder of course)
    Collections.reverse(files);
    for (Path file : files) {
      ProcessComponent deletionProcess = ProcessFactory.instance().createDeleteFileProcess(
          file.toFile(), networkManager);
      rootProcess.add(deletionProcess);
    }

    return new AsyncComponent(rootProcess);
  }

  /**
   * This is a workaround to delete files when a {@link FolderIndex} is already existent. Since the node is
   * already here, the deletion could be speed up because it must not be looked up in the user profile.
   *
   * @param files
   * @param networkManager
   * @return
   * @throws NoSessionException
   * @throws NoPeerConnectionException
   */
  @Deprecated
  public static ProcessComponent buildDeletionProcessFromNodelist(List<Index> files,
      NetworkManager networkManager) throws NoSessionException, NoPeerConnectionException {
    List<Path> filesToDelete = new ArrayList<Path>();
    for (Index documentIndex : files) {
      filesToDelete.add(FileUtil.getPath(networkManager.getSession().getRoot(), documentIndex));
    }

    return buildDeletionProcess(filesToDelete, networkManager);
  }

  /**
   * Creates a process with all children processes.
   *
   * @param files the files to download (order does not depend)
   * @param networkManager the connected node (note, it must have a session)
   * @return the root process component containing all sub-processes (and sub-tasks)
   * @throws NoSessionException
   */
  public static ProcessComponent buildDownloadProcess(List<Index> files, NetworkManager networkManager)
      throws NoSessionException {
    // the root process, where everything runs in parallel (only async children are added)
    SequentialProcess rootProcess = new SequentialProcess();

    // build a flat map of the folders to download (such that O(1) for each lookup)
    Map<FolderIndex, SequentialProcess> folderMap = new HashMap<FolderIndex, SequentialProcess>();
    Map<FileIndex, AsyncComponent> fileMap = new HashMap<FileIndex, AsyncComponent>();

    for (Index file : files) {
      PublicKey fileKey = file.getFilePublicKey();
      ProcessComponent downloadProcess = ProcessFactory.instance().createDownloadFileProcess(fileKey,
          networkManager);
      if (file.isFolder()) {
        // when a directory, the process may have multiple children, thus we need a sequential process
        SequentialProcess folderProcess = new SequentialProcess();
        folderProcess.add(downloadProcess);
        folderMap.put((FolderIndex) file, folderProcess);
      } else {
        // when a file, the process can run in parallel with all siblings (done in next step)
        fileMap.put((FileIndex) file, new AsyncComponent(downloadProcess));
      }
    }

    // find children with same parents and make them run in parallel
    // idea: iterate through all children and search for parent in other map. If not there, they can be
    // added to the root process anyway
    for (FileIndex file : fileMap.keySet()) {
      AsyncComponent fileProcess = fileMap.get(file);
      Index parent = file.getParent();
      if (parent == null) {
        // file is in root, thus we can add it to the root process
        rootProcess.add(fileProcess);
      } else if (folderMap.containsKey(parent)) {
        // the parent exists here
        SequentialProcess parentProcess = folderMap.get(parent);
        parentProcess.add(fileProcess);
      } else {
        // file is not in root and parent is not here, thus we simply add it to the root process
        rootProcess.add(fileProcess);
      }
    }

    // files and folder are linked. We now link the folders with other folders
    for (FolderIndex folder : folderMap.keySet()) {
      SequentialProcess folderProcess = folderMap.get(folder);
      // In addition, we can make this process run asynchronous because it does not affect the siblings
      AsyncComponent asyncFolderProcess = new AsyncComponent(folderProcess);
      Index parent = folder.getParent();
      if (parent == null) {
        // file is in root, thus we can add it to the root process.
        rootProcess.add(asyncFolderProcess);
      } else if (folderMap.containsKey(parent)) {
        // this folder has a parent
        SequentialProcess parentProcess = folderMap.get(parent);
        parentProcess.add(asyncFolderProcess);
      } else {
        // folder is not in root and parent folder does not sync here, add it to the root process
        rootProcess.add(asyncFolderProcess);
      }
    }

    return rootProcess;
  }

  /**
   * Creates a process chain of the given processes in the same order
   *
   * @param processes the processes to align to a center
   * @return the process chain
   */
  public static IProcessComponent createProcessChain(List<ProcessComponent> processes) {
    SequentialProcess rootProcess = new SequentialProcess();
    for (ProcessComponent processComponent : processes) {
      rootProcess.add(processComponent);
    }
    return rootProcess;
  }

  /**
   * Get a list of all files and subfiles in the root directory. The files are visited and returned in
   * preorder
   *
   * @param root
   * @return
   */
  public static List<Path> getPreorderList(Path root) {
    List<Path> allFiles = new ArrayList<Path>();
    listFiles(root, allFiles);
    return allFiles;
  }

  private static void listFiles(Path path, List<Path> preorderList) {
    preorderList.add(path);
    File[] listFiles = path.toFile().listFiles();
    if (listFiles != null)
      for (File child : listFiles) {
        listFiles(child.toPath(), preorderList);
      }
  }
}
TOP

Related Classes of org.hive2hive.core.processes.implementations.files.util.FileRecursionUtil

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.