package org.inferred.freebuilder.processor.util.feature;

import com.google.common.base.Optional;

import org.inferred.freebuilder.processor.util.ParameterizedType;
import org.inferred.freebuilder.processor.util.QualifiedName;
import org.inferred.freebuilder.processor.util.SourceBuilder;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;

/**
 * Types in the java.util.function package, if available. Defaults to {@link #UNAVAILABLE} in tests.
 */
public enum FunctionPackage implements Feature<FunctionPackage> {

  AVAILABLE, UNAVAILABLE;

  /**
   * Constant to pass to {@link SourceBuilder#feature(FeatureType)} to get the current status of
   * {@link FunctionPackage}.
   */
  public static final FeatureType<FunctionPackage> FUNCTION_PACKAGE =
      new FeatureType<FunctionPackage>() {

        @Override
        protected FunctionPackage testDefault() {
          return UNAVAILABLE;
        }

        @Override
        protected FunctionPackage forEnvironment(ProcessingEnvironment env) {
          TypeElement element = env.getElementUtils()
              .getTypeElement(UNARY_OPERATOR.getQualifiedName().toString());
          return (element != null) ? AVAILABLE : UNAVAILABLE;
        }
      };

  private static final ParameterizedType CONSUMER =
      QualifiedName.of("java.util.function", "Consumer").withParameters("T");
  private static final ParameterizedType BI_CONSUMER =
      QualifiedName.of("java.util.function", "BiConsumer").withParameters("T", "U");
  private static final ParameterizedType UNARY_OPERATOR =
      QualifiedName.of("java.util.function", "UnaryOperator").withParameters("T");

  /**
   * Parameterized type for {@code java.util.function.Consumer<T>}, if available.
   */
  public Optional<ParameterizedType> consumer() {
    return ifAvailable(CONSUMER);
  }

  /**
   * Parameterized type for {@code java.util.function.BiConsumer<T>}, if available.
   */
  public Optional<ParameterizedType> biConsumer() {
    return ifAvailable(BI_CONSUMER);
  }

  /**
   * Parameterized type for {@code java.util.function.UnaryOperator<T>}, if available.
   */
  public Optional<ParameterizedType> unaryOperator() {
    return ifAvailable(UNARY_OPERATOR);
  }

  private <T> Optional<T> ifAvailable(T value) {
    return (this == AVAILABLE) ? Optional.of(value) : Optional.<T>absent();
  }
}

@@ -220,16 +220,72 @@
 edName;%0A
+import org.inferred.freebuilder.processor.util.Shading;%0A
 import o
@@ -425,27 +425,60 @@
 del.
-element.Type
+SourceVersion;%0Aimport javax.lang.model.util.
 Element
+s
 ;%0A%0A/
@@ -1151,210 +1151,565 @@
     
-TypeElement element = env.getElementUtils()%0A              .getTypeElement(UNARY_OPERATOR.getQualifiedName().toString());%0A          return (element != null) ? AVAILABLE : UNAVAILABLE;%0A        %7D%0A      %7D;%0A
+if (runningInEclipse()) %7B%0A            // Eclipse is bugged: sourceVersion will never be %3E 7.%0A            // Work around this by checking for the presence of java.util.function.Consumer instead.%0A            return hasType(env.getElementUtils(), CONSUMER) ? AVAILABLE : UNAVAILABLE;%0A          %7D else %7B%0A            return hasLambdas(env.getSourceVersion()) ? AVAILABLE : UNAVAILABLE;%0A          %7D%0A        %7D%0A      %7D;%0A%0A  private static final String ECLIPSE_DISPATCHER =%0A      Shading.unshadedName(%22org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher%22);
 %0A  p
@@ -2689,16 +2689,763 @@
 );%0A  %7D%0A%0A
+  private static final boolean runningInEclipse() %7B%0A    // If we're running in Eclipse, we will have been invoked by the Eclipse round dispatcher.%0A    Throwable t = new Throwable();%0A    t.fillInStackTrace();%0A    for (StackTraceElement method : t.getStackTrace()) %7B%0A      if (method.getClassName().equals(ECLIPSE_DISPATCHER)) %7B%0A        return true;%0A      %7D else if (!method.getClassName().startsWith(%22org.inferred%22)) %7B%0A        return false;%0A      %7D%0A    %7D%0A    return false;%0A  %7D%0A%0A  private static final boolean hasLambdas(SourceVersion version) %7B%0A    return version.ordinal() %3E= 8;%0A  %7D%0A%0A  private static boolean hasType(Elements elements, ParameterizedType type) %7B%0A    return elements.getTypeElement(type.getQualifiedName().toString()) != null;%0A  %7D%0A%0A
   privat

Use source level in FunctionPackage