// Path: agera/src/main/java/com/google/android/agera/Common.java
// static final NullOperator NULL_OPERATOR = new NullOperator();
// 
// Path: agera/src/main/java/com/google/android/agera/Common.java
// static final StaticCondicate TRUE_CONDICATE = new StaticCondicate(true);
// 
// Path: agera/src/main/java/com/google/android/agera/FunctionCompilerStates.java
// interface FItem<TPrev, TFrom> extends FBase<TPrev, TFrom> {
//   /**
//    * Adds a {@link Function} to the behavior chain to unpack an item into a {@link List}, allowing
//    * list behaviors to be used from this point on.
//    *
//    * @param function the unpack function
//    */
//   @NonNull
//   <TTo> FList<TTo, List<TTo>, TFrom> unpack(@NonNull Function<? super TPrev, List<TTo>> function);
// }
// 
// Path: agera/src/main/java/com/google/android/agera/FunctionCompilerStates.java
// interface FList<TPrev, TPrevList, TFrom> extends FBase<TPrevList, TFrom> {
// 
//   /**
//    * Adds a {@link Function} to the behavior chain to change the entire list to a new list.
//    *
//    * <p>The {@code morph} directive is functionally equivalent to {@code apply}, which treats the
//    * input list as a single item. But {@code morph} is aware of the list-typed output and allows
//    * list behaviors to follow immediately. Since the only difference between {@link #apply} and
//    * {@code morph} is the next state of the compiler, {@code thenMorph} does not exist since
//    * {@link #thenApply} can be used in its place.
//    *
//    * @param function the function to apply to the list
//    */
//   @NonNull
//   <TTo> FList<TTo, List<TTo>, TFrom> morph(@NonNull Function<List<TPrev>, List<TTo>> function);
// 
//   /**
//    * Adds a {@link Function} to the behavior chain to map each item into a new type.
//    *
//    * @param function the function to apply to each item to create a new list
//    */
//   @NonNull
//   <TTo> FList<TTo, List<TTo>, TFrom> map(@NonNull Function<TPrev, TTo> function);
// 
//   /**
//    * Adds a {@link Function} to the end of the behavior chain to map each item into a new type.
//    *
//    * @param function the function to apply to each item to create a new list
//    */
//   @NonNull
//   <TTo> Function<TFrom, List<TTo>> thenMap(@NonNull Function<? super TPrev, TTo> function);
// 
//   /**
//    * Adds a {@link Predicate} to the behavior chain to filter out items.
//    *
//    * @param filter the predicate to filter by
//    */
//   @NonNull
//   FList<TPrev, TPrevList, TFrom> filter(@NonNull Predicate<? super TPrev> filter);
// 
//   /**
//    * Adds a max number of item limit to the behavior chain.
//    *
//    * @param limit the max number of items the list is limited to
//    */
//   @NonNull
//   FList<TPrev, TPrevList, TFrom> limit(int limit);
// 
//   /**
//    * Adds a {@link Comparator} to the behavior chain to sort the items.
//    *
//    * @param comparator the comparator to sort the items
//    */
//   @NonNull
//   FList<TPrev, TPrevList, TFrom> sort(@NonNull Comparator<TPrev> comparator);
// 
//   /**
//    * Adds a {@link Predicate} to the end of the behavior chain to filter out items.
//    *
//    * @param filter the predicate to filter by
//    */
//   @NonNull
//   Function<TFrom, TPrevList> thenFilter(@NonNull Predicate<? super TPrev> filter);
// 
//   /**
//    * Adds a max number of item limit to the end of the behavior chain.
//    *
//    * @param limit the max number of items the list is limited to
//    */
//   @NonNull
//   Function<TFrom, TPrevList> thenLimit(int limit);
// 
//   /**
//    * Adds a {@link Comparator} to the behavior chain to sort the items.
//    *
//    * @param comparator the comparator to sort the items
//    */
//   @NonNull
//   Function<TFrom, TPrevList> thenSort(@NonNull Comparator<TPrev> comparator);
// }


