001/* 002 * Copyright (c) 2016-2017 Chris K Wensel <[email protected]>. All Rights Reserved. 003 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved. 004 * 005 * Project and contact information: http://www.cascading.org/ 006 * 007 * This file is part of the Cascading project. 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package cascading.operation.expression; 023 024import java.beans.ConstructorProperties; 025 026import cascading.flow.FlowProcess; 027import cascading.operation.Function; 028import cascading.operation.FunctionCall; 029import cascading.tuple.Fields; 030import cascading.tuple.Tuple; 031 032/** 033 * Class ScriptTupleFunction dynamically resolves a given expression using argument {@link cascading.tuple.Tuple} values. 034 * This {@link cascading.operation.Function} is based on the <a href="http://www.janino.net/">Janino</a> compiler. 035 * <p> 036 * This class is different from {@link ScriptFunction} in that it requires a new {@link Tuple} instance to be returned 037 * by the script. ScriptFunction allows only a single value to be returned, which is passed into a result Tuple instance 038 * internally. 039 * <p> 040 * Specifically this function uses the {@link org.codehaus.janino.ScriptEvaluator}, 041 * thus the syntax from that class is inherited here. 042 * <p> 043 * A script may use field names directly as parameters in the expression, or field positions with the syntax 044 * "$n", where n is an integer. 045 * <p> 046 * Given an argument tuple with the fields "a" and "b", the following script returns true: <br> 047 * {@code boolean result = (a + b == $0 + $1);}<br> 048 * {@code return cascading.tuple.Tuples.tuple( boolean );}<br> 049 * <p> 050 * Unlike an "expression" used by {@link ExpressionFunction}, a "script" requires each line to end in an semi-colon 051 * (@{code ;}) and the final line to be a {@code return} statement that returns a new {@link Tuple} instance. 052 * <p> 053 * Since Janino does not support "varargs", see the {@link cascading.tuple.Tuples} class for helper methods. 054 * <p> 055 * Further, the types of the tuple elements will be coerced into the given parameterTypes. Regardless of the actual 056 * tuple element values, they will be converted to the types expected by the script if possible. 057 */ 058public class ScriptTupleFunction extends ScriptOperation implements Function<ScriptOperation.Context> 059 { 060 /** 061 * Constructor ScriptFunction creates a new ScriptFunction instance. 062 * <p> 063 * This constructor will use the runtime {@link cascading.operation.OperationCall#getArgumentFields()} 064 * to source the {@code parameterNames} and {@code parameterTypes} required by the other constructors. 065 * <p> 066 * The {@code returnType} will be retrieved from the given {@code fieldDeclaration.getTypeClass(0)}. 067 * 068 * @param fieldDeclaration of type Fields 069 * @param script of type String 070 */ 071 @ConstructorProperties({"fieldDeclaration", "script"}) 072 public ScriptTupleFunction( Fields fieldDeclaration, String script ) 073 { 074 super( ANY, fieldDeclaration, script, Tuple.class ); 075 } 076 077 /** 078 * Constructor ScriptFunction creates a new ScriptFunction instance. 079 * <p> 080 * This constructor will use the runtime {@link cascading.operation.OperationCall#getArgumentFields()} 081 * to source the {@code parameterNames} and {@code parameterTypes} required by the other constructors, but 082 * use {@code expectedTypes} to coerce the incoming types to before passing as parameters to the expression. 083 * 084 * @param fieldDeclaration of type Fields 085 * @param script of type String 086 * @param expectedTypes of type Class[] 087 */ 088 @ConstructorProperties({"fieldDeclaration", "script", "expectedTypes"}) 089 public ScriptTupleFunction( Fields fieldDeclaration, String script, Class[] expectedTypes ) 090 { 091 super( expectedTypes.length, fieldDeclaration, script, Tuple.class, expectedTypes ); 092 } 093 094 /** 095 * Constructor ScriptFunction creates a new ScriptFunction instance. 096 * <p> 097 * This constructor expects all parameter type names to be declared with their types. Positional parameters must 098 * be named the same as in the given script with the "$" sign prepended. 099 * 100 * @param fieldDeclaration of type Fields 101 * @param script of type String 102 * @param parameterNames of type String[] 103 * @param parameterTypes of type Class[] 104 */ 105 @ConstructorProperties({"fieldDeclaration", "script", "parameterNames", "parameterTypes"}) 106 public ScriptTupleFunction( Fields fieldDeclaration, String script, String[] parameterNames, Class[] parameterTypes ) 107 { 108 super( parameterTypes.length, fieldDeclaration, script, Tuple.class, parameterNames, parameterTypes ); 109 } 110 111 public String getScript() 112 { 113 return getBlock(); 114 } 115 116 @Override 117 public void operate( FlowProcess flowProcess, FunctionCall<Context> functionCall ) 118 { 119 functionCall.getOutputCollector().add( (Tuple) evaluate( functionCall.getContext(), functionCall.getArguments() ) ); 120 } 121 }