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  }