using System; using System.Collections.Generic; namespace FastReport.AdvMatrix { internal static class TopNBuilder { private static void EnumTopNItems(HeaderData root, bool isColumn, Func 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 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 list; if (topNGroup.Descriptor.SortAggregate != null) list = topNGroup.GetSortedList(false); else { list = new List(); 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); } } }