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; 031import org.codehaus.janino.ExpressionEvaluator; 032 033/** 034 * Class ExpressionFunction dynamically resolves a given expression using argument {@link Tuple} values. This {@link Function} 035 * is based on the <a href="http://www.janino.net/">Janino</a> compiler. 036 * <p> 037 * Specifically this function uses the {@link ExpressionEvaluator}, thus the syntax from that class is inherited here. 038 * <p> 039 * An expression may use field names directly as parameters in the expression, or field positions with the syntax 040 * "$n", where n is an integer. 041 * <p> 042 * Given an argument tuple with the fields "a" and "b", the following expression returns true: <br> 043 * {@code a + b == $0 + $1}<br> 044 * <p> 045 * Further, the types of the tuple elements will be coerced into the given parameterTypes. Regardless of the actual 046 * tuple element values, they will be converted to the types expected by the expression. 047 * <p> 048 * Field names used in the expression should be valid Java variable names; for example, '+' or '-' are not allowed. 049 * Also the use of a field name that begins with an upper-case character is likely to fail and should be avoided. 050 */ 051public class ExpressionFunction extends ExpressionOperation implements Function<ScriptOperation.Context> 052 { 053 /** 054 * Constructor ExpressionFunction creates a new ExpressionFunction instance. 055 * <p> 056 * This constructor, when used with incoming arguments that have type information, the argument field 057 * names can be used directly in the the expression, for example {@code a + b }. The type of {@code a} and {@code b} 058 * will be inherited from the incoming argument fields. 059 * <p> 060 * Or, if the incoming argument selector is {@link Fields#NONE}, an expression using only static method calls 061 * or constants can be used. 062 * <p> 063 * This is useful when inserting random numbers for example, {@code (int) (Math.random() * Integer.MAX_VALUE) }. 064 * 065 * @param fieldDeclaration of type Fields 066 * @param expression of type String 067 */ 068 @ConstructorProperties({"fieldDeclaration", "expression"}) 069 public ExpressionFunction( Fields fieldDeclaration, String expression ) 070 { 071 super( fieldDeclaration, expression ); 072 073 verify( fieldDeclaration ); 074 } 075 076 /** 077 * Constructor ExpressionFunction creates a new ExpressionFunction instance. 078 * <p> 079 * This constructor assumes all parameter are of the same type. 080 * 081 * @param fieldDeclaration of type Fields 082 * @param expression of type String 083 * @param parameterType of type Class 084 */ 085 @ConstructorProperties({"fieldDeclaration", "expression", "parameterType"}) 086 public ExpressionFunction( Fields fieldDeclaration, String expression, Class parameterType ) 087 { 088 super( fieldDeclaration, expression, parameterType ); 089 090 verify( fieldDeclaration ); 091 } 092 093 /** 094 * Constructor ExpressionFunction creates a new ExpressionFunction instance. 095 * <p> 096 * This constructor expects all parameter type names to be declared with their types. Positional parameters must 097 * be named the same as in the given expression with the "$" sign prepended. 098 * 099 * @param fieldDeclaration of type Fields 100 * @param expression of type String 101 * @param parameterNames of type String[] 102 * @param parameterTypes of type Class[] 103 */ 104 @ConstructorProperties({"fieldDeclaration", "expression", "parameterNames", "parameterTypes"}) 105 public ExpressionFunction( Fields fieldDeclaration, String expression, String[] parameterNames, Class[] parameterTypes ) 106 { 107 super( fieldDeclaration, expression, parameterNames, parameterTypes ); 108 109 verify( fieldDeclaration ); 110 } 111 112 private void verify( Fields fieldDeclaration ) 113 { 114 if( !fieldDeclaration.isSubstitution() && fieldDeclaration.size() != 1 ) 115 throw new IllegalArgumentException( "fieldDeclaration may only declare one field, was " + fieldDeclaration.print() ); 116 } 117 118 @Override 119 public void operate( FlowProcess flowProcess, FunctionCall<ExpressionOperation.Context> functionCall ) 120 { 121 functionCall.getContext().result.set( 0, evaluate( functionCall.getContext(), functionCall.getArguments() ) ); 122 functionCall.getOutputCollector().add( functionCall.getContext().result ); 123 } 124 }