001/*
002 * Copyright (c) 2016-2017 Chris K Wensel. All Rights Reserved.
003 *
004 * Project and contact information: http://www.cascading.org/
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 java.util.Map;
024
025import cascading.flow.FlowProcess;
026import cascading.operation.Function;
027import cascading.operation.FunctionCall;
028import cascading.operation.OperationCall;
029import cascading.tuple.Fields;
030import cascading.tuple.Tuple;
031import cascading.tuple.TupleEntry;
032import cascading.tuple.Tuples;
033import heretical.pointer.operation.Copier;
034
035/**
036 * Class NestedBaseCopyFunction is the base class for {@link Function} implementations that rely on the
037 * {@link CopySpec} class when declaring transformations on nested object types.
038 * <p>
039 * Specifically, {@code *CopyAsFunction} and {@code *CopyIntoFunction} classes create or update (respectively)
040 * nested object types from a Function argument where the field value is a nest object type.
041 * <p>
042 * In the case of a {@code *CopyIntoFunction} the last argument in the {@code arguments} {@link TupleEntry} will be
043 * the object the CopySpec copies values into.
044 * <p>
045 * In the case of a {@code *CopyAsFunction} a new root object will be created for the CopySpec to copy values into.
046 * <p>
047 * Note the arguments TupleEntry will be passed to any {@link Transform} instances that are resettable
048 * {@link Transform#isResettable()} allowing for parameterized transformations on child values as they are copied
049 * to the new location.
050 * <p>
051 * In the case of JSON objects, a single JSON object is selected as an argument so that values contained in that object
052 * can be copied into the new object.
053 * <p>
054 * For selecting the values from multiple existing field values in order to create a new object or update an existing one
055 * see {@link NestedBaseBuildFunction} sub-classes.
056 *
057 * @see CopySpec
058 */
059public abstract class NestedBaseCopyFunction<Node, Result> extends NestedSpecBaseOperation<Node, Result, NestedBaseCopyFunction.Context> implements Function<NestedBaseCopyFunction.Context>
060  {
061  protected static class Context
062    {
063    public Tuple result;
064    public Fields fields;
065
066    public Context( Tuple result, Fields fields )
067      {
068      this.result = result;
069      this.fields = fields;
070      }
071    }
072
073  protected Copier<Node, Result> copier;
074
075  public NestedBaseCopyFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, CopySpec... copySpecs )
076    {
077    super( nestedCoercibleType, fieldDeclaration );
078    this.copier = new Copier<>( getNestedPointerCompiler(), copySpecs );
079
080    if( fieldDeclaration.isDefined() && fieldDeclaration.size() != 1 )
081      throw new IllegalArgumentException( "can only return a single field" );
082    }
083
084  @Override
085  public void prepare( FlowProcess flowProcess, OperationCall<Context> operationCall )
086    {
087    operationCall.setContext( new Context( Tuple.size( 1 ), operationCall.getArgumentFields().subtract( Fields.FIRST ) ) );
088    }
089
090  @Override
091  public void operate( FlowProcess flowProcess, FunctionCall<Context> functionCall )
092    {
093    TupleEntry arguments = functionCall.getArguments();
094    Node fromNode = (Node) arguments.getObject( 0, getCoercibleType() );
095
096    if( arguments.size() > ( isInto() ? 2 : 1 ) )
097      resetTransforms( arguments, functionCall.getContext().fields );
098
099    Node resultNode = getResultNode( functionCall );
100
101    copier.copy( fromNode, resultNode );
102
103    Context context = functionCall.getContext();
104
105    context.result.set( 0, resultNode );
106
107    functionCall.getOutputCollector().add( context.result );
108    }
109
110  protected void resetTransforms( TupleEntry arguments, Fields fields )
111    {
112    Map<Comparable, Object> values = Tuples.asComparableMap( fields, arguments );
113
114    copier.resetTransforms( values );
115    }
116  }