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.assertion;
022
023import java.beans.ConstructorProperties;
024
025import cascading.flow.FlowProcess;
026import cascading.operation.AssertionLevel;
027import cascading.operation.PlannerLevel;
028import cascading.operation.ValueAssertion;
029import cascading.operation.ValueAssertionCall;
030import cascading.operation.expression.ExpressionOperation;
031import cascading.tuple.Fields;
032import cascading.tuple.TupleEntry;
033
034/**
035 * Class AssertExpression dynamically resolves a given expression using argument {@link cascading.tuple.Tuple} values. Any Tuple that
036 * returns true for the given expression pass the assertion. This {@link cascading.operation.Assertion}
037 * is based on the <a href="http://www.janino.net/">Janino</a> compiler.
038 * <p/>
039 * Specifically this filter uses the {@link org.codehaus.janino.ExpressionEvaluator}, thus the syntax from that class is inherited here.
040 * <p/>
041 * An expression may use field names directly as parameters in the expression, or field positions with the syntax
042 * "$n", where n is an integer.
043 * <p/>
044 * Given an argument tuple with the fields "a" and "b", the following expression returns true: <br/>
045 * <code>a + b == $0 + $1</code><br/>
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 expression.
049 */
050public class AssertExpression extends ExpressionOperation implements ValueAssertion<ExpressionOperation.Context>
051  {
052  /**
053   * Constructor ExpressionFilter creates a new ExpressionFilter instance.
054   *
055   * @param expression    of type String
056   * @param parameterType of type Class
057   */
058  @ConstructorProperties({"expression", "parameterType"})
059  public AssertExpression( String expression, Class parameterType )
060    {
061    super( Fields.ALL, expression, parameterType );
062    }
063
064  /**
065   * Constructor AssertExpression creates a new AssertExpression instance.
066   *
067   * @param fieldDeclaration of type Fields
068   * @param expression       of type String
069   * @param parameterNames   of type String[]
070   * @param parameterTypes   of type Class[]
071   */
072  @ConstructorProperties({"fieldDeclaration", "expression", "parameterNames", "parameterTypes"})
073  public AssertExpression( Fields fieldDeclaration, String expression, String[] parameterNames, Class[] parameterTypes )
074    {
075    super( fieldDeclaration, expression, parameterNames, parameterTypes );
076    }
077
078  @Override
079  public boolean supportsPlannerLevel( PlannerLevel plannerLevel )
080    {
081    return plannerLevel instanceof AssertionLevel;
082    }
083
084  @Override
085  public void doAssert( FlowProcess flowProcess, ValueAssertionCall<Context> assertionCall )
086    {
087    TupleEntry input = assertionCall.getArguments();
088
089    if( !(Boolean) evaluate( assertionCall.getContext(), input ) )
090      BaseAssertion.throwFail( "argument tuple: %s did not evaluate to true with expression: %s", input.getTuple().print(), block );
091    }
092  }