CellData.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using FastReport.Table;
  5. namespace FastReport.AdvMatrix
  6. {
  7. internal class CellData
  8. {
  9. private AdvMatrixObject matrix;
  10. private List<CellDescriptor> descriptors;
  11. private ExpressionCache expressionCache;
  12. private Hashtable processedIndices;
  13. public AggregateExpressionPairList SharedAggregates { get; private set; }
  14. private void BeforeProcess()
  15. {
  16. expressionCache.Clear();
  17. processedIndices.Clear();
  18. }
  19. private void ProcessDataRow(int columnIndex, int rowIndex)
  20. {
  21. // filter out repeated calculation with the same indices. It may occur if a cell has reference to column/row totals,
  22. // and a header has nested items. Repeated calculation will lead to doubling the total values.
  23. long compoundIndex = ((long)columnIndex << 32) + rowIndex;
  24. if (processedIndices.ContainsKey(compoundIndex))
  25. return;
  26. processedIndices[compoundIndex] = 1;
  27. // process all aggregates
  28. foreach (AggregateExpressionPair item in SharedAggregates)
  29. {
  30. item.ProcessDataRow(columnIndex, rowIndex, expressionCache.Calc(item.Expression));
  31. }
  32. }
  33. // searches for existing aggregate and returns it if any; otherwise returns null
  34. public AggregateExpressionPair FindAggregate(AggregateExpressionPair aggr)
  35. {
  36. if (aggr == null)
  37. return null;
  38. foreach (CellDescriptor d in descriptors)
  39. {
  40. AggregateExpressionPair result = d.Aggregates.Find(aggr.AggregateName, aggr.Expression);
  41. if (result != null)
  42. return result;
  43. }
  44. return null;
  45. }
  46. public void InitDescriptors()
  47. {
  48. descriptors.Clear();
  49. ExpressionParser parser = new ExpressionParser(matrix);
  50. SharedAggregates = new AggregateExpressionPairList(matrix);
  51. int offsetX = matrix.Data.Rows.Size;
  52. int offsetY = matrix.Data.Columns.Size;
  53. for (int x = offsetX; x < matrix.Columns.Count; x++)
  54. {
  55. for (int y = offsetY; y < matrix.Rows.Count; y++)
  56. {
  57. TableCell cell = matrix[x, y];
  58. CellDescriptor d = parser.ProcessCell(cell);
  59. descriptors.Add(d);
  60. // share aggregates among cell descriptors
  61. for (int i = 0; i < d.Aggregates.Count; i++)
  62. {
  63. d.Aggregates[i] = SharedAggregates.AddUnique(d.Aggregates[i]);
  64. }
  65. // forbid cell to process its expressions
  66. cell.AllowExpressions = false;
  67. }
  68. }
  69. }
  70. public void GetExpressions(List<string> expressions)
  71. {
  72. foreach (CellDescriptor d in descriptors)
  73. {
  74. d.GetExpressions(expressions);
  75. }
  76. }
  77. public void ClearData()
  78. {
  79. foreach (CellDescriptor descr in descriptors)
  80. {
  81. descr.ClearData();
  82. }
  83. }
  84. public void Init()
  85. {
  86. ClearData();
  87. foreach (CellDescriptor d in descriptors)
  88. {
  89. d.ColumnIndex = matrix.Data.Columns.NextIndex;
  90. d.RowIndex = matrix.Data.Rows.NextIndex;
  91. }
  92. }
  93. public void ProcessDataRow(List<HeaderData> columnItems, List<HeaderData> rowItems)
  94. {
  95. BeforeProcess();
  96. foreach (HeaderData columnItem in columnItems)
  97. {
  98. foreach (HeaderData rowItem in rowItems)
  99. {
  100. if (columnItem.Descriptor.IsTopNItem || rowItem.Descriptor.IsTopNItem)
  101. continue;
  102. TableCell cell = matrix[columnItem.Descriptor.TemplateColumn.Index, rowItem.Descriptor.TemplateRow.Index];
  103. CellDescriptor descr = cell.GetDescriptor();
  104. ProcessDataRow(columnItem.Index, rowItem.Index);
  105. // calculate column total, row total and grand total for the given cell
  106. if (descr.HasColumnTotal)
  107. ProcessDataRow(columnItem.Index, descr.RowIndex);
  108. if (descr.HasRowTotal)
  109. ProcessDataRow(descr.ColumnIndex, rowItem.Index);
  110. if (descr.HasGrandTotal)
  111. ProcessDataRow(descr.ColumnIndex, descr.RowIndex);
  112. }
  113. }
  114. }
  115. public CellData(AdvMatrixObject matrix)
  116. {
  117. this.matrix = matrix;
  118. descriptors = new List<CellDescriptor>();
  119. expressionCache = new ExpressionCache(matrix);
  120. processedIndices = new Hashtable();
  121. }
  122. // expression cache is used to store expression values between ProcessDataRow calls. It saves a lot of time if several cells share the same expression.
  123. private class ExpressionCache
  124. {
  125. private Dictionary<string, object> cache;
  126. private AdvMatrixObject matrix;
  127. public void Clear()
  128. {
  129. cache.Clear();
  130. }
  131. public object Calc(string expression)
  132. {
  133. Report report = matrix.Report;
  134. if (String.IsNullOrEmpty(expression) || report == null)
  135. return null;
  136. object value;
  137. if (!cache.TryGetValue(expression, out value))
  138. {
  139. value = report.Calc(expression);
  140. cache[expression] = value;
  141. }
  142. return value;
  143. }
  144. public ExpressionCache(AdvMatrixObject matrix)
  145. {
  146. cache = new Dictionary<string, object>();
  147. this.matrix = matrix;
  148. }
  149. }
  150. }
  151. }