/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.libformula.editor;
import java.util.HashMap;
import java.util.Map;
import org.pentaho.di.core.Const;
import org.pentaho.reporting.libraries.formula.lvalues.ContextLookup;
import org.pentaho.reporting.libraries.formula.lvalues.FormulaFunction;
import org.pentaho.reporting.libraries.formula.lvalues.LValue;
import org.pentaho.reporting.libraries.formula.lvalues.ParsePosition;
import org.pentaho.reporting.libraries.formula.lvalues.StaticValue;
import org.pentaho.reporting.libraries.formula.lvalues.Term;
import org.pentaho.reporting.libraries.formula.parser.FormulaParser;
import org.pentaho.reporting.libraries.formula.parser.ParseException;
import org.pentaho.reporting.libraries.formula.parser.Token;
import org.pentaho.reporting.libraries.formula.typing.coretypes.DateTimeType;
import org.pentaho.reporting.libraries.formula.typing.coretypes.LogicalType;
import org.pentaho.reporting.libraries.formula.typing.coretypes.NumberType;
import org.pentaho.reporting.libraries.formula.typing.coretypes.TextType;
public class FormulaEvaluator {
private String[] keyWords;
private String[] inputFields;
private FormulaParser formulaParser;
/**
* @param keyWords
* @param inputFields
*/
public FormulaEvaluator( String[] keyWords, String[] inputFields ) {
this.keyWords = keyWords;
this.inputFields = inputFields;
formulaParser = new FormulaParser();
}
public Map<String, FormulaMessage> evaluateFormula( String expression ) {
Map<String, FormulaMessage> messages = new HashMap<String, FormulaMessage>();
if ( expression == null || expression.trim().equals( "" ) ) {
return messages;
}
try {
LValue lValue = formulaParser.parse( expression.trim() );
// if (lValue.toString()!=null) report+=lValue.toString()+" ("+lValue.getClass().getName()+") \n";
// Verify all the functions used
// See if we can find them all in our list.
//
// Key - Message mapping
//
if ( lValue != null ) {
try {
verifyLValue( lValue, messages );
} catch ( Exception e ) {
FormulaMessage message =
new FormulaMessage( FormulaMessage.TYPE_ERROR, "Parse Exception", "Parsing error in formula : "
+ e.getMessage() );
messages.put( message.toString(), message );
}
}
} catch ( ParseException parseException ) {
Token token = parseException.currentToken;
boolean handled = false;
if ( token != null ) {
String problem = token.toString();
if ( problem != null ) {
FormulaMessage message =
new FormulaMessage(
FormulaMessage.TYPE_ERROR, new ParsePosition(
token.beginLine, token.beginColumn, token.endLine, token.endColumn ), "Parse Exception",
"Parse exception near '"
+ problem + "' on line " + token.beginLine + ", column " + token.beginColumn + "\n\n"
+ parseException.getMessage() );
messages.put( message.toString(), message );
handled = true;
}
}
if ( !handled ) {
if ( parseException.getMessage() != null ) {
FormulaMessage message =
new FormulaMessage( FormulaMessage.TYPE_ERROR, "Parse Exception", "Parsing error in formula : "
+ parseException.getMessage() );
messages.put( message.toString(), message );
} else {
FormulaMessage message =
new FormulaMessage( FormulaMessage.TYPE_ERROR, "Parse Exception", "Parsing error in formula : "
+ parseException.toString() );
messages.put( message.toString(), message );
}
}
} catch ( Throwable e ) {
if ( e.getMessage() != null ) {
FormulaMessage message =
new FormulaMessage( FormulaMessage.TYPE_ERROR, "Parse Exception", "Parsing error in formula : "
+ e.getMessage() );
messages.put( message.toString(), message );
} else {
FormulaMessage message =
new FormulaMessage( FormulaMessage.TYPE_ERROR, "Parse Exception", "Parsing error in formula : "
+ e.toString() );
messages.put( message.toString(), message );
}
}
return messages;
}
public void verifyLValue( LValue lvalue, Map<String, FormulaMessage> messages ) {
if ( lvalue instanceof FormulaFunction ) {
FormulaFunction formulaFunction = (FormulaFunction) lvalue;
String functionName = formulaFunction.getFunctionName();
if ( Const.indexOfString( functionName, keyWords ) < 0 ) {
FormulaMessage formulaMessage =
new FormulaMessage(
FormulaMessage.TYPE_ERROR, formulaFunction.getParsePosition(), functionName, "Unknown function" );
messages.put( formulaMessage.toString(), formulaMessage );
} else {
FormulaMessage formulaMessage =
new FormulaMessage(
FormulaMessage.TYPE_FUNCTION, formulaFunction.getParsePosition(), functionName, "Function" );
messages.put( formulaMessage.toString(), formulaMessage );
}
LValue[] arguments = formulaFunction.getChildValues();
if ( arguments != null ) {
for ( LValue argument : arguments ) {
verifyLValue( argument, messages );
}
}
} else if ( lvalue instanceof Term ) {
//
// This is a Term (an expression, can contain several other LValues)
//
Term term = (Term) lvalue;
LValue head = term.getHeadValue();
verifyLValue( head, messages );
LValue[] operands = term.getOperands();
if ( operands != null ) {
for ( LValue operand : operands ) {
verifyLValue( operand, messages );
}
}
// report.append("Term head value found : ").append(term.getHeadValue().toString());
} else if ( lvalue instanceof StaticValue ) {
//
// Static value : String, number, date...
//
StaticValue staticValue = (StaticValue) lvalue;
if ( staticValue.getValueType() instanceof NumberType ) {
FormulaMessage formulaMessage =
new FormulaMessage( FormulaMessage.TYPE_STATIC_NUMBER, staticValue.getParsePosition(), staticValue
.toString(), "Static number" );
messages.put( formulaMessage.toString(), formulaMessage );
} else if ( staticValue.getValueType() instanceof TextType ) {
FormulaMessage formulaMessage =
new FormulaMessage( FormulaMessage.TYPE_STATIC_STRING, staticValue.getParsePosition(), staticValue
.toString(), "Static string" );
messages.put( formulaMessage.toString(), formulaMessage );
} else if ( staticValue.getValueType() instanceof DateTimeType ) {
FormulaMessage formulaMessage =
new FormulaMessage( FormulaMessage.TYPE_STATIC_DATE, staticValue.getParsePosition(), staticValue
.toString(), "Static date/time" );
messages.put( formulaMessage.toString(), formulaMessage );
} else if ( staticValue.getValueType() instanceof LogicalType ) {
FormulaMessage formulaMessage =
new FormulaMessage( FormulaMessage.TYPE_STATIC_LOGICAL, staticValue.getParsePosition(), staticValue
.toString(), "Static logical" );
messages.put( formulaMessage.toString(), formulaMessage );
}
} else if ( lvalue instanceof ContextLookup ) {
//
// A field name
//
ContextLookup contextLookup = (ContextLookup) lvalue;
FormulaMessage fieldMessage =
new FormulaMessage(
FormulaMessage.TYPE_FIELD, contextLookup.getParsePosition(), contextLookup.getName(), "Field" );
messages.put( fieldMessage.toString(), fieldMessage );
String name = contextLookup.getName();
if ( Const.indexOfString( name, inputFields ) < 0 ) {
FormulaMessage formulaMessage =
new FormulaMessage(
FormulaMessage.TYPE_ERROR, contextLookup.getParsePosition(), name, "Unknown field name" );
messages.put( formulaMessage.toString(), formulaMessage );
}
}
}
}