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