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);
}
}
}