TopNBuilder.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using System;
  2. using System.Collections.Generic;
  3. namespace FastReport.AdvMatrix
  4. {
  5. internal static class TopNBuilder
  6. {
  7. private static void EnumTopNItems(HeaderData root, bool isColumn, Func<HeaderDataList, bool, int> func)
  8. {
  9. for (int i = 0; i < root.Items.Count; i++)
  10. {
  11. HeaderDataList dl = root.Items[i];
  12. // only serve items with TopN enabled and item count > TopN
  13. if (dl.Descriptor.TopN.Count > 0 && dl.Count > dl.Descriptor.TopN.Count)
  14. {
  15. i = func(dl, isColumn);
  16. }
  17. foreach (HeaderData d in dl)
  18. {
  19. EnumTopNItems(d, isColumn, func);
  20. }
  21. }
  22. }
  23. private static HeaderDataList FindDescriptorInternal(HeaderData root, string name)
  24. {
  25. if (root == null || String.IsNullOrEmpty(name))
  26. return null;
  27. foreach (HeaderDataList dl in root.Items)
  28. {
  29. if (dl.Descriptor.Name == name)
  30. return dl;
  31. // in case we have topN items in parent groups, go deep
  32. foreach (HeaderData d in dl)
  33. {
  34. HeaderDataList result = FindDescriptorInternal(d, name);
  35. if (result != null)
  36. return result;
  37. }
  38. }
  39. return null;
  40. }
  41. private static HeaderDataList FindDescriptor(HeaderData root, string name)
  42. {
  43. HeaderDataList result = FindDescriptorInternal(root, name);
  44. if (result == null)
  45. result = FindDescriptorInternal(root.Parent, name);
  46. return result;
  47. }
  48. private static void MergeAggregates(AdvMatrixObject matrix, HeaderDataList source, HeaderData target, bool isColumn)
  49. {
  50. if (isColumn)
  51. MergeColumnAggregates(matrix, source, target);
  52. else
  53. MergeRowAggregates(matrix, source, target);
  54. }
  55. private static void MergeColumnAggregates(AdvMatrixObject matrix, HeaderDataList source, HeaderData target)
  56. {
  57. foreach (HeaderData rowItem in matrix.Data.Rows.Data.AllTerminalItems)
  58. {
  59. foreach (HeaderData columnItem in source)
  60. {
  61. foreach (AggregateExpressionPair ag in matrix.Data.CellData.SharedAggregates)
  62. {
  63. ag.Merge(columnItem.Index, rowItem.Index, target.Index, rowItem.Index);
  64. }
  65. }
  66. }
  67. }
  68. private static void MergeRowAggregates(AdvMatrixObject matrix, HeaderDataList source, HeaderData target)
  69. {
  70. foreach (HeaderData columnItem in matrix.Data.Columns.Data.AllTerminalItems)
  71. {
  72. foreach (HeaderData rowItem in source)
  73. {
  74. foreach (AggregateExpressionPair ag in matrix.Data.CellData.SharedAggregates)
  75. {
  76. ag.Merge(columnItem.Index, rowItem.Index, columnItem.Index, target.Index);
  77. }
  78. }
  79. }
  80. }
  81. public static void Process(AdvMatrixObject matrix)
  82. {
  83. Func<HeaderDataList, bool, int> func = (topNGroup, isColumn) =>
  84. {
  85. HeaderDescriptor descr = topNGroup.Descriptor;
  86. HeaderData parent = topNGroup.Parent;
  87. MatrixHeader header = isColumn ? matrix.Data.Columns : matrix.Data.Rows;
  88. int index = parent.Items.IndexOf(topNGroup) + 1;
  89. HeaderDataList topNTotal = FindDescriptor(parent, descr.TopN.Total.Name);
  90. HeaderData topNTotalItem = topNTotal != null ? topNTotal[0] : null; // if owner is present, it will have at least one item
  91. HeaderDataList othersGroup = FindDescriptor(parent, descr.TopN.Others.Name);
  92. HeaderDataList othersTotal = FindDescriptor(parent, descr.TopN.OthersTotal.Name);
  93. HeaderData othersTotalItem = othersTotal != null ? othersTotal[0] : null; // same as above
  94. if (topNTotal == null || othersGroup == null || othersTotal == null)
  95. {
  96. // simple mode: use mainItem as a template for everything
  97. // TopN total
  98. topNTotal = new HeaderDataList(parent, descr) { Visible = descr.TopN.Total.Visible };
  99. topNTotalItem = new HeaderData(parent, descr) { Value = descr.TopN.Total.Text, Index = header.NextIndex };
  100. topNTotal.Add(topNTotalItem);
  101. parent.Items.Insert(index, topNTotal);
  102. index++;
  103. // Others
  104. othersGroup = new HeaderDataList(parent, descr) { Visible = descr.TopN.Others.Visible };
  105. parent.Items.Insert(index, othersGroup);
  106. index++;
  107. // Others total
  108. othersTotal = new HeaderDataList(parent, descr) { Visible = descr.TopN.OthersTotal.Visible };
  109. othersTotalItem = new HeaderData(parent, descr) { Value = descr.TopN.OthersTotal.Text, Index = header.NextIndex };
  110. othersTotal.Add(othersTotalItem);
  111. parent.Items.Insert(index, othersTotal);
  112. index++;
  113. }
  114. else
  115. {
  116. topNTotal.Visible = descr.TopN.Total.Visible;
  117. othersGroup.Visible = descr.TopN.Others.Visible;
  118. othersTotal.Visible = descr.TopN.OthersTotal.Visible;
  119. }
  120. // get sorted list of items
  121. List<HeaderData> list;
  122. if (topNGroup.Descriptor.SortAggregate != null)
  123. list = topNGroup.GetSortedList(false);
  124. else
  125. {
  126. list = new List<HeaderData>();
  127. list.AddRange(topNGroup);
  128. }
  129. // leave topN items in the main item, move the rest to others item
  130. topNGroup.Clear();
  131. for (int i = 0; i < descr.TopN.Count; i++)
  132. {
  133. topNGroup.Add(list[i]);
  134. }
  135. othersGroup.Clear();
  136. for (int i = descr.TopN.Count; i < list.Count; i++)
  137. {
  138. othersGroup.Add(list[i]);
  139. }
  140. // calculate aggregates for total items
  141. MergeAggregates(matrix, topNGroup, topNTotalItem, isColumn);
  142. MergeAggregates(matrix, othersGroup, othersTotalItem, isColumn);
  143. return index;
  144. };
  145. EnumTopNItems(matrix.Data.Columns.Data, true, func);
  146. EnumTopNItems(matrix.Data.Rows.Data, false, func);
  147. }
  148. }
  149. }