/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.master.file;

import alluxio.AlluxioURI;
import alluxio.collections.Pair;
import alluxio.exception.FileDoesNotExistException;
import alluxio.exception.InvalidPathException;
import alluxio.master.file.meta.Inode;
import alluxio.master.file.meta.InodeDirectory;
import alluxio.master.file.meta.MountTable;
import alluxio.master.file.options.DeleteOptions;
import alluxio.underfs.UnderFileSystem;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;

import javax.annotation.Nullable;

/**
 * Helper class for deleting persisted entries from the UFS.
 */
public final class SafeUfsDeleter implements UfsDeleter {
  private static final Logger LOG = LoggerFactory.getLogger(SafeUfsDeleter.class);

  private final MountTable mMountTable;
  private final AlluxioURI mRootPath;
  private UfsSyncChecker mUfsSyncChecker;

  /**
   * Creates a new instance of {@link SafeUfsDeleter}.
   *
   * @param mountTable the mount table
   * @param inodes sub-tree being deleted (any node should appear before descendants)
   * @param deleteOptions delete options
   */
  public SafeUfsDeleter(MountTable mountTable, List<Pair<AlluxioURI, Inode>> inodes,
      DeleteOptions deleteOptions)
      throws IOException, FileDoesNotExistException, InvalidPathException {
    mMountTable = mountTable;
    // Root of sub-tree occurs before any of its descendants
    mRootPath = inodes.get(0).getFirst();
    if (!deleteOptions.isUnchecked() && !deleteOptions.isAlluxioOnly()) {
      mUfsSyncChecker = new UfsSyncChecker(mMountTable);
      for (Pair<AlluxioURI, Inode> inodePair : inodes) {
        AlluxioURI alluxioUri = inodePair.getFirst();
        Inode inode = inodePair.getSecond();
        // Mount points are not deleted recursively as we need to preserve the directory itself
        if (inode.isPersisted() && inode.isDirectory() && !mMountTable.isMountPoint(alluxioUri)) {
          mUfsSyncChecker.checkDirectory((InodeDirectory) inode, alluxioUri);
        }
      }
    }
  }

  /**
   * Deletes a path if not covered by a recursive delete.
   *
   * @param alluxioUri Alluxio path to delete
   * @param inode to delete
   * @return true, if succeeded; false. if failed to delete
   */
  public boolean delete(AlluxioURI alluxioUri, Inode inode)
      throws IOException, InvalidPathException {
    boolean failedToDelete = false;
    MountTable.Resolution resolution = mMountTable.resolve(alluxioUri);
    String ufsUri = resolution.getUri().toString();
    UnderFileSystem ufs = resolution.getUfs();
    AlluxioURI parentUri = alluxioUri.getParent();
    if (!isRecursiveDeleteSafe(parentUri)) {
      // Parent will not recursively delete, so delete this inode individually
      if (inode.isFile()) {
        if (!ufs.deleteFile(ufsUri)) {
          failedToDelete = ufs.isFile(ufsUri);
          if (!failedToDelete) {
            LOG.warn("The file to delete does not exist in ufs: {}", ufsUri);
          }
        }
      } else {
        if (isRecursiveDeleteSafe(alluxioUri)) {
          if (!ufs.deleteDirectory(ufsUri,
              alluxio.underfs.options.DeleteOptions.defaults().setRecursive(true))) {
            // TODO(adit): handle partial failures of recursive deletes
            failedToDelete = ufs.isDirectory(ufsUri);
            if (!failedToDelete) {
              LOG.warn("The directory to delete does not exist in ufs: {}", ufsUri);
            }
          }
        } else {
          failedToDelete = true;
          LOG.warn("The directory cannot be deleted from the ufs as it is not in sync: {}",
              ufsUri);
        }
      }
    }
    return !failedToDelete;
  }

  /**
   * Check if recursively deleting from the UFS is "safe".
   *
   * @param alluxioUri Alluxio path to delete
   * @return true, if path can be deleted recursively from UFS; false, otherwise
   */
  private boolean isRecursiveDeleteSafe(@Nullable AlluxioURI alluxioUri) {
    if (alluxioUri == null || !alluxioUri.toString().startsWith(mRootPath.toString())) {
      // Path is not part of sub-tree being deleted
      return false;
    }
    if (mUfsSyncChecker == null) {
      // Delete is unchecked
      return true;
    }
    return mUfsSyncChecker.isDirectoryInSync(alluxioUri);
  }
}

@@ -2557,214 +2557,17 @@
 %0A%0A  
-/**%0A   * Deletes a path if not covered by a recursive delete.%0A   *%0A   * @param alluxioUri Alluxio path to delete%0A   * @param inode to delete%0A   * @return true, if succeeded; false. if failed to delete%0A   */
+@Override
 %0A  p

Update javadoc