    001/*
    002 * Copyright (c) 2007-2017 Xplenty, Inc. 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.operation.expression;
    022
    023import java.beans.ConstructorProperties;
    024
    025import cascading.flow.FlowProcess;
    026import cascading.operation.Function;
    027import cascading.operation.FunctionCall;
    028import cascading.tuple.Fields;
    029import cascading.tuple.Tuple;
    030
    031/**
    032 * Class ScriptTupleFunction dynamically resolves a given expression using argument {@link cascading.tuple.Tuple} values.
    033 * This {@link cascading.operation.Function} is based on the <a href="http://www.janino.net/">Janino</a> compiler.
    034 * <p/>
    035 * This class is different from {@link ScriptFunction} in that it requires a new {@link Tuple} instance to be returned
    036 * by the script. ScriptFunction allows only a single value to be returned, which is passed into a result Tuple instance
    037 * internally.
    038 * <p/>
    039 * Specifically this function uses the {@link org.codehaus.janino.ScriptEvaluator},
    040 * thus the syntax from that class is inherited here.
    041 * <p/>
    042 * A script may use field names directly as parameters in the expression, or field positions with the syntax
    043 * "$n", where n is an integer.
    044 * <p/>
    045 * Given an argument tuple with the fields "a" and "b", the following script returns true: <br/>
    046 * {@code boolean result = (a + b == $0 + $1);}<br/>
    047 * {@code return cascading.tuple.Tuples.tuple( boolean );}<br/>
    048 * <p/>
    049 * Unlike an "expression" used by {@link ExpressionFunction}, a "script" requires each line to end in an semi-colon
    050 * (@{code ;}) and the final line to be a {@code return} statement that returns a new {@link Tuple} instance.
    051 * <p/>
    052 * Since Janino does not support "varargs", see the {@link cascading.tuple.Tuples} class for helper methods.
    053 * <p/>
    054 * Further, the types of the tuple elements will be coerced into the given parameterTypes. Regardless of the actual
    055 * tuple element values, they will be converted to the types expected by the script if possible.
    056 */
    057public class ScriptTupleFunction extends ScriptOperation implements Function<ScriptOperation.Context>
    058  {
    059  /**
    060   * Constructor ScriptFunction creates a new ScriptFunction instance.
    061   * <p/>
    062   * This constructor will use the runtime {@link cascading.operation.OperationCall#getArgumentFields()}
    063   * to source the {@code parameterNames} and {@code parameterTypes} required by the other constructors.
    064   * <p/>
    065   * The {@code returnType} will be retrieved from the given {@code fieldDeclaration.getTypeClass(0)}.
    066   *
    067   * @param fieldDeclaration of type Fields
    068   * @param script           of type String
    069   */
    070  @ConstructorProperties({"fieldDeclaration", "script"})
    071  public ScriptTupleFunction( Fields fieldDeclaration, String script )
    072    {
    073    super( ANY, fieldDeclaration, script, Tuple.class );
    074    }
    075
    076  /**
    077   * Constructor ScriptFunction creates a new ScriptFunction instance.
    078   * <p/>
    079   * This constructor will use the runtime {@link cascading.operation.OperationCall#getArgumentFields()}
    080   * to source the {@code parameterNames} and {@code parameterTypes} required by the other constructors, but
    081   * use {@code expectedTypes} to coerce the incoming types to before passing as parameters to the expression.
    082   *
    083   * @param fieldDeclaration of type Fields
    084   * @param script           of type String
    085   * @param expectedTypes    of type Class[]
    086   */
    087  @ConstructorProperties({"fieldDeclaration", "script", "expectedTypes"})
    088  public ScriptTupleFunction( Fields fieldDeclaration, String script, Class[] expectedTypes )
    089    {
    090    super( expectedTypes.length, fieldDeclaration, script, Tuple.class, expectedTypes );
    091    }
    092
    093  /**
    094   * Constructor ScriptFunction creates a new ScriptFunction 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 script with the "$" sign prepended.
    098   *
    099   * @param fieldDeclaration of type Fields
    100   * @param script           of type String
    101   * @param parameterNames   of type String[]
    102   * @param parameterTypes   of type Class[]
    103   */
    104  @ConstructorProperties({"fieldDeclaration", "script", "parameterNames", "parameterTypes"})
    105  public ScriptTupleFunction( Fields fieldDeclaration, String script, String[] parameterNames, Class[] parameterTypes )
    106    {
    107    super( parameterTypes.length, fieldDeclaration, script, Tuple.class, parameterNames, parameterTypes );
    108    }
    109
    110  public String getScript()
    111    {
    112    return getBlock();
    113    }
    114
    115  @Override
    116  public void operate( FlowProcess flowProcess, FunctionCall<Context> functionCall )
    117    {
    118    functionCall.getOutputCollector().add( (Tuple) evaluate( functionCall.getContext(), functionCall.getArguments() ) );
    119    }
    120  }