/*
 * Copyright 2014 Google LLC
 *
 * 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.auto.value;

import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import com.google.auto.value.processor.AutoAnnotationProcessor;
import com.google.auto.value.processor.AutoOneOfProcessor;
import com.google.auto.value.processor.AutoValueProcessor;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.processing.Processor;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

/**
 * Tests that we can compile our AutoValue tests using the Eclipse batch compiler. Since the tests
 * exercise many AutoValue subtleties, the ability to compile them all is a good indication of
 * Eclipse support.
 */
public class CompileWithEclipseTest {
  private static final String SOURCE_ROOT = System.getProperty("basedir");

  @BeforeClass
  public static void setSourceRoot() {
    assertWithMessage("basedir property must be set - test must be run from Maven")
        .that(SOURCE_ROOT).isNotNull();
  }

  public @Rule TemporaryFolder tmp = new TemporaryFolder();

  private static final ImmutableSet<String> IGNORED_TEST_FILES =
      ImmutableSet.of("AutoValueNotEclipseTest.java", "CompileWithEclipseTest.java");

  private static final Predicate<File> JAVA_FILE =
      f -> f.getName().endsWith(".java") && !IGNORED_TEST_FILES.contains(f.getName());

  private static final Predicate<File> JAVA8_TEST =
      f -> f.getName().equals("AutoValueJava8Test.java")
          || f.getName().equals("AutoOneOfJava8Test.java")
          || f.getName().equals("EmptyExtension.java");

  @Test
  public void compileWithEclipseJava6() throws Exception {
    compileWithEclipse("6", JAVA_FILE.and(JAVA8_TEST.negate()));
  }

  @Test
  public void compileWithEclipseJava8() throws Exception {
    compileWithEclipse("8", JAVA_FILE);
  }

  private void compileWithEclipse(String version, Predicate<File> predicate) throws IOException {
    File sourceRootFile = new File(SOURCE_ROOT);
    File javaDir = new File(sourceRootFile, "src/main/java");
    File javatestsDir = new File(sourceRootFile, "src/test/java");
    Set<File> sources =
        new ImmutableSet.Builder<File>()
            .addAll(filesUnderDirectory(javaDir, predicate))
            .addAll(filesUnderDirectory(javatestsDir, predicate))
            .build();
    assertThat(sources).isNotEmpty();
    JavaCompiler compiler = new EclipseCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    // This hack is only needed in a Google-internal Java 8 environment where symbolic links make it
    // hard for ecj to find the boot class path. Elsewhere it is unnecessary but harmless. Notably,
    // on Java 9+ there is no rt.jar. There, fileManager.getLocation(PLATFORM_CLASS_PATH) returns
    // null, because the relevant classes are in modules inside
    // fileManager.getLocation(SYSTEM_MODULES).
    File rtJar = new File(System.getProperty("java.home") + "/lib/rt.jar");
    if (rtJar.exists()) {
      List<File> bootClassPath = ImmutableList.<File>builder()
          .add(rtJar)
          .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH))
          .build();
      fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath);
    }
    Iterable<? extends JavaFileObject> sourceFileObjects =
        fileManager.getJavaFileObjectsFromFiles(sources);
    String outputDir = tmp.getRoot().toString();
    ImmutableList<String> options =
        ImmutableList.of("-d", outputDir, "-s", outputDir, "-source", version, "-target", version);
    JavaCompiler.CompilationTask task =
        compiler.getTask(null, fileManager, null, options, null, sourceFileObjects);
    // Explicitly supply an empty list of extensions for AutoValueProcessor, because otherwise this
    // test will pick up a test one and get confused.
    AutoValueProcessor autoValueProcessor = new AutoValueProcessor(ImmutableList.of());
    ImmutableList<? extends Processor> processors =
        ImmutableList.of(
            autoValueProcessor, new AutoOneOfProcessor(), new AutoAnnotationProcessor());
    task.setProcessors(processors);
    assertWithMessage("Compilation should succeed").that(task.call()).isTrue();
  }

  private static ImmutableSet<File> filesUnderDirectory(File dir, Predicate<File> predicate)
      throws IOException {
    assertWithMessage(dir.toString()).that(dir.isDirectory()).isTrue();
    return Files.walk(dir.toPath()).map(Path::toFile).filter(predicate).collect(toImmutableSet());
  }
}

@@ -611,24 +611,95 @@
 uto.value;%0A%0A
+import static com.google.common.base.StandardSystemProperty.JAVA_HOME;%0A
 import stati
@@ -751,24 +751,24 @@
 mutableSet;%0A
-
 import stati
@@ -1338,16 +1338,48 @@
 dicate;%0A
+import java.util.stream.Stream;%0A
 import j
@@ -1743,16 +1743,82 @@
 yFolder;
+%0Aimport org.junit.runner.RunWith;%0Aimport org.junit.runners.JUnit4;
 %0A%0A/**%0A *
@@ -2033,16 +2033,39 @@
 rt.%0A */%0A
+@RunWith(JUnit4.class)%0A
 public c
@@ -4292,38 +4292,24 @@
 ile(
-System.getProperty(%22java.home%22
+JAVA_HOME.value(
 ) + 
@@ -5780,22 +5780,41 @@
 ();%0A    
-return
+try (Stream%3CPath%3E paths =
  Files.w
@@ -5830,16 +5830,38 @@
 oPath())
+) %7B%0A      return paths
 .map(Pat
@@ -5912,18 +5912,24 @@
 ableSet());%0A
+    %7D%0A
   %7D%0A%7D%0A

Use try-with-resources for Files.walk. Otherwise the Stream is not closed correctly.