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 }