123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- using System;
- using System.Collections.Generic;
- namespace FastReport.AdvMatrix
- {
- internal static class TopNBuilder
- {
- private static void EnumTopNItems(HeaderData root, bool isColumn, Func<HeaderDataList, bool, int> func)
- {
- for (int i = 0; i < root.Items.Count; i++)
- {
- HeaderDataList dl = root.Items[i];
- // only serve items with TopN enabled and item count > TopN
- if (dl.Descriptor.TopN.Count > 0 && dl.Count > dl.Descriptor.TopN.Count)
- {
- i = func(dl, isColumn);
- }
- foreach (HeaderData d in dl)
- {
- EnumTopNItems(d, isColumn, func);
- }
- }
- }
- private static HeaderDataList FindDescriptorInternal(HeaderData root, string name)
- {
- if (root == null || String.IsNullOrEmpty(name))
- return null;
- foreach (HeaderDataList dl in root.Items)
- {
- if (dl.Descriptor.Name == name)
- return dl;
- // in case we have topN items in parent groups, go deep
- foreach (HeaderData d in dl)
- {
- HeaderDataList result = FindDescriptorInternal(d, name);
- if (result != null)
- return result;
- }
- }
- return null;
- }
- private static HeaderDataList FindDescriptor(HeaderData root, string name)
- {
- HeaderDataList result = FindDescriptorInternal(root, name);
- if (result == null)
- result = FindDescriptorInternal(root.Parent, name);
- return result;
- }
- private static void MergeAggregates(AdvMatrixObject matrix, HeaderDataList source, HeaderData target, bool isColumn)
- {
- if (isColumn)
- MergeColumnAggregates(matrix, source, target);
- else
- MergeRowAggregates(matrix, source, target);
- }
- private static void MergeColumnAggregates(AdvMatrixObject matrix, HeaderDataList source, HeaderData target)
- {
- foreach (HeaderData rowItem in matrix.Data.Rows.Data.AllTerminalItems)
- {
- foreach (HeaderData columnItem in source)
- {
- foreach (AggregateExpressionPair ag in matrix.Data.CellData.SharedAggregates)
- {
- ag.Merge(columnItem.Index, rowItem.Index, target.Index, rowItem.Index);
- }
- }
- }
- }
- private static void MergeRowAggregates(AdvMatrixObject matrix, HeaderDataList source, HeaderData target)
- {
- foreach (HeaderData columnItem in matrix.Data.Columns.Data.AllTerminalItems)
- {
- foreach (HeaderData rowItem in source)
- {
- foreach (AggregateExpressionPair ag in matrix.Data.CellData.SharedAggregates)
- {
- ag.Merge(columnItem.Index, rowItem.Index, columnItem.Index, target.Index);
- }
- }
- }
- }
- public static void Process(AdvMatrixObject matrix)
- {
- Func<HeaderDataList, bool, int> func = (topNGroup, isColumn) =>
- {
- HeaderDescriptor descr = topNGroup.Descriptor;
- HeaderData parent = topNGroup.Parent;
- MatrixHeader header = isColumn ? matrix.Data.Columns : matrix.Data.Rows;
- int index = parent.Items.IndexOf(topNGroup) + 1;
- HeaderDataList topNTotal = FindDescriptor(parent, descr.TopN.Total.Name);
- HeaderData topNTotalItem = topNTotal != null ? topNTotal[0] : null; // if owner is present, it will have at least one item
- HeaderDataList othersGroup = FindDescriptor(parent, descr.TopN.Others.Name);
- HeaderDataList othersTotal = FindDescriptor(parent, descr.TopN.OthersTotal.Name);
- HeaderData othersTotalItem = othersTotal != null ? othersTotal[0] : null; // same as above
- if (topNTotal == null || othersGroup == null || othersTotal == null)
- {
- // simple mode: use mainItem as a template for everything
- // TopN total
- topNTotal = new HeaderDataList(parent, descr) { Visible = descr.TopN.Total.Visible };
- topNTotalItem = new HeaderData(parent, descr) { Value = descr.TopN.Total.Text, Index = header.NextIndex };
- topNTotal.Add(topNTotalItem);
- parent.Items.Insert(index, topNTotal);
- index++;
- // Others
- othersGroup = new HeaderDataList(parent, descr) { Visible = descr.TopN.Others.Visible };
- parent.Items.Insert(index, othersGroup);
- index++;
- // Others total
- othersTotal = new HeaderDataList(parent, descr) { Visible = descr.TopN.OthersTotal.Visible };
- othersTotalItem = new HeaderData(parent, descr) { Value = descr.TopN.OthersTotal.Text, Index = header.NextIndex };
- othersTotal.Add(othersTotalItem);
- parent.Items.Insert(index, othersTotal);
- index++;
- }
- else
- {
- topNTotal.Visible = descr.TopN.Total.Visible;
- othersGroup.Visible = descr.TopN.Others.Visible;
- othersTotal.Visible = descr.TopN.OthersTotal.Visible;
- }
- // get sorted list of items
- List<HeaderData> list;
- if (topNGroup.Descriptor.SortAggregate != null)
- list = topNGroup.GetSortedList(false);
- else
- {
- list = new List<HeaderData>();
- list.AddRange(topNGroup);
- }
- // leave topN items in the main item, move the rest to others item
- topNGroup.Clear();
- for (int i = 0; i < descr.TopN.Count; i++)
- {
- topNGroup.Add(list[i]);
- }
- othersGroup.Clear();
- for (int i = descr.TopN.Count; i < list.Count; i++)
- {
- othersGroup.Add(list[i]);
- }
- // calculate aggregates for total items
- MergeAggregates(matrix, topNGroup, topNTotalItem, isColumn);
- MergeAggregates(matrix, othersGroup, othersTotalItem, isColumn);
- return index;
- };
- EnumTopNItems(matrix.Data.Columns.Data, true, func);
- EnumTopNItems(matrix.Data.Rows.Data, false, func);
- }
- }
- }
|