001/*
002 * Copyright (c) 2016-2020 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.operation.SerPredicate;
034import cascading.tuple.Fields;
035import cascading.tuple.Tuple;
036import cascading.tuple.TupleEntry;
037import cascading.util.Pair;
038import heretical.pointer.path.NestedPointerCompiler;
039import heretical.pointer.path.Pointer;
040
041/**
042 *
043 */
044public abstract class NestedBaseFunction<Node, Result> extends NestedBaseOperation<Node, Result, NestedBaseFunction.Context> implements Function<NestedBaseFunction.Context>
045  {
046  protected class Context
047    {
048    public Tuple result;
049    public Map<Fields, Pair<SerPredicate<?>, Pointer<Node>>> pointers;
050
051    public Context( Map<Fields, Pair<SerPredicate<?>, Pointer<Node>>> pointers, Tuple result )
052      {
053      this.result = result;
054      this.pointers = pointers;
055      }
056    }
057
058  protected String rootPointer = "";
059  protected SerPredicate<?> defaultValueFilter = ( v ) -> true;
060  protected Map<Fields, Pointer<Node>> pointers = new LinkedHashMap<>();
061
062  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration )
063    {
064    this( nestedCoercibleType, fieldDeclaration, Collections.emptyMap() );
065    }
066
067  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, SerPredicate<?> defaultValueFilter )
068    {
069    this( nestedCoercibleType, fieldDeclaration, defaultValueFilter, Collections.emptyMap() );
070    }
071
072  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, String rootPointer )
073    {
074    this( nestedCoercibleType, fieldDeclaration, Collections.emptyMap() );
075
076    if( rootPointer != null )
077      this.rootPointer = rootPointer;
078    }
079
080  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, String rootPointer, SerPredicate<?> defaultValueFilter )
081    {
082    this( nestedCoercibleType, fieldDeclaration, defaultValueFilter, Collections.emptyMap() );
083
084    if( rootPointer != null )
085      this.rootPointer = rootPointer;
086    }
087
088  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, Map<Fields, String> pointerMap )
089    {
090    this( nestedCoercibleType, fieldDeclaration, null, pointerMap );
091    }
092
093  public NestedBaseFunction( NestedCoercibleType<Node, Result> nestedCoercibleType, Fields fieldDeclaration, SerPredicate<?> defaultValueFilter, Map<Fields, String> pointerMap )
094    {
095    super( nestedCoercibleType, fieldDeclaration );
096
097    if( defaultValueFilter != null )
098      this.defaultValueFilter = defaultValueFilter;
099
100    if( pointerMap != null && !pointerMap.isEmpty() )
101      {
102      NestedPointerCompiler<Node, Result> compiler = getNestedPointerCompiler();
103
104      for( Map.Entry<Fields, String> entry : pointerMap.entrySet() )
105        this.pointers.put( entry.getKey(), compiler.compile( entry.getValue() ) );
106      }
107    }
108
109  @Override
110  public void prepare( FlowProcess flowProcess, OperationCall<NestedBaseFunction.Context> operationCall )
111    {
112    Map<Fields, Pair<SerPredicate<?>, Pointer<Node>>> resolvedPointers = new LinkedHashMap<>();
113    Fields argumentFields = operationCall.getArgumentFields();
114
115    for( Map.Entry<Fields, Pointer<Node>> entry : this.pointers.entrySet() )
116      resolvedPointers.put( argumentFields.select( entry.getKey() ), new Pair<>( defaultValueFilter, entry.getValue() ) );
117
118    if( resolvedPointers.isEmpty() ) // use resolved argument fields
119      {
120      NestedPointerCompiler<Node, Result> compiler = getNestedPointerCompiler();
121
122      for( Iterator<Fields> iterator = argumentFields.fieldsIterator(); iterator.hasNext(); )
123        {
124        Fields argument = iterator.next();
125
126        Pointer<Node> pointer = compiler.compile( rootPointer + "/" + argument.get( 0 ).toString() );
127        resolvedPointers.put( argument, new Pair<>( defaultValueFilter, pointer ) );
128        }
129      }
130
131    operationCall.setContext( new Context( resolvedPointers, Tuple.size( 1 ) ) );
132    }
133
134  @Override
135  public void operate( FlowProcess flowProcess, FunctionCall<NestedBaseFunction.Context> functionCall )
136    {
137    Context context = functionCall.getContext();
138    Node node = getNode( functionCall.getArguments() );
139
140    Set<Map.Entry<Fields, Pair<SerPredicate<?>, Pointer<Node>>>> entries = context.pointers.entrySet();
141
142    for( Map.Entry<Fields, Pair<SerPredicate<?>, Pointer<Node>>> entry : entries )
143      {
144      Fields key = entry.getKey();
145      SerPredicate<Object> predicate = (SerPredicate<Object>) entry.getValue().getLhs();
146      Pointer<Node> pointer = entry.getValue().getRhs();
147
148      Object argumentValue = functionCall.getArguments().getObject( key );
149
150      if( predicate.test( argumentValue ) )
151        {
152        Node result = getLiteralNode( argumentValue );
153
154        pointer.set( node, result );
155        }
156      }
157
158    context.result.set( 0, node );
159
160    functionCall.getOutputCollector().add( context.result );
161    }
162
163  protected abstract Node getNode( TupleEntry arguments );
164  }