/* 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.parquet.filter2.recordlevel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.parquet.hadoop.metadata.ColumnPath;
import org.apache.parquet.filter2.recordlevel.IncrementallyUpdatedFilterPredicate.ValueInspector;
import org.apache.parquet.io.PrimitiveColumnIO;
import org.apache.parquet.io.api.GroupConverter;
import org.apache.parquet.io.api.RecordMaterializer;

import static org.apache.parquet.Preconditions.checkNotNull;

/**
 * A pass-through proxy for a {@link RecordMaterializer} that updates a {@link IncrementallyUpdatedFilterPredicate}
 * as it receives concrete values for the current record. If, after the record assembly signals that
 * there are no more values, the predicate indicates that this record should be dropped, {@link #getCurrentRecord()}
 * returns null to signal that this record is being skipped.
 * Otherwise, the record is retrieved from the delegate.
 */
public class FilteringRecordMaterializer<T> extends RecordMaterializer<T> {
  // the real record materializer
  private final RecordMaterializer<T> delegate;

  // the proxied root converter
  private final FilteringGroupConverter rootConverter;

  // the predicate
  private final IncrementallyUpdatedFilterPredicate filterPredicate;

  public FilteringRecordMaterializer(
      RecordMaterializer<T> delegate,
      List<PrimitiveColumnIO> columnIOs,
      Map<ColumnPath, List<ValueInspector>> valueInspectorsByColumn,
      IncrementallyUpdatedFilterPredicate filterPredicate) {

    checkNotNull(columnIOs, "columnIOs");
    checkNotNull(valueInspectorsByColumn, "valueInspectorsByColumn");
    this.filterPredicate = checkNotNull(filterPredicate, "filterPredicate");
    this.delegate = checkNotNull(delegate, "delegate");

    // keep track of which path of indices leads to which primitive column
    Map<List<Integer>, PrimitiveColumnIO> columnIOsByIndexFieldPath = new HashMap<List<Integer>, PrimitiveColumnIO>();

    for (PrimitiveColumnIO c : columnIOs) {
      columnIOsByIndexFieldPath.put(getIndexFieldPathList(c), c);
    }

    // create a proxy for the delegate's root converter
    this.rootConverter = new FilteringGroupConverter(
        delegate.getRootConverter(), Collections.<Integer>emptyList(), valueInspectorsByColumn, columnIOsByIndexFieldPath);
  }

  public static List<Integer> getIndexFieldPathList(PrimitiveColumnIO c) {
    return intArrayToList(c.getIndexFieldPath());
  }

  public static List<Integer> intArrayToList(int[] arr) {
    List<Integer> list = new ArrayList<Integer>(arr.length);
    for (int i : arr) {
      list.add(i);
    }
    return list;
  }



  @Override
  public T getCurrentRecord() {

    // find out if the predicate thinks we should keep this record
    boolean keep = IncrementallyUpdatedFilterPredicateEvaluator.evaluate(filterPredicate);

    // reset the stateful predicate no matter what
    IncrementallyUpdatedFilterPredicateResetter.reset(filterPredicate);

    if (keep) {
      return delegate.getCurrentRecord();
    } else {
      // signals a skip
      return null;
    }
  }

  @Override
  public void skipCurrentRecord() {
    delegate.skipCurrentRecord();
  }

  @Override
  public GroupConverter getRootConverter() {
    return rootConverter;
  }
}

@@ -875,20 +875,17 @@
 il.Array
-List
+s
 ;%0Aimport
@@ -978,16 +978,52 @@
 til.Map;
+%0Aimport java.util.stream.Collectors;
 %0A%0Aimport
@@ -2832,40 +2832,8 @@
 Map%3C
-List%3CInteger%3E, PrimitiveColumnIO
 %3E();
@@ -2874,24 +2874,147 @@
 olumnIOs) %7B%0A
+      List%3CInteger%3E indexFieldPath = Arrays.stream(c.getIndexFieldPath())%0A          .boxed().collect(Collectors.toList());%0A
       column
@@ -3037,20 +3037,17 @@
 ath.put(
-getI
+i
 ndexFiel
@@ -3055,15 +3055,8 @@
 Path
-List(c)
 , c)
@@ -3223,25 +3223,16 @@
 ections.
-%3CInteger%3E
 emptyLis
@@ -3235,16 +3235,24 @@
 yList(),
+%0A       
  valueIn
@@ -3306,330 +3306,8 @@
  %7D%0A%0A
-  public static List%3CInteger%3E getIndexFieldPathList(PrimitiveColumnIO c) %7B%0A    return intArrayToList(c.getIndexFieldPath());%0A  %7D%0A%0A  public static List%3CInteger%3E intArrayToList(int%5B%5D arr) %7B%0A    List%3CInteger%3E list = new ArrayList%3CInteger%3E(arr.length);%0A    for (int i : arr) %7B%0A      list.add(i);%0A    %7D%0A    return list;%0A  %7D%0A%0A%0A%0A
   @O
@@ -3638,22 +3638,33 @@
     
-if (keep) %7B%0A  
+// null - signals a skip%0A
     
@@ -3669,16 +3669,25 @@
   return
+ (keep) ?
  delegat
@@ -3710,71 +3710,16 @@
 rd()
-;%0A    %7D else %7B%0A      // signals a skip%0A      return null;%0A    %7D
+ : null;
 %0A  %7D

Use JDK 8 Facilities to Simplify FilteringRecordMaterializer (#719)