001/*
002 * Copyright (c) 2007-2022 The Cascading Authors. All Rights Reserved.
003 *
004 * Project and contact information: https://cascading.wensel.net/
005 *
006 * This file is part of the Cascading project.
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020
021package cascading.nested.core;
022
023import cascading.flow.FlowProcess;
024import cascading.operation.Function;
025import cascading.operation.FunctionCall;
026import cascading.operation.OperationException;
027import cascading.tuple.Fields;
028import cascading.tuple.Tuple;
029import heretical.pointer.path.NestedPointer;
030import heretical.pointer.path.NestedPointerCompiler;
031
032/**
033 * Class NestedGetAllFunction is the base class for {@link Function} implementations that want to simply retrieve
034 * a collection of values in nested object, and then transform each element node into a tuple with declared fields.
035 * <p>
036 * The {@code stringRootPointer} value must point to a container node with one or more elements. Each element
037 * will have values extracted and a new tuple will be emitted for each element.
038 * <p>
039 * For every field named in the fieldDeclaration {@link Fields} argument, there must be a corresponding
040 * {@code stringPointer} value.
041 * <p>
042 * If {@code failOnMissingNode} is {@code true} and the root pointer is empty or the field pointer returns a
043 * {@code null} value, the operation will fail.
044 * <p>
045 * If the fieldDeclaration Fields instance declares a type information, the {@code nestedCoercibleType} will be used
046 * to coerce any referenced child value to the expected field type.
047 */
048public class NestedGetAllFunction<Node, Result> extends NestedGetFunction<Node, Result>
049  {
050  protected NestedPointer<Node, Result> rootPointer;
051
052  /**
053   * Constructor NestedGetAllFunction creates a new NestedGetFunction instance.
054   *
055   * @param nestedCoercibleType of NestedCoercibleType
056   * @param stringRootPointer   of type String
057   * @param fieldDeclaration    of type Fields
058   * @param failOnMissingNode   of type boolean
059   * @param stringPointers      of type String...
060   */
061  public NestedGetAllFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, String stringRootPointer, Fields fieldDeclaration, boolean failOnMissingNode, String... stringPointers )
062    {
063    super( nestedCoercibleType, fieldDeclaration, failOnMissingNode, stringPointers );
064
065    NestedPointerCompiler<Node, Result> compiler = getNestedPointerCompiler();
066
067    this.rootPointer = compiler.nested( stringRootPointer );
068    }
069
070  @Override
071  public void operate( FlowProcess flowProcess, FunctionCall<Tuple> functionCall )
072    {
073    Tuple resultTuple = functionCall.getContext();
074    Node argument = (Node) functionCall.getArguments().getObject( 0, getCoercibleType() );
075
076    Result result = rootPointer.allAt( argument );
077
078    if( failOnMissingNode && getNestedPointerCompiler().size( result ) == 0 )
079      throw new OperationException( "nodes missing from json node tree: " + rootPointer );
080
081    Iterable<Node> iterable = getNestedPointerCompiler().iterable( result );
082
083    for( Node node : iterable )
084      {
085      extractResult( resultTuple, node );
086
087      functionCall.getOutputCollector().add( resultTuple );
088      }
089    }
090  }