using System; using System.Collections.Generic; using FastReport.Code; using FastReport.Utils; using FastReport.Table; namespace FastReport.AdvMatrix { internal class ExpressionParser { private AdvMatrixObject matrix; internal static readonly string[] SpecialFunctionNames = { "ColumnTotal", "RowTotal", "GrandTotal", "GrandColumnTotal", "GrandRowTotal", "SpecificColumn", "SpecificRow", "FirstColumn", "FirstRow", "LastColumn", "LastRow", "PreviousColumn", "PreviousRow", "NextColumn", "NextRow", "ColumnMaxValue", "ColumnMinValue", "RowMaxValue", "RowMinValue", "PercentOfColumnTotal", "PercentOfRowTotal", "PercentOfGrandTotal", "PercentOfPreviousColumn", "PercentOfPreviousRow" }; private bool IsAggregate(string ident) { return Aggregates.Find(ident) != null || ident.StartsWith("_"); } private bool IsSpecialFunction(string ident) { foreach (string s in SpecialFunctionNames) { if (s == ident) return true; } return false; } private string ToSpecialFunctionCall(string name) { return matrix.Name + ".Data.Context.Get" + name; } // parse expression: // - search for aggregates and collect them in the aggregates list // - replace aggregate calls: AggrName(expression) -> MatrixName.Data.Context.GetAggregate("AggrName", "expression") // - search for special functions like "PreviousColumn(...)" and collect them in the specialFunctions list // - replace special function calls: PreviousColumn() -> MatrixName.Data.Context.GetPreviousColumn() private string Parse(string expression, AggregateExpressionPairList aggregates, List specialFunctions) { FindTextArgs args = new FindTextArgs(); args.Text = new FastString(expression); args.OpenBracket = "("; args.CloseBracket = ")"; // search for method calls while (args.StartIndex < args.Text.Length) { if (CodeUtils.GetExpression(args, false) == null) break; // get identifier at the left of ( int i = args.StartIndex; while (i > 0) { char c = args.Text[i - 1]; if (!(char.IsLetterOrDigit(c) || c == '_')) break; i--; } string ident = args.Text.Substring(i, args.StartIndex - i); // if ident has preceding . symbol, ignore it if (i > 0 && args.Text[i - 1] == '.') ident = ""; if (IsAggregate(ident)) { // ident is either one of predefined aggregates or a user aggregate string newText = aggregates.AddUnique(ident, args.FoundText).ToAggregateCall(); args.Text.Remove(i, args.EndIndex - i); args.Text.Insert(i, newText); args.StartIndex = i + newText.Length; } else if (IsSpecialFunction(ident)) { // ident is a special function like PreviousColumn(...) if (specialFunctions != null) specialFunctions.Add(ident); string newText = ToSpecialFunctionCall(ident); args.Text.Remove(i, ident.Length); args.Text.Insert(i, newText); args.StartIndex = i + newText.Length; } else { string newText = "(" + Parse(args.FoundText, aggregates, specialFunctions) + ")"; args.Text.Remove(args.StartIndex, args.EndIndex - args.StartIndex); args.Text.Insert(args.StartIndex, newText); args.StartIndex += newText.Length; } } return args.Text.ToString(); } // parse expression with single aggregate public AggregateExpressionPair Parse(string expression) { AggregateExpressionPairList list = new AggregateExpressionPairList(matrix); Parse(expression, list, null); if (list.Count == 1) return list[0]; return null; } public CellDescriptor ProcessCell(TableCell cell) { // create cell descriptor and attach it to a cell CellDescriptor cellDescriptor = new CellDescriptor(matrix, cell); string text = cell.Text; string[] brackets = cell.Brackets.Split(','); if (String.IsNullOrEmpty(text) || brackets.Length != 2) return cellDescriptor; // process all expressions in the cell's text FindTextArgs args = new FindTextArgs(); args.Text = new FastString(text); args.OpenBracket = brackets[0]; args.CloseBracket = brackets[1]; while (args.StartIndex < args.Text.Length) { string expression = CodeUtils.GetExpression(args, false); if (expression == null) break; expression = Parse(expression, cellDescriptor.Aggregates, cellDescriptor.SpecialFunctions); cellDescriptor.Expressions.Add(expression); expression = brackets[0] + expression + brackets[1]; args.Text.Remove(args.StartIndex, args.EndIndex - args.StartIndex); args.Text.Insert(args.StartIndex, expression); args.StartIndex += expression.Length; } // set the new text to the cell descriptor cellDescriptor.Text = args.Text.ToString(); cellDescriptor.UpdateContentType(); return cellDescriptor; } public ExpressionParser(AdvMatrixObject matrix) { this.matrix = matrix; } } }