    001    /*
    002     * Copyright (c) 2007-2014 Concurrent, 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
    021    package cascading.operation.expression;
    022
    023    import java.beans.ConstructorProperties;
    024
    025    import cascading.flow.FlowProcess;
    026    import cascading.operation.Function;
    027    import cascading.operation.FunctionCall;
    028    import cascading.tuple.Fields;
    029    import 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     */
    057    public 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      }