/*
 * Copyright 2016-present Facebook, Inc.
 *
 * 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.facebook.buck.jvm.java;

import com.facebook.buck.io.file.PathListing;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.function.BiFunction;
import java.util.function.Function;

public class ClasspathChecker {

  private static final ImmutableSet<String> ALLOWED_EXTENSIONS_SET = ImmutableSet.of("jar", "zip");

  private final String separator;
  private final String pathSeparator;
  private final Function<String, Path> toPathFunc;
  private final Function<Path, Boolean> isDirectoryFunc;
  private final Function<Path, Boolean> isFileFunc;
  private final BiFunction<Path, String, Iterable<Path>> globberFunc;

  public ClasspathChecker() {
    this(
        File.separator,
        File.pathSeparator,
        Paths::get,
        Files::isDirectory,
        Files::isRegularFile,
        (path, glob) -> {
          try {
            return PathListing.listMatchingPaths(path, glob, PathListing.GET_PATH_MODIFIED_TIME);
          } catch (IOException e) {
            throw new UncheckedIOException(e);
          }
        });
  }

  @VisibleForTesting
  ClasspathChecker(
      String separator,
      String pathSeparator,
      Function<String, Path> toPathFunc,
      Function<Path, Boolean> isDirectoryFunc,
      Function<Path, Boolean> isFileFunc,
      BiFunction<Path, String, Iterable<Path>> globberFunc) {
    this.separator = separator;
    this.pathSeparator = pathSeparator;
    this.toPathFunc = toPathFunc;
    this.isDirectoryFunc = isDirectoryFunc;
    this.isFileFunc = isFileFunc;
    this.globberFunc = globberFunc;
  }

  /**
   * Parses a Java classpath string ("path/to/foo:baz.jar:blech.zip:path/to/*") and checks if at
   * least one entry is valid (exists on disk).
   *
   * <p>From http://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html :
   *
   * <p>Class path entries can contain the basename wildcard character *, which is considered
   * equivalent to specifying a list of all the files in the directory with the extension .jar or
   * .JAR. For example, the class path entry foo/* specifies all JAR files in the directory named
   * foo. A classpath entry consisting simply of * expands to a list of all the jar files in the
   * current directory.
   */
  public boolean validateClasspath(String classpath) {
    for (String entry : Splitter.on(pathSeparator).split(classpath)) {
      // On Windows, Path.endsWith("*") throws an error:
      //
      // java.nio.file.InvalidPathException: Illegal char <*> at index 0
      //
      // So, we split manually.
      List<String> classpathComponents = Splitter.on(separator).splitToList(entry);
      if (classpathComponents.isEmpty()) {
        continue;
      }

      if (Iterables.getLast(classpathComponents).equals("*")) {
        // Trim the * off the path.
        List<String> dirComponents = classpathComponents.subList(0, classpathComponents.size() - 1);
        Path entryDir = toPathFunc.apply(Joiner.on(separator).join(dirComponents));
        if (!Iterables.isEmpty(globberFunc.apply(entryDir, "*.jar"))) {
          return true;
        } else if (!Iterables.isEmpty(globberFunc.apply(entryDir, "*.JAR"))) {
          return true;
        }
      } else {
        Path entryPath = toPathFunc.apply(entry);
        if (isDirectoryFunc.apply(entryPath)) {
          return true;
        } else if (isFileFunc.apply(entryPath)
            && ALLOWED_EXTENSIONS_SET.contains(
                com.google.common.io.Files.getFileExtension(
                    entryPath.toString().toLowerCase(Locale.US)))) {
          return true;
        }
      }
    }
    return false;
  }
}

@@ -1198,16 +1198,53 @@
 unction;
+%0Aimport java.util.function.Predicate;
 %0A%0Apublic
@@ -1510,38 +1510,30 @@
 e final 
-Function%3CPath, Boolean
+Predicate%3CPath
 %3E isDire
@@ -1559,38 +1559,30 @@
 e final 
-Function%3CPath, Boolean
+Predicate%3CPath
 %3E isFile
@@ -2220,38 +2220,30 @@
 ,%0A      
-Function%3CPath, Boolean
+Predicate%3CPath
 %3E isDire
@@ -2263,30 +2263,22 @@
     
-Function%3CPath, Boolean
+Predicate%3CPath
 %3E is
@@ -4284,29 +4284,28 @@
 rectoryFunc.
-apply
+test
 (entryPath))
@@ -4356,29 +4356,28 @@
 (isFileFunc.
-apply
+test
 (entryPath)%0A

Replace Function<X,Boolean> with Predicate to avoid boxing boolean