import static com.google.android.agera.Common.NULL_OPERATOR;
import static com.google.android.agera.Common.TRUE_CONDICATE;
import static com.google.android.agera.Preconditions.checkNotNull;
import static java.util.Collections.emptyList;
import android.support.annotation.NonNull;
import com.google.android.agera.FunctionCompilerStates.FItem;
import com.google.android.agera.FunctionCompilerStates.FList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/*
 * Copyright 2015 Google Inc. 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.android.agera;



@SuppressWarnings({"unchecked, rawtypes"})
final class FunctionCompiler implements FList, FItem {
  private static final ThreadLocal<FunctionCompiler> compilers = new ThreadLocal<>();

  @NonNull
  static FunctionCompiler functionCompiler() {
    FunctionCompiler compiler = compilers.get();
    if (compiler == null) {
      compiler = new FunctionCompiler();
    } else {
      // Remove compiler from the ThreadLocal to prevent reuse in the middle of a compilation.
      // recycle(), called by compile(), will return the compiler here. ThreadLocal.set(null) keeps
      // the entry (with a null value) whereas remove() removes the entry; because we expect the
      // return of the compiler, don't use the heavier remove().
      compilers.set(null);
    }
    return compiler;
  }

  private static void recycle(@NonNull final FunctionCompiler compiler) {
    compiler.functions.clear();
    compilers.set(compiler);
  }

  @NonNull
  private final List<Function> functions;

  FunctionCompiler() {
    this.functions = new ArrayList<>();
  }

  private void addFunction(@NonNull final Function function) {
// Path: agera/src/main/java/com/google/android/agera/Common.java
// static final NullOperator NULL_OPERATOR = new NullOperator();
// 
// Path: agera/src/main/java/com/google/android/agera/Common.java
// static final StaticCondicate TRUE_CONDICATE = new StaticCondicate(true);
// 
// Path: agera/src/main/java/com/google/android/agera/FunctionCompilerStates.java
// interface FItem<TPrev, TFrom> extends FBase<TPrev, TFrom> {
//   /**
//    * Adds a {@link Function} to the behavior chain to unpack an item into a {@link List}, allowing
//    * list behaviors to be used from this point on.
//    *
//    * @param function the unpack function
//    */
//   @NonNull
//   <TTo> FList<TTo, List<TTo>, TFrom> unpack(@NonNull Function<? super TPrev, List<TTo>> function);
// }
// 
// Path: agera/src/main/java/com/google/android/agera/FunctionCompilerStates.java
// interface FList<TPrev, TPrevList, TFrom> extends FBase<TPrevList, TFrom> {
// 
//   /**
//    * Adds a {@link Function} to the behavior chain to change the entire list to a new list.
//    *
//    * <p>The {@code morph} directive is functionally equivalent to {@code apply}, which treats the
//    * input list as a single item. But {@code morph} is aware of the list-typed output and allows
//    * list behaviors to follow immediately. Since the only difference between {@link #apply} and
//    * {@code morph} is the next state of the compiler, {@code thenMorph} does not exist since
//    * {@link #thenApply} can be used in its place.
//    *
//    * @param function the function to apply to the list
//    */
//   @NonNull
//   <TTo> FList<TTo, List<TTo>, TFrom> morph(@NonNull Function<List<TPrev>, List<TTo>> function);
// 
//   /**
//    * Adds a {@link Function} to the behavior chain to map each item into a new type.
//    *
//    * @param function the function to apply to each item to create a new list
//    */
//   @NonNull
//   <TTo> FList<TTo, List<TTo>, TFrom> map(@NonNull Function<TPrev, TTo> function);
// 
//   /**
//    * Adds a {@link Function} to the end of the behavior chain to map each item into a new type.
//    *
//    * @param function the function to apply to each item to create a new list
//    */
//   @NonNull
//   <TTo> Function<TFrom, List<TTo>> thenMap(@NonNull Function<? super TPrev, TTo> function);
// 
//   /**
//    * Adds a {@link Predicate} to the behavior chain to filter out items.
//    *
//    * @param filter the predicate to filter by
//    */
//   @NonNull
//   FList<TPrev, TPrevList, TFrom> filter(@NonNull Predicate<? super TPrev> filter);
// 
//   /**
//    * Adds a max number of item limit to the behavior chain.
//    *
//    * @param limit the max number of items the list is limited to
//    */
//   @NonNull
//   FList<TPrev, TPrevList, TFrom> limit(int limit);
// 
//   /**
//    * Adds a {@link Comparator} to the behavior chain to sort the items.
//    *
//    * @param comparator the comparator to sort the items
//    */
//   @NonNull
//   FList<TPrev, TPrevList, TFrom> sort(@NonNull Comparator<TPrev> comparator);
// 
//   /**
//    * Adds a {@link Predicate} to the end of the behavior chain to filter out items.
//    *
//    * @param filter the predicate to filter by
//    */
//   @NonNull
//   Function<TFrom, TPrevList> thenFilter(@NonNull Predicate<? super TPrev> filter);
// 
//   /**
//    * Adds a max number of item limit to the end of the behavior chain.
//    *
//    * @param limit the max number of items the list is limited to
//    */
//   @NonNull
//   Function<TFrom, TPrevList> thenLimit(int limit);
// 
//   /**
//    * Adds a {@link Comparator} to the behavior chain to sort the items.
//    *
//    * @param comparator the comparator to sort the items
//    */
//   @NonNull
//   Function<TFrom, TPrevList> thenSort(@NonNull Comparator<TPrev> comparator);
// }


// Path: agera/src/main/java/com/google/android/agera/FunctionCompiler.java
import static com.google.android.agera.Common.NULL_OPERATOR;
import static com.google.android.agera.Common.TRUE_CONDICATE;
import static com.google.android.agera.Preconditions.checkNotNull;
import static java.util.Collections.emptyList;
import android.support.annotation.NonNull;
import com.google.android.agera.FunctionCompilerStates.FItem;
import com.google.android.agera.FunctionCompilerStates.FList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/*
 * Copyright 2015 Google Inc. 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.android.agera;



@SuppressWarnings({"unchecked, rawtypes"})
final class FunctionCompiler implements FList, FItem {
  private static final ThreadLocal<FunctionCompiler> compilers = new ThreadLocal<>();

  @NonNull
  static FunctionCompiler functionCompiler() {
    FunctionCompiler compiler = compilers.get();
    if (compiler == null) {
      compiler = new FunctionCompiler();
    } else {
      // Remove compiler from the ThreadLocal to prevent reuse in the middle of a compilation.
      // recycle(), called by compile(), will return the compiler here. ThreadLocal.set(null) keeps
      // the entry (with a null value) whereas remove() removes the entry; because we expect the
      // return of the compiler, don't use the heavier remove().
      compilers.set(null);
    }
    return compiler;
  }

  private static void recycle(@NonNull final FunctionCompiler compiler) {
    compiler.functions.clear();
    compilers.set(compiler);
  }

  @NonNull
  private final List<Function> functions;

  FunctionCompiler() {
    this.functions = new ArrayList<>();
  }

  private void addFunction(@NonNull final Function function) {
    if (function != NULL_OPERATOR) {