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.Filter;
028import cascading.operation.FilterCall;
029
030/**
031 * Class ScriptFilter dynamically resolves a given expression using argument {@link cascading.tuple.Tuple} values.
032 * This {@link cascading.operation.Filter} is based on the <a href="http://www.janino.net/">Janino</a> compiler.
033 * <p>
034 * Specifically this filter uses the {@link org.codehaus.janino.ScriptEvaluator},
035 * thus the syntax from that class is inherited here.
036 * <p>
037 * A script may use field names directly as parameters in the expression, or field positions with the syntax
038 * "$n", where n is an integer.
039 * <p>
040 * Given an argument tuple with the fields "a" and "b", the following script returns true: <br>
041 * {@code boolean result = (a + b == $0 + $1);}<br>
042 * {@code return boolean;}<br>
043 * <p>
044 * Unlike an "expression" used by {@link ExpressionFilter}, a "script" requires each line to end in an semi-colon
045 * (@{code ;}) and the final line to be a {@code return} statement.
046 * <p>
047 * Further, the types of the tuple elements will be coerced into the given parameterTypes. Regardless of the actual
048 * tuple element values, they will be converted to the types expected by the script if possible.
049 */
050public class ScriptFilter extends ScriptOperation implements Filter<ScriptOperation.Context>
051  {
052  /**
053   * Constructor ScriptFilter creates a new ScriptFilter instance.
054   *
055   * @param script of type String
056   */
057  @ConstructorProperties({"script"})
058  public ScriptFilter( String script )
059    {
060    super( ANY, script, Boolean.class );
061    }
062
063  /**
064   * Constructor ScriptFilter creates a new ScriptFilter instance.
065   *
066   * @param script        of type String
067   * @param parameterName of type String
068   * @param parameterType of type Class
069   */
070  @ConstructorProperties({"script", "parameterName", "parameterType"})
071  public ScriptFilter( String script, String parameterName, Class parameterType )
072    {
073    super( 1, script, Boolean.class, new String[]{parameterName}, new Class[]{parameterType} );
074    }
075
076  /**
077   * Constructor ScriptFilter creates a new ScriptFilter 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 script        of type String
084   * @param expectedTypes of type Class[]
085   */
086  @ConstructorProperties({"script", "expectedTypes"})
087  public ScriptFilter( String script, Class[] expectedTypes )
088    {
089    super( expectedTypes.length, script, Boolean.class, expectedTypes );
090    }
091
092  /**
093   * Constructor ScriptFilter creates a new ScriptFilter instance.
094   *
095   * @param script         of type String
096   * @param parameterNames of type String[]
097   * @param parameterTypes of type Class[]
098   */
099  @ConstructorProperties({"script", "parameterNames", "parameterTypes"})
100  public ScriptFilter( String script, String[] parameterNames, Class[] parameterTypes )
101    {
102    super( parameterTypes.length, script, Boolean.class, parameterNames, parameterTypes );
103    }
104
105  public String getScript()
106    {
107    return getBlock();
108    }
109
110  @Override
111  public boolean isRemove( FlowProcess flowProcess, FilterCall<Context> filterCall )
112    {
113    return (Boolean) evaluate( filterCall.getContext(), filterCall.getArguments() );
114    }
115  }