using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing.Design; using FastReport.Utils; namespace FastReport.Data { /// /// Specifies the total type. /// public enum TotalType { /// /// The total returns sum of values. /// Sum, /// /// The total returns minimal value. /// Min, /// /// The total returns maximal value. /// Max, /// /// The total returns average value. /// Avg, /// /// The total returns number of values. /// Count, /// /// The total returns number of distinct values. /// CountDistinct } /// /// Represents a total that is used to calculate aggregates such as Sum, Min, Max, Avg, Count. /// public partial class Total : Base { #region Fields private TotalType totalType; private string expression; private DataBand evaluator; private BandBase printOn; private string evaluateCondition; private bool includeInvisibleRows; private bool resetAfterPrint; private bool resetOnReprint; // engine private object value; private int count; private bool keeping; private Total keepTotal; private TotalCollection subTotals; private TotalCollection parentTotal; private const string subPrefix = "_sub"; private Hashtable distinctValues; #endregion #region Properties /// /// Gets or sets the total type. /// [DefaultValue(TotalType.Sum)] [Category("Data")] public TotalType TotalType { get { return totalType; } set { totalType = value; } } /// /// Gets or sets the expression used to calculate the total. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string Expression { get { return expression; } set { expression = value; } } /// /// Gets or sets the evaluator databand. /// /// /// The total will be calculated for each row of this band. /// [Category("Data")] public DataBand Evaluator { get { return evaluator; } set { evaluator = value; } } /// /// This property is kept for compatibility only. /// [Category("Data")] [Browsable(false)] public BandBase Resetter { get { return printOn; } set { printOn = value; } } /// /// Gets or sets the band to print the total on. /// /// /// The total will be resetted after the specified band has been printed. /// [Category("Data")] public BandBase PrintOn { get { return printOn; } set { printOn = value; } } /// /// Gets or sets a value that determines whether the total should be resetted after print. /// [DefaultValue(true)] [Category("Behavior")] public bool ResetAfterPrint { get { return resetAfterPrint; } set { resetAfterPrint = value; } } /// /// Gets or sets a value that determines whether the total should be resetted if printed /// on repeated band (i.e. band with "RepeatOnEveryPage" flag). /// [DefaultValue(true)] [Category("Behavior")] public bool ResetOnReprint { get { return resetOnReprint; } set { resetOnReprint = value; } } /// /// Gets or sets the condition which tells the total to evaluate. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string EvaluateCondition { get { return evaluateCondition; } set { evaluateCondition = value; } } /// /// Gets or sets a value that determines if invisible rows of the Evaluator should /// be included into the total's value. /// [DefaultValue(false)] [Category("Behavior")] public bool IncludeInvisibleRows { get { return includeInvisibleRows; } set { includeInvisibleRows = value; } } /// /// This property is not relevant to this class. /// [Browsable(false)] public new Restrictions Restrictions { get { return base.Restrictions; } set { base.Restrictions = value; } } /// /// Gets the value of total. /// [Browsable(false)] public object Value { get { return GetValue(); } } private bool IsPageFooter { get { return PrintOn is PageFooterBand || PrintOn is ColumnFooterBand || ((PrintOn is HeaderFooterBandBase) && (PrintOn as HeaderFooterBandBase).RepeatOnEveryPage); } } private bool IsInsideHierarchy { get { return Report.Engine.HierarchyLevel > 1 && !Name.StartsWith(subPrefix) && PrintOn != null && PrintOn.ParentDataBand != null && PrintOn.ParentDataBand.IsHierarchical; } } #endregion #region Private Methods private object GetValue() { if (IsInsideHierarchy) { Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); return subTotal.Value; } if (TotalType == TotalType.Avg) { if (value == null || count == 0) return null; return new Variant(value) / count; } else if (TotalType == TotalType.Count) return count; else if (TotalType == TotalType.CountDistinct) return distinctValues.Keys.Count; return value; } private void AddValue(object value) { if (value == null || value is DBNull) return; if (TotalType == TotalType.CountDistinct) { distinctValues[value] = 1; return; } if (this.value == null) this.value = value; else { switch (TotalType) { case TotalType.Sum: case TotalType.Avg: this.value = (new Variant(this.value) + new Variant(value)).Value; break; case TotalType.Min: IComparable val1 = this.value as IComparable; IComparable val2 = value as IComparable; if (val1 != null && val2 != null && val1.CompareTo(val2) > 0) this.value = value; break; case TotalType.Max: val1 = this.value as IComparable; val2 = value as IComparable; if (val1 != null && val2 != null && val1.CompareTo(val2) < 0) this.value = value; break; } } } private Total FindSubTotal(string name) { Total result = subTotals.FindByName(name); if (result == null) { result = this.Clone(); result.Name = name; subTotals.Add(result); } return result; } #endregion #region Public Methods /// public override void Assign(Base source) { BaseAssign(source); } /// public override void Serialize(FRWriter writer) { Total c = writer.DiffObject as Total; base.Serialize(writer); if (TotalType != c.TotalType) writer.WriteValue("TotalType", TotalType); if (Expression != c.Expression) writer.WriteStr("Expression", Expression); if (Evaluator != c.Evaluator) writer.WriteRef("Evaluator", Evaluator); if (PrintOn != c.PrintOn) writer.WriteRef("PrintOn", PrintOn); if (ResetAfterPrint != c.ResetAfterPrint) writer.WriteBool("ResetAfterPrint", ResetAfterPrint); if (ResetOnReprint != c.ResetOnReprint) writer.WriteBool("ResetOnReprint", ResetOnReprint); if (EvaluateCondition != c.EvaluateCondition) writer.WriteStr("EvaluateCondition", EvaluateCondition); if (IncludeInvisibleRows != c.IncludeInvisibleRows) writer.WriteBool("IncludeInvisibleRows", IncludeInvisibleRows); } internal Total Clone() { Total total = new Total(); total.SetReport(Report); total.TotalType = TotalType; total.Expression = Expression; total.Evaluator = Evaluator; total.PrintOn = PrintOn; total.ResetAfterPrint = ResetAfterPrint; total.ResetOnReprint = ResetOnReprint; total.EvaluateCondition = EvaluateCondition; total.IncludeInvisibleRows = IncludeInvisibleRows; return total; } #endregion #region Report Engine /// public override string[] GetExpressions() { List expressions = new List(); if (!String.IsNullOrEmpty(Expression)) expressions.Add(Expression); if (!String.IsNullOrEmpty(EvaluateCondition)) expressions.Add(EvaluateCondition); return expressions.ToArray(); } /// public override void Clear() { base.Clear(); value = null; count = 0; distinctValues.Clear(); } internal void AddValue() { if (IsInsideHierarchy) { Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); subTotal.AddValue(); return; } if (!Evaluator.Visible && !IncludeInvisibleRows) return; if (!String.IsNullOrEmpty(EvaluateCondition) && (bool)Report.Calc(EvaluateCondition) == false) return; if (TotalType != TotalType.Count && String.IsNullOrEmpty(Expression)) return; if (keeping) { keepTotal.AddValue(); return; } // if Total refers to another total Total total = IsRefersToTotal(); if (total != null) { if (!total.parentTotal.Contains(this)) { total.parentTotal.Add(this); } return; } object value = TotalType == TotalType.Count ? null : Report.Calc(Expression); AddValue(value); if (TotalType != TotalType.Avg || (value != null && !(value is DBNull))) count++; } internal void ResetValue() { if (IsInsideHierarchy) { Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); subTotal.ResetValue(); return; } Clear(); } internal void ExecuteTotal(object val) { foreach (Total total in parentTotal) total.Execute(val); } private void Execute(object val) { AddValue(val); if (value != null && !(value is DBNull)) count++; } private Total IsRefersToTotal() { string expr = Expression; if (expr.StartsWith("[") && expr.EndsWith("]")) expr = expr.Substring(1, expr.Length - 2); return Report.Dictionary.Totals.FindByName(expr); } internal void StartKeep() { if (!IsPageFooter || keeping) return; keeping = true; keepTotal = Clone(); } internal void EndKeep() { if (!IsPageFooter || !keeping) return; keeping = false; if (TotalType == TotalType.CountDistinct) { foreach (object key in keepTotal.distinctValues) { distinctValues[key] = 1; } } else { AddValue(keepTotal.value); count += keepTotal.count; } } #endregion /// /// Initializes a new instance of the class with default settings. /// public Total() { expression = ""; evaluateCondition = ""; resetAfterPrint = true; subTotals = new TotalCollection(null); parentTotal = new TotalCollection(null); distinctValues = new Hashtable(); SetFlags(Flags.CanCopy, false); } } }