/*
 * Copyright 2016 The Bazel Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.idea.blaze.base.io;

import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;
import com.intellij.openapi.components.ServiceManager;
import java.io.File;
import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.List;
import javax.annotation.Nullable;

/** File system operations. Mocked out in tests involving file manipulations. */
public class FileOperationProvider {

  public static FileOperationProvider getInstance() {
    return ServiceManager.getService(FileOperationProvider.class);
  }

  public boolean exists(File file) {
    return file.exists();
  }

  public boolean isDirectory(File file) {
    return file.isDirectory();
  }

  public boolean isFile(File file) {
    return file.isFile();
  }

  public long getFileModifiedTime(File file) {
    return file.lastModified();
  }

  public boolean setFileModifiedTime(File file, long time) {
    return file.setLastModified(time);
  }

  public long getFileSize(File file) {
    return file.length();
  }

  @Nullable
  public File[] listFiles(File file) {
    return file.listFiles();
  }

  public void createSymbolicLink(File link, File target) throws IOException {
    Files.createSymbolicLink(link.toPath(), target.toPath());
  }

  public File readSymbolicLink(File link) throws IOException {
    return Files.readSymbolicLink(link.toPath()).toFile();
  }

  public boolean isSymbolicLink(File file) {
    return Files.isSymbolicLink(file.toPath());
  }

  public File getCanonicalFile(File file) throws IOException {
    return file.getCanonicalFile();
  }

  public Path createTempFile(
      Path tempDirectory, String prefix, String suffix, FileAttribute<?>... attributes)
      throws IOException {
    return Files.createTempFile(tempDirectory, prefix, suffix, attributes);
  }

  public File copy(File source, File target, CopyOption... options) throws IOException {
    return Files.copy(source.toPath(), target.toPath(), options).toFile();
  }

  public boolean mkdirs(File file) {
    return file.mkdirs();
  }

  public void deleteRecursively(File file) throws IOException {
    deleteRecursively(file, false);
  }

  /**
   * Deletes the file or directory at the given path recursively, allowing insecure delete according
   * to {@code allowInsecureDelete}.
   *
   * @see RecursiveDeleteOption#ALLOW_INSECURE
   */
  public void deleteRecursively(File file, boolean allowInsecureDelete) throws IOException {
    if (allowInsecureDelete) {
      MoreFiles.deleteRecursively(file.toPath(), RecursiveDeleteOption.ALLOW_INSECURE);
    } else {
      MoreFiles.deleteRecursively(file.toPath());
    }
  }

  // If the file is too big, this method can blow up RAM as it reads the file contents
  // entirely into memory. Only use this for small files.
  public List<String> readAllLines(File file) throws IOException {
    return Files.readAllLines(file.toPath());
  }
}

@@ -655,16 +655,136 @@
 se.io;%0A%0A
+import static com.google.common.collect.ImmutableList.toImmutableList;%0A%0Aimport com.google.common.collect.ImmutableList;%0A
 import c
@@ -810,24 +810,24 @@
 .MoreFiles;%0A
-
 import com.g
@@ -1057,16 +1057,68 @@
 e.Path;%0A
+import java.nio.file.attribute.BasicFileAttributes;%0A
 import j
@@ -1174,24 +1174,95 @@
 .util.List;%0A
+import java.util.function.BiPredicate;%0Aimport java.util.stream.Stream;%0A
 import javax
@@ -3016,32 +3016,400 @@
 .mkdirs();%0A  %7D%0A%0A
+  /** Walk through the directory and return all meet requirement files in it. */%0A  public ImmutableList%3CPath%3E listFilesRecursively(%0A      File dir, int maxDepth, BiPredicate%3CPath, BasicFileAttributes%3E matcher) throws IOException %7B%0A    try (Stream%3CPath%3E stream = Files.find(dir.toPath(), maxDepth, matcher)) %7B%0A      return stream.collect(toImmutableList());%0A    %7D%0A  %7D%0A%0A
   public void de
@@ -3924,16 +3924,16 @@
  else %7B%0A
-
       Mo
@@ -3985,16 +3985,569 @@
  %7D%0A  %7D%0A%0A
+  /**%0A   * Deletes all files within the directory at the given path recursively, allowing insecure delete%0A   * according to %7B@code allowInsecureDelete%7D. Does not delete the directory itself.%0A   *%0A   * @see RecursiveDeleteOption#ALLOW_INSECURE%0A   */%0A  public void deleteDirectoryContents(File file, boolean allowInsecureDelete) throws IOException %7B%0A    if (allowInsecureDelete) %7B%0A      MoreFiles.deleteDirectoryContents(file.toPath(), RecursiveDeleteOption.ALLOW_INSECURE);%0A    %7D else %7B%0A      MoreFiles.deleteDirectoryContents(file.toPath());%0A    %7D%0A  %7D%0A%0A
   // If 

Add file operations