001/*
002 * Copyright (c) 2016-2017 Chris K Wensel <chris@wensel.net>. 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.Collections;
024import java.util.Iterator;
025import java.util.LinkedHashMap;
026import java.util.Map;
027import java.util.Set;
028
029import cascading.flow.FlowProcess;
030import cascading.operation.Function;
031import cascading.operation.FunctionCall;
032import cascading.operation.OperationCall;
033import cascading.tuple.Fields;
034import cascading.tuple.Tuple;
035import cascading.tuple.TupleEntry;
036import heretical.pointer.path.NestedPointerCompiler;
037import heretical.pointer.path.Pointer;
038
039/**
040 *
041 */
042public abstract class NestedBaseFunction<Node, Result> extends NestedBaseOperation<Node, Result, NestedBaseFunction.Context> implements Function<NestedBaseFunction.Context>
043  {
044  protected class Context
045    {
046    public Tuple result;
047    public Map<Fields, Pointer<Node>> pointers;
048
049    public Context( Map<Fields, Pointer<Node>> pointers, Tuple result )
050      {
051      this.result = result;
052      this.pointers = pointers;
053      }
054    }
055
056  protected String rootPointer = "";
057  protected Map<Fields, Pointer<Node>> pointers = new LinkedHashMap<>();
058
059  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration )
060    {
061    this( nestedCoercibleType, fieldDeclaration, Collections.emptyMap() );
062    }
063
064  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, String rootPointer )
065    {
066    this( nestedCoercibleType, fieldDeclaration, Collections.emptyMap() );
067    this.rootPointer = rootPointer;
068    }
069
070  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, Map<Fields, String> pointerMap )
071    {
072    super( nestedCoercibleType, fieldDeclaration );
073
074    if( pointerMap == null || pointerMap.isEmpty() )
075      return;
076
077    NestedPointerCompiler<Node, Result> compiler = getNestedPointerCompiler();
078
079    for( Map.Entry<Fields, String> entry : pointerMap.entrySet() )
080      this.pointers.put( entry.getKey(), compiler.compile( entry.getValue() ) );
081    }
082
083  @Override
084  public void prepare( FlowProcess flowProcess, OperationCall<NestedBaseFunction.Context> operationCall )
085    {
086    Map<Fields, Pointer<Node>> resolvedPointers = new LinkedHashMap<>();
087    Fields argumentFields = operationCall.getArgumentFields();
088
089    for( Map.Entry<Fields, Pointer<Node>> entry : this.pointers.entrySet() )
090      resolvedPointers.put( argumentFields.select( entry.getKey() ), entry.getValue() );
091
092    if( resolvedPointers.isEmpty() ) // use resolved argument fields
093      {
094      NestedPointerCompiler<Node, Result> compiler = getNestedPointerCompiler();
095
096      for( Iterator<Fields> iterator = argumentFields.fieldsIterator(); iterator.hasNext(); )
097        {
098        Fields argument = iterator.next();
099
100        resolvedPointers.put( argument, compiler.compile( rootPointer + "/" + argument.get( 0 ).toString() ) );
101        }
102      }
103
104    operationCall.setContext( new Context( resolvedPointers, Tuple.size( 1 ) ) );
105    }
106
107  @Override
108  public void operate( FlowProcess flowProcess, FunctionCall<NestedBaseFunction.Context> functionCall )
109    {
110    Node node = getNode( functionCall.getArguments() );
111
112    Set<Map.Entry<Fields, Pointer<Node>>> entries = functionCall.getContext().pointers.entrySet();
113
114    for( Map.Entry<Fields, Pointer<Node>> entry : entries )
115      {
116      Fields key = entry.getKey();
117      Pointer<Node> value = entry.getValue();
118
119      Object argumentValue = functionCall.getArguments().getObject( key );
120      Node result = getLiteralNode( argumentValue );
121
122      value.set( node, result );
123      }
124
125    functionCall.getContext().result.set( 0, node );
126
127    functionCall.getOutputCollector().add( functionCall.getContext().result );
128    }
129
130  protected abstract Node getNode( TupleEntry arguments );
131
132  }