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  }