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.Filter;
027    import 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     */
049    public 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      }