using System; using System.Collections; using System.Collections.Generic; using FastReport.Table; namespace FastReport.AdvMatrix { internal class CellData { private AdvMatrixObject matrix; private List descriptors; private ExpressionCache expressionCache; private Hashtable processedIndices; public AggregateExpressionPairList SharedAggregates { get; private set; } private void BeforeProcess() { expressionCache.Clear(); processedIndices.Clear(); } private void ProcessDataRow(int columnIndex, int rowIndex) { // filter out repeated calculation with the same indices. It may occur if a cell has reference to column/row totals, // and a header has nested items. Repeated calculation will lead to doubling the total values. long compoundIndex = ((long)columnIndex << 32) + rowIndex; if (processedIndices.ContainsKey(compoundIndex)) return; processedIndices[compoundIndex] = 1; // process all aggregates foreach (AggregateExpressionPair item in SharedAggregates) { item.ProcessDataRow(columnIndex, rowIndex, expressionCache.Calc(item.Expression)); } } // searches for existing aggregate and returns it if any; otherwise returns null public AggregateExpressionPair FindAggregate(AggregateExpressionPair aggr) { if (aggr == null) return null; foreach (CellDescriptor d in descriptors) { AggregateExpressionPair result = d.Aggregates.Find(aggr.AggregateName, aggr.Expression); if (result != null) return result; } return null; } public void InitDescriptors() { descriptors.Clear(); ExpressionParser parser = new ExpressionParser(matrix); SharedAggregates = new AggregateExpressionPairList(matrix); int offsetX = matrix.Data.Rows.Size; int offsetY = matrix.Data.Columns.Size; for (int x = offsetX; x < matrix.Columns.Count; x++) { for (int y = offsetY; y < matrix.Rows.Count; y++) { TableCell cell = matrix[x, y]; CellDescriptor d = parser.ProcessCell(cell); descriptors.Add(d); // share aggregates among cell descriptors for (int i = 0; i < d.Aggregates.Count; i++) { d.Aggregates[i] = SharedAggregates.AddUnique(d.Aggregates[i]); } // forbid cell to process its expressions cell.AllowExpressions = false; } } } public void GetExpressions(List expressions) { foreach (CellDescriptor d in descriptors) { d.GetExpressions(expressions); } } public void ClearData() { foreach (CellDescriptor descr in descriptors) { descr.ClearData(); } } public void Init() { ClearData(); foreach (CellDescriptor d in descriptors) { d.ColumnIndex = matrix.Data.Columns.NextIndex; d.RowIndex = matrix.Data.Rows.NextIndex; } } public void ProcessDataRow(List columnItems, List rowItems) { BeforeProcess(); foreach (HeaderData columnItem in columnItems) { foreach (HeaderData rowItem in rowItems) { if (columnItem.Descriptor.IsTopNItem || rowItem.Descriptor.IsTopNItem) continue; TableCell cell = matrix[columnItem.Descriptor.TemplateColumn.Index, rowItem.Descriptor.TemplateRow.Index]; CellDescriptor descr = cell.GetDescriptor(); ProcessDataRow(columnItem.Index, rowItem.Index); // calculate column total, row total and grand total for the given cell if (descr.HasColumnTotal) ProcessDataRow(columnItem.Index, descr.RowIndex); if (descr.HasRowTotal) ProcessDataRow(descr.ColumnIndex, rowItem.Index); if (descr.HasGrandTotal) ProcessDataRow(descr.ColumnIndex, descr.RowIndex); } } } public CellData(AdvMatrixObject matrix) { this.matrix = matrix; descriptors = new List(); expressionCache = new ExpressionCache(matrix); processedIndices = new Hashtable(); } // expression cache is used to store expression values between ProcessDataRow calls. It saves a lot of time if several cells share the same expression. private class ExpressionCache { private Dictionary cache; private AdvMatrixObject matrix; public void Clear() { cache.Clear(); } public object Calc(string expression) { Report report = matrix.Report; if (String.IsNullOrEmpty(expression) || report == null) return null; object value; if (!cache.TryGetValue(expression, out value)) { value = report.Calc(expression); cache[expression] = value; } return value; } public ExpressionCache(AdvMatrixObject matrix) { cache = new Dictionary(); this.matrix = matrix; } } } }