using System; using System.Collections.Generic; namespace FastReport.AdvMatrix { internal class AggregateExpressionPairList : List { private AdvMatrixObject matrix; public AggregateExpressionPair Find(string aggregateName, string expression) { foreach (AggregateExpressionPair item in this) { if (item.AggregateName == aggregateName && item.Expression == expression) return item; } return null; } public AggregateExpressionPair Add(string aggregateName, string expression) { AggregateExpressionPair result = new AggregateExpressionPair(matrix, aggregateName, expression); Add(result); return result; } public AggregateExpressionPair AddUnique(string aggregateName, string expression) { AggregateExpressionPair result = Find(aggregateName, expression); if (result != null) return result; return Add(aggregateName, expression); } public AggregateExpressionPair AddUnique(AggregateExpressionPair ag) { AggregateExpressionPair result = Find(ag.AggregateName, ag.Expression); if (result != null) return result; Add(ag); return ag; } public AggregateExpressionPairList(AdvMatrixObject matrix) { this.matrix = matrix; } } internal class AggregateExpressionPair { private AdvMatrixObject matrix; private CellDataStorage storage; public string AggregateName { get; private set; } public string Expression { get; private set; } private string ToStringLiteral(string text) { return "\"" + text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; } public string ToAggregateCall() { return matrix.Name + ".Data.Context.GetAggregate(" + ToStringLiteral(AggregateName) + ", " + ToStringLiteral(Expression) + ")"; } public override string ToString() { return AggregateName + "(" + Expression + ")"; } public void ProcessDataRow(int columnIndex, int rowIndex, object value) { storage.AddValue(columnIndex, rowIndex, value); } public object GetValue(int columnIndex, int rowIndex) { object value = storage.GetValue(columnIndex, rowIndex); if (value != null && storage.AggregateType == typeof(UserAggregate)) value = matrix.Report.InvokeMethod(AggregateName, new object[] { value }); return value; } public void Merge(int sourceColumnIndex, int sourceRowIndex, int targetColumnIndex, int targetRowIndex) { storage.Merge(sourceColumnIndex, sourceRowIndex, targetColumnIndex, targetRowIndex); } public void ClearData() { storage.Clear(); } public AggregateExpressionPair(AdvMatrixObject matrix, string aggregateName, string expression) { this.matrix = matrix; AggregateName = aggregateName; Expression = expression; Type aggregateType = Aggregates.Find(aggregateName); if (aggregateType == null) aggregateType = typeof(UserAggregate); storage = new CellDataStorage(aggregateType); } private class CellDataStorage { // array of array: the simplest and most efficient way to store dense matrix data private List> rows; internal Type AggregateType { get; private set; } private AggregateBase GetCell(int columnIndex, int rowIndex, bool create) { if (rowIndex >= rows.Count) { if (!create) return null; // append rows if row index is out of bounds int delta = rowIndex - rows.Count + 1; for (int i = 0; i < delta; i++) { rows.Add(new List()); } } List row = rows[rowIndex]; if (columnIndex >= row.Count) { if (!create) return null; // append columns if column index is out of bounds int delta = columnIndex - row.Count + 1; for (int i = 0; i < delta; i++) { row.Add(null); } } AggregateBase aggregate = row[columnIndex]; if (aggregate == null) { if (!create) return null; // initial state - the cell is empty. Create aggregate instance aggregate = Activator.CreateInstance(AggregateType) as AggregateBase; row[columnIndex] = aggregate; } return aggregate; } public void Clear() { rows.Clear(); } public void AddValue(int columnIndex, int rowIndex, object value) { // do not put null values to the matrix! if (value == null || value == DBNull.Value) return; AggregateBase aggregate = GetCell(columnIndex, rowIndex, true); aggregate.AddValue(value); } public object GetValue(int columnIndex, int rowIndex) { AggregateBase aggregate = GetCell(columnIndex, rowIndex, false); if (aggregate == null) return null; return aggregate.GetValue(); } public void Merge(int sourceColumnIndex, int sourceRowIndex, int targetColumnIndex, int targetRowIndex) { AggregateBase sourceAggr = GetCell(sourceColumnIndex, sourceRowIndex, false); AggregateBase targetAggr = GetCell(targetColumnIndex, targetRowIndex, true); targetAggr.Merge(sourceAggr); } public CellDataStorage(Type aggregateType) { AggregateType = aggregateType; rows = new List>(); } } } }