/*
 * Copyright 2015-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.rules.macros;

import com.facebook.buck.jvm.java.HasClasspathEntries;
import com.facebook.buck.jvm.java.JavaLibrary;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.MacroException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePaths;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;

import java.io.File;
import java.nio.file.Path;
import java.util.Objects;

import javax.annotation.Nullable;

/**
 * Used to expand the macro {@literal $(classpath //some:target)} to the transitive classpath of
 * that target, expanding all paths to be absolute.
 */
public class ClasspathMacroExpander
    extends BuildTargetMacroExpander
    implements MacroExpanderWithCustomFileOutput {

  private HasClasspathEntries getHasClasspathEntries(BuildRule rule)
      throws MacroException {
    if (!(rule instanceof HasClasspathEntries)) {
      throw new MacroException(
          String.format(
              "%s used in classpath macro does not correspond to a rule with a java classpath",
              rule.getBuildTarget()));
    }
    return (HasClasspathEntries) rule;
  }

  @Override
  public ImmutableList<BuildRule> extractBuildTimeDepsFrom(
      BuildTarget target,
      CellPathResolver cellNames,
      BuildRuleResolver resolver,
      BuildTarget input)
      throws MacroException {
    return ImmutableList.copyOf(
        getHasClasspathEntries(resolve(resolver, input)).getTransitiveClasspathDeps());
  }

  @Override
  public String expandForFile(
      BuildTarget target,
      CellPathResolver cellNames,
      BuildRuleResolver resolver,
      ImmutableList<String> input) throws MacroException {
    // javac is the canonical reader of classpaths, and its code for reading classpaths from
    // files is a little weird:
    // http://hg.openjdk.java.net/jdk7/jdk7/langtools/file/ce654f4ecfd8/src/share/classes/com/sun/tools/javac/main/CommandLine.java#l74
    // The # characters that might be present in classpaths due to flavoring would be read as
    // comments. As a simple workaround, we quote the entire classpath.
    return String.format("'%s'", expand(target, cellNames, resolver, input));
  }

  @Override
  protected String expand(SourcePathResolver resolver, BuildRule rule)
      throws MacroException {
    return Joiner.on(File.pathSeparator).join(
        FluentIterable.from(getHasClasspathEntries(rule).getTransitiveClasspathDeps())
            .transform(
                new Function<JavaLibrary, Path>() {
                  @Nullable
                  @Override
                  public Path apply(JavaLibrary input) {
                    return input.getPathToOutput() == null
                        ? null
                        : input.getProjectFilesystem()
                              .resolve(input.getPathToOutput());
                  }
                })
            .filter(Objects::nonNull)
            .transform(Object::toString)
            .toSortedSet(Ordering.natural()));
  }

  @Override
  public Object extractRuleKeyAppendablesFrom(
      BuildTarget target,
      CellPathResolver cellNames,
      BuildRuleResolver resolver,
      BuildTarget input)
      throws MacroException {
    return FluentIterable.from(
            getHasClasspathEntries(resolve(resolver, input))
                .getTransitiveClasspathDeps())
        .filter(
            input1 -> input1.getPathToOutput() != null)
        .transform(SourcePaths.getToBuildTargetSourcePath())
        .toSortedSet(Ordering.natural());
  }

}

@@ -700,55 +700,8 @@
 es;%0A
-import com.facebook.buck.jvm.java.JavaLibrary;%0A
 impo
@@ -1052,108 +1052,29 @@
 mon.
-base.Function;%0Aimport com.google.common.base.Joiner;%0Aimport com.google.common.collect.FluentIterable
+collect.ImmutableList
 ;%0Aim
@@ -1105,35 +1105,40 @@
 ollect.Immutable
-Lis
+SortedSe
 t;%0Aimport com.go
@@ -1205,83 +1205,32 @@
 ava.
-nio.file.Path;%0Aimport java.util.Objects;%0A%0Aimport javax.annotation.Nullable;
+util.stream.Collectors;%0A
 %0A%0A/*
@@ -3081,72 +3081,8 @@
 urn 
-Joiner.on(File.pathSeparator).join(%0A        FluentIterable.from(
 getH
@@ -3105,16 +3105,25 @@
 es(rule)
+%0A        
 .getTran
@@ -3135,33 +3135,32 @@
 eClasspathDeps()
-)
 %0A            .tr
@@ -3156,221 +3156,44 @@
     
-    .transform(%0A                new Function%3CJavaLibrary, Path%3E() %7B%0A                  @Nullable%0A                  @Override%0A                  public Path apply(JavaLibrary input) %7B%0A                    return input
+.stream()%0A        .filter(dep -%3E dep
 .get
@@ -3211,132 +3211,63 @@
 t() 
-=
+!
 = null
-%0A                        ? null%0A                        : input.getProjectFilesystem()%0A                              
+)%0A        .map(dep -%3E dep.getProjectFilesystem()
 .res
@@ -3271,21 +3271,19 @@
 resolve(
-input
+dep
 .getPath
@@ -3297,171 +3297,131 @@
 t())
-;%0A                  %7D%0A                %7D)%0A            .filter
+)%0A        .map
 (Object
-s
 ::
-nonNull)%0A            .transform(Object::toString)%0A            .toSortedSet(Ordering.natural()
+toString)%0A        .sorted(Ordering.natural())%0A        .collect(Collectors.joining(File.pathSeparator
 ));%0A
@@ -3648,41 +3648,34 @@
 urn 
-FluentIterable.from(%0A            
+ImmutableSortedSet.copyOf(
 getH
@@ -3727,24 +3727,16 @@
         
-       
- 
 .getTran
@@ -3756,17 +3756,16 @@
 thDeps()
-)
 %0A       
@@ -3766,23 +3766,24 @@
        .
-filter(
+stream()
 %0A       
@@ -3787,28 +3787,26 @@
     
-    input1 -%3E input1
+.filter(dep -%3E dep
 .get
@@ -3842,18 +3842,19 @@
    .
-transform(
+map(dep -%3E 
 Sour
@@ -3893,48 +3893,56 @@
 th()
-)%0A        .toSortedSet(Ordering.natural(
+.apply(dep))%0A        .collect(Collectors.toSet()
 ));%0A

Convert classpath macro from iterables to streams