public void AddFunctionResolveWorksTest() { const string function = "add"; const EvaluationMode evaluationMode = EvaluationMode.Resolve; #region 1L + 1L var parameters1 = new ArrayList {1L, 1L}; var expressionFunction1 = new ExpressionFunction(function, parameters1, evaluationMode); Assert.IsNotNull(expressionFunction1); var result1 = expressionFunction1.Run(); Assert.IsNotNull(result1); Assert.IsInstanceOfType(result1, typeof(long)); Assert.AreEqual(2L, result1); #endregion #region 10L + 10L var parameters2 = new ArrayList { 10L, 10L }; var expressionFunction2 = new ExpressionFunction(function, parameters2, evaluationMode); Assert.IsNotNull(expressionFunction2); var result2 = expressionFunction2.Run(); Assert.IsNotNull(result2); Assert.IsInstanceOfType(result2, typeof(long)); Assert.AreEqual(20L, result2); #endregion }
public void AddFunctionResolveCharInvalidInputFormatTest() { const string function = "add"; const EvaluationMode evaluationMode = EvaluationMode.Resolve; #region 1 char + 1 char var parameters1 = new ArrayList { '1', '1' }; var expresssionFunction1 = new ExpressionFunction(function, parameters1, evaluationMode); expresssionFunction1.Run(); #endregion }
public void AddFunctionResolveStringInvalidInputFormatTest() { const string function = "add"; const EvaluationMode evaluationMode = EvaluationMode.Resolve; #region 10 string + 10 string var parameters1 = new ArrayList { "10", "10" }; var expresssionFunction1 = new ExpressionFunction(function, parameters1, evaluationMode); expresssionFunction1.Run(); #endregion }
/// <summary> /// Evaluates the function. /// </summary> /// <param name="function">The function.</param> /// <param name="mode">The mode.</param> /// <returns>The result of the evaluated function.</returns> private object EvaluateFunction(string function, EvaluationMode mode) { Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionEvaluatorEvaluateFunction, "Function: '{0}'. Evaluation Mode: '{1}'.", function, mode); object result = null; try { // Locate the opening and closing () characters for the function // by looking for the first and last instance of each character // This will ignore any nested functions which may be used as a parameter int open = function.IndexOf('('); int close = function.LastIndexOf(')'); // Break apart the function expression to identify the function name // and parameter string (content between parentheses) string functionName = function.Substring(0, open); string parameterString = function.Substring(open + 1, close - open - 1); // Create new array lists to hold the unresolved and resolved parameters for the function // Only evaluate parameters if the parameter string is not empty, as it will // be for functions like DateTimeNow() and Null() ArrayList unresolvedParameters = new ArrayList(); ArrayList parameters = new ArrayList(); if (!string.IsNullOrEmpty(parameterString.Trim())) { // The function expression could contain nested functions with their own commas // For example, Trim(Left(ReplaceString(attribute, "This", "That"), 8)) // Consequently, we can't assume that a split by comma returns each parameter // We need to loop through each and determine if a parameter needs to be reassembled // based on the positioning of ( and ) characters and quotation marks StringBuilder reassembled = new StringBuilder(); bool openString = false; int openFunctions = 0; // First, make sure that there are an appropriate number of ( and ) characters // and also make sure there is no open string in the function foreach (char c in parameterString) { if (c.Equals('\"')) { openString = !openString; } if (c.Equals('(') && !openString) { openFunctions += 1; } if (c.Equals(')') && !openString) { openFunctions -= 1; } } // If there is an open string or the number of open and close // parantheses characters do not match, throw an exception if (openString) { throw Logger.Instance.ReportError(EventIdentifier.ExpressionEvaluatorEvaluateFunctionQuotesValidationError, new InvalidFunctionFormatException(Messages.ExpressionEvaluator_FunctionParameterQuotesValidationError, functionName)); } if (openFunctions != 0) { throw Logger.Instance.ReportError(EventIdentifier.ExpressionEvaluatorEvaluateFunctionParenthesisValidationError, new InvalidFunctionFormatException(Messages.ExpressionEvaluator_FunctionParameterParenthesisValidationError, functionName)); } // Loop through each parameter fragment, split by comma, // and determine if reassembly is required foreach (string s in parameterString.Split(',')) { // Count the number of ( and ) characters in the current string // Only consider the parentheses relevent if it is not in an open string foreach (char c in s) { if (c.Equals('\"')) { openString = !openString; } else if (c.Equals('(') && !openString) { openFunctions += 1; } else if (c.Equals(')') && !openString) { openFunctions -= 1; } } // Add the string to the reassembled string builder // and determine how to proceed based on whether or not // we are currently reassembling parameter fragments reassembled.Append(s); if (openFunctions > 0 || openString) { reassembled.Append(","); } else { // If a parameter is not open, it either means that no // reassembly was required or we have completed reassembly // Either way, add the parameter to the list of unresolved parameters // and reset the string builder unresolvedParameters.Add(reassembled.ToString().Trim()); reassembled = new StringBuilder(); } } foreach (string s in unresolvedParameters) { Logger.Instance.WriteVerbose(EventIdentifier.ExpressionEvaluatorEvaluateFunction, "Resolving unresolved function parameter '{0}'.", s); ParameterType type = DetermineParameterType(s); switch (type) { case ParameterType.String: // Add the string value after trimming the quotes parameters.Add(EscapeString(s)); break; case ParameterType.Integer: // Add the parsed value as long as FIM datatype is System.Int64 parameters.Add(long.Parse(s, CultureInfo.InvariantCulture)); break; case ParameterType.Boolean: // Add the parsed Boolean value parameters.Add(bool.Parse(s)); break; case ParameterType.Lookup: if (mode == EvaluationMode.Parse) { // For parse, add the lookup to the cache // Mark the grammar in the parameter list by adding the appropriate enum value if (!this.lookupCache.ContainsKey(s)) { this.lookupCache.Add(s, null); } parameters.Add(ParameterType.Lookup); } else { // For resolution, pull the value from the lookup cache and // add it to the parameter list if (this.lookupCache.ContainsKey(s)) { parameters.Add(this.lookupCache[s]); } else { throw Logger.Instance.ReportError(EventIdentifier.ExpressionEvaluatorEvaluateFunctionLookupCacheValidationError, new InvalidFunctionFormatException(Messages.ExpressionEvaluator_LookupCacheValidationError, s)); } } break; case ParameterType.Variable: if (mode == EvaluationMode.Parse) { // For parse, add the variable to the cache // Mark the variable in the parameter list by adding the appropriate enum value if (!this.variableCache.ContainsKey(s)) { this.variableCache.Add(s, null); } parameters.Add(ParameterType.Variable); } else { // For resolution, pull the value from the variable cache and // add it to the parameter list if (this.variableCache.ContainsKey(s)) { parameters.Add(this.variableCache[s]); } else { throw Logger.Instance.ReportError(EventIdentifier.ExpressionEvaluatorEvaluateFunctionVariableCacheValidationError, new InvalidFunctionFormatException(Messages.ExpressionEvaluator_VariableCacheValidationError, s)); } } break; case ParameterType.Function: if (mode == EvaluationMode.Parse) { // For parse, recursively evaluate the function and any nested functions // Mark the function in the parameter list by adding the appropriate enum value this.EvaluateFunction(s, mode); parameters.Add(ParameterType.Function); } else { // For resolution, recursively resolve the function and any nested functions // and add the end result to the parameter list for the current function parameters.Add(this.EvaluateFunction(s, mode)); } break; case ParameterType.Expression: if (mode == EvaluationMode.Parse) { // For parse, recursively evaluate the expression and any nested functions // Mark the expression in the paramter list by adding the appropriate enum value this.EvaluateExpression(s, mode); parameters.Add(ParameterType.Expression); } else { // For resolution, recursively resolve the expression and any nested functions // and add the end result to the parameter list for the current function parameters.Add(this.EvaluateExpression(s, mode)); } break; default: throw Logger.Instance.ReportError(EventIdentifier.ExpressionEvaluatorEvaluateFunctionParameterTypeValidationError, new InvalidFunctionFormatException()); } } } // Special handling for EvaluateExpression() function so that the lookups in the expression to evaluate are resolved // Assumption: the lookups used in the expression are already used in some other expressions // e.g. Consider [//Query/Site/xUserTemplateExpression] returning IIF(Eq([//Target/Department],"HR"),"Template1","Template2") // We want EvaluateExpression([//Query/Site/xUserTemplateExpression]). // In this case caller activity need to ensure that [//Target/Department] is already used any other expression // so that it's part of LookupCache. if (functionName.Equals(ParameterType.EvaluateExpression.ToString(), StringComparison.OrdinalIgnoreCase)) { Logger.Instance.WriteWarning(EventIdentifier.ExpressionEvaluatorEvaluateFunctionDeprecatedFunctionWarning, Messages.ExpressionFunction_DeprecatedFunctionWarning, ParameterType.EvaluateExpression.ToString(), "Resolve Dynamic Grammar capability of UpdateResources activity"); string expression = this.EvaluateExpression(parameterString, mode) as string; // so e.g. IIF(Eq([//Target/Department],"HR"),"Template1","Template2") if (mode != EvaluationMode.Parse && !string.IsNullOrEmpty(expression)) { // now evalaute the actual expression i.e. e.g. IIF(Eq([//Target/Department],"HR"),"Template1","Template2") result = this.EvaluateExpression(expression, mode); } } else { // Evaluate the function to make sure it is properly formatted, // all required parameters are available, and parameters are of the appropriate type // For most functions, the root value is allowed to be null to handle // scenarios when an attribute or expression is resolved to null // In this circumstance, an exception should not be thrown but the function should resolve to null ExpressionFunction expressionFunction = new ExpressionFunction(functionName, parameters, mode); result = expressionFunction.Run(); } return result; } finally { Logger.Instance.WriteMethodExit(EventIdentifier.ExpressionEvaluatorEvaluateFunction, "Function: '{0}'. Evaluation Mode: '{1}'. Returning: '{2}'.", function, mode, result); } }