content: /**
   * 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 org.apache.aurora.common.collections;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Iterator;
  import java.util.List;
  
  import com.google.common.cache.CacheBuilder;
  import com.google.common.cache.CacheLoader;
  import com.google.common.cache.LoadingCache;
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.Iterables;
  import com.google.common.collect.Lists;
  
  /**
   * Utility functions for dealing with iterables.
   *
   * @author William Farner
   */
  public final class Iterables2 {
  
    private Iterables2() {
      // Utility class.
    }
  
    /**
     * An iterator that zips multiple iterables into a single list iterator, filling missing values
     * with a provided default.
     *
     * @param <T> The value type for the iterator.
     */
    private static class ZippingIterator<T> implements Iterator<List<T>> {
  
      private final Iterable<Iterable<T>> iterables;
      private final T defaultValue;
  
      private List<Iterator<T>> iterators = null;
      private final LoadingCache<Iterator<T>, Boolean> overflowing = CacheBuilder.newBuilder().build(
          new CacheLoader<Iterator<T>, Boolean>() {
            @Override public Boolean load(Iterator<T> iterator) {
              return false;
            }
          });
  
      ZippingIterator(Iterable<Iterable<T>> iterables, T defaultValue) {
        this.iterables = iterables;
        this.defaultValue = defaultValue;
      }
  
      private void init() {
        if (iterators == null) {
          // Iterables -> Iterators.
          iterators = ImmutableList.copyOf(Iterables.transform(iterables,
              Iterable::iterator));
        }
      }
  
      @Override public boolean hasNext() {
        init();
        for (Iterator<T> it : iterators) {
          if (it.hasNext()) {
            return true;
          }
        }
  
        return false;
      }
  
      @Override public List<T> next() {
        init();
        List<T> data = new ArrayList<T>(iterators.size());
  
        for (Iterator<T> it : iterators) {
          if (it.hasNext()) {
            data.add(it.next());
          } else {
            overflowing.asMap().put(it, true);
            data.add(defaultValue);
          }
        }
  
        return data;
      }
  
      @Override public void remove() {
        init();
        for (Iterator<T> it : iterators) {
          if (!overflowing.getUnchecked(it)) {
            it.remove();
          }
        }
      }
  
      @Override public String toString() {
        return Lists.newArrayList(iterables).toString();
      }
    }
  
    /**
     * Zips multiple iterables into one iterable that will return iterators to step over
     * rows of the input iterators (columns).  The order of the returned values within each row will
     * match the ordering of the input iterables. The iterators will iterate the length of the longest
     * input iterable, filling other columns with {@code defaultValue}.
     * The returned iterator is lazy, in that 'rows' are constructed as they are requested.
     *
     * @param iterables Columns to iterate over.
     * @param defaultValue Default fill value when an input iterable is exhausted.
     * @param <T> Type of value being iterated over.
     * @return An iterator that iterates over rows of the input iterables.
     */
    public static <T> Iterable<List<T>> zip(final Iterable<Iterable<T>> iterables,
        final T defaultValue) {
  
      return () -> new ZippingIterator<T>(iterables, defaultValue);
    }
  
    /**
     * Varargs convenience function to call {@link #zip(Iterable, Object)}.
     *
     * @param defaultValue Default fill value when an input iterable is exhausted.
     * @param iterables Columns to iterate over.
     * @param <T> Type of value being iterated over.
     * @return An iterator that iterates over rows of the input iterables.
     */
    public static <T> Iterable<List<T>> zip(T defaultValue, Iterable<T>... iterables) {
      return zip(Arrays.asList(iterables), defaultValue);
    }
  }
  