using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;
using FastReport.Utils;
using System.CodeDom;
using System.ComponentModel;
using System.Collections;
using FastReport.CrossView;
using System.Windows.Forms;
namespace FastReport.Data
{
///
/// This class stores all report data items such as datasources, connections, relations, parameters,
/// system variables.
///
///
/// You can access the report dictionary via Report.Dictionary property.
///
public class Dictionary : Base, IParent
{
#region Fields
private ConnectionCollection connections;
private DataSourceCollection dataSources;
private RelationCollection relations;
private ParameterCollection parameters;
private SystemVariables systemVariables;
private TotalCollection totals;
private CubeSourceCollection cubeSources;
private List registeredItems;
private ObjectCollection cachedAllObjects;
private bool cacheAllObjects;
#endregion
#region Properties
///
/// Gets a collection of connection objects available in a report.
///
public ConnectionCollection Connections
{
get { return connections; }
}
///
/// Gets a collection of datasources available in a report.
///
///
/// Usually you don't need to use this property. It contains only datasources
/// registered using the RegisterData method. All other datasources are contained
/// in connection objects and may be accessed via property.
///
public DataSourceCollection DataSources
{
get { return dataSources; }
}
///
/// Gets a collection of relations.
///
public RelationCollection Relations
{
get { return relations; }
}
///
/// Gets a collection of parameters.
///
///
/// Another way to access parameters is to use the Report.Parameters property
/// which is actually a shortcut to this property. You also may use the Report.GetParameter
/// and Report.GetParameterValue methods.
///
public ParameterCollection Parameters
{
get { return parameters; }
}
///
/// Gets a collection of system variables like Date, PageNofM etc.
///
///
/// Another way to access a system variable is to use the Report.GetVariableValue method.
///
public SystemVariables SystemVariables
{
get { return systemVariables; }
}
///
/// Gets a collection of totals.
///
///
/// Another way to get a total value is to use the Report.GetTotalValue method.
///
public TotalCollection Totals
{
get { return totals; }
}
///
/// Gets a collection of cubesources available in a report.
///
///
/// Usually you don't need to use this property. It contains only cubesources
/// registered using the RegisterData method.
///
public CubeSourceCollection CubeSources
{
get { return cubeSources; }
}
///
/// Gets a list of registered items.
///
///
/// This property is for internal use only.
///
public List RegisteredItems
{
get { return registeredItems; }
}
internal bool CacheAllObjects
{
get { return cacheAllObjects; }
set
{
cacheAllObjects = value;
if (cacheAllObjects)
{
cachedAllObjects = base.AllObjects;
}
else
{
cachedAllObjects = null;
}
}
}
///
public new ObjectCollection AllObjects
{
get
{
if (cachedAllObjects != null)
return cachedAllObjects;
return base.AllObjects;
}
}
#endregion
#region Private Methods
private RegDataItem FindRegisteredItem(object data)
{
foreach (RegDataItem item in registeredItems)
{
if (item.data == data)
return item;
}
return null;
}
private RegDataItem FindRegisteredItem(string name)
{
foreach (RegDataItem item in registeredItems)
{
if (item.name == name)
return item;
}
return null;
}
internal void AddRegisteredItem(object data, string name)
{
if (FindRegisteredItem(data) == null)
registeredItems.Add(new RegDataItem(data, name));
}
#endregion
#region Public Methods
///
public override void Assign(Base source)
{
BaseAssign(source);
}
internal void RegisterDataSet(DataSet data, string referenceName, bool enabled)
{
DictionaryHelper helper = new DictionaryHelper(this, AllObjects, Report.AllNamedObjects);
helper.RegisterDataSet(data, referenceName, enabled);
}
internal void RegisterDataTable(DataTable table, string referenceName, bool enabled)
{
AddRegisteredItem(table, referenceName);
TableDataSource source = FindDataComponent(referenceName) as TableDataSource;
if (source != null)
{
source.Reference = table;
source.InitSchema();
source.RefreshColumns(true);
}
else
{
// check tables inside connections. Are we trying to replace the connection table
// with table provided by an application?
source = FindByAlias(referenceName) as TableDataSource;
// check "Data.TableName" case
if (source == null && referenceName.StartsWith("Data."))
source = FindByAlias(referenceName.Remove(0, 5)) as TableDataSource;
if (source != null && (source.Connection != null || source.IgnoreConnection))
{
source.IgnoreConnection = true;
source.Reference = table;
source.InitSchema();
source.RefreshColumns(true);
}
else
{
source = new TableDataSource();
source.ReferenceName = referenceName;
source.Reference = table;
source.Name = CreateUniqueName(referenceName.Contains(".") ? table.TableName : referenceName);
source.Alias = CreateUniqueAlias(source.Alias);
source.Enabled = enabled;
source.InitSchema();
DataSources.Add(source);
}
}
}
///
/// Registers a DataView.
///
/// The DataView to register.
/// The name of the data object.
/// Determines wheter to enable the object or not.
///
/// This method is for internal use only.
///
public void RegisterDataView(DataView view, string referenceName, bool enabled)
{
AddRegisteredItem(view, referenceName);
ViewDataSource source = FindDataComponent(referenceName) as ViewDataSource;
if (source != null)
{
source.Reference = view;
source.InitSchema();
source.RefreshColumns();
}
else
{
source = new ViewDataSource();
source.ReferenceName = referenceName;
source.Reference = view;
source.Name = CreateUniqueName(referenceName);
source.Alias = CreateUniqueAlias(source.Alias);
source.Enabled = enabled;
source.InitSchema();
DataSources.Add(source);
}
}
internal void RegisterDataRelation(DataRelation relation, string referenceName, bool enabled)
{
AddRegisteredItem(relation, referenceName);
if (FindDataComponent(referenceName) != null)
return;
Relation rel = new Relation();
rel.ReferenceName = referenceName;
rel.Reference = relation;
rel.Name = relation.RelationName;
rel.Enabled = enabled;
rel.ParentDataSource = FindDataTableSource(relation.ParentTable);
rel.ChildDataSource = FindDataTableSource(relation.ChildTable);
string[] parentColumns = new string[relation.ParentColumns.Length];
string[] childColumns = new string[relation.ChildColumns.Length];
for (int i = 0; i < relation.ParentColumns.Length; i++)
{
parentColumns[i] = relation.ParentColumns[i].Caption;
}
for (int i = 0; i < relation.ChildColumns.Length; i++)
{
childColumns[i] = relation.ChildColumns[i].Caption;
}
rel.ParentColumns = parentColumns;
rel.ChildColumns = childColumns;
Relations.Add(rel);
}
///
/// Registers a business object.
///
/// The business object.
/// The name of the object.
/// Maximum level of data nesting.
/// Determines wheter to enable the object or not.
///
/// This method is for internal use only.
///
public void RegisterBusinessObject(IEnumerable data, string referenceName, int maxNestingLevel, bool enabled)
{
AddRegisteredItem(data, referenceName);
Type dataType = data.GetType();
if (data is BindingSource)
{
if ((data as BindingSource).DataSource is Type)
dataType = ((data as BindingSource).DataSource as Type);
else
dataType = (data as BindingSource).DataSource.GetType();
}
BusinessObjectConverter converter = new BusinessObjectConverter(this);
BusinessObjectDataSource source = FindDataComponent(referenceName) as BusinessObjectDataSource;
if (source != null)
{
source.Reference = data;
source.DataType = dataType;
converter.UpdateExistingObjects(source, maxNestingLevel);
}
else
{
source = new BusinessObjectDataSource();
source.ReferenceName = referenceName;
source.Reference = data;
source.DataType = dataType;
source.Name = CreateUniqueName(referenceName);
source.Alias = CreateUniqueAlias(source.Alias);
source.Enabled = enabled;
DataSources.Add(source);
converter.CreateInitialObjects(source, maxNestingLevel);
}
}
///
/// Registers a CubeLink.
///
/// The CubeLink to register.
/// The name of the data object.
/// Determines wheter to enable the object or not.
///
/// This method is for internal use only.
///
public void RegisterCubeLink(IBaseCubeLink cubeLink, string referenceName, bool enabled)
{
AddRegisteredItem(cubeLink, referenceName);
CubeSourceBase source = FindDataComponent(referenceName) as CubeSourceBase;
if (source != null)
{
source.Reference = cubeLink;
}
else
{
source = new SliceCubeSource();
source.ReferenceName = referenceName;
source.Reference = cubeLink;
source.Name = CreateUniqueName(referenceName);
source.Alias = CreateUniqueAlias(source.Alias);
source.Enabled = enabled;
CubeSources.Add(source);
}
}
///
/// Registers a data object.
///
/// The object to register.
/// The name of the object.
/// Determines wheter to enable the object or not.
///
/// This method is for internal use only.
///
public void RegisterData(object data, string name, bool enabled)
{
if (data is DataSet)
{
RegisterDataSet(data as DataSet, name, enabled);
}
else if (data is DataTable)
{
RegisterDataTable(data as DataTable, name, enabled);
}
else if (data is DataView)
{
RegisterDataView(data as DataView, name, enabled);
}
else if (data is DataRelation)
{
RegisterDataRelation(data as DataRelation, name, enabled);
}
else if (data is IEnumerable)
{
RegisterBusinessObject(data as IEnumerable, name, 1, enabled);
}
else if (data is IBaseCubeLink)
{
RegisterCubeLink(data as IBaseCubeLink, name, enabled);
}
}
///
/// Unregisters the previously registered data.
///
/// The application data.
public void UnregisterData(object data)
{
UnregisterData(data, "Data");
}
///
/// Unregisters the previously registered data.
///
/// The application data.
/// The name of the data.
///
/// You must specify the same data and name as when you call RegisterData.
///
public void UnregisterData(object data, string name)
{
for (int i = 0; i < registeredItems.Count; i++)
{
RegDataItem item = registeredItems[i];
if (item.name == name)
{
registeredItems.RemoveAt(i);
break;
}
}
DataComponentBase comp = FindDataComponent(name);
if (comp != null)
comp.Dispose();
if (data is DataSet)
{
foreach (DataTable table in (data as DataSet).Tables)
{
UnregisterData(table, name + "." + table.TableName);
}
foreach (DataRelation relation in (data as DataSet).Relations)
{
UnregisterData(relation, name + "." + relation.RelationName);
}
}
}
///
/// Re-registers the data registered before.
///
///
/// This method is for internal use only.
///
public void ReRegisterData()
{
ReRegisterData(this);
}
///
/// Re-registers the data registered before.
///
///
public void ReRegisterData(Dictionary dictionary)
{
// re-register all data items. It is needed after load or "new report" operations
if (registeredItems.Count > 0)
{
DictionaryHelper helper = new DictionaryHelper(dictionary, dictionary.AllObjects, dictionary.Report.AllNamedObjects);
helper.ReRegisterData(registeredItems, false);
}
}
///
/// Clears all registered data.
///
public void ClearRegisteredData()
{
registeredItems.Clear();
}
///
/// Enables or disables relations between data tables.
///
///
/// Call this method if you create master-detail report from code. This method enables
/// relation between two data tables which Enabled flag is set to true. Relations
/// whose parent and child tables are disabled, gets disabled too.
///
public void UpdateRelations()
{
foreach (Relation relation in Relations)
{
relation.Enabled = relation.ParentDataSource != null && relation.ParentDataSource.Enabled &&
relation.ChildDataSource != null && relation.ChildDataSource.Enabled;
}
}
///
/// Creates unique name for data item such as connection, datasource, relation, parameter or total.
///
/// The base name.
/// The new unique name.
///
/// Use this method to create unique name of the data item. It is necessary when you create new
/// items in code to avoid conflicts with existing report items.
/// This example show how to add a new parameter:
///
/// Report report1;
/// Parameter par = new Parameter();
/// par.Name = report1.Dictionary.CreateUniqueName("Parameter");
/// report1.Parameters.Add(par);
///
///
///
public string CreateUniqueName(string name)
{
string baseName = name;
int i = 1;
while (FindByName(name) != null || Report.FindObject(name) != null)
{
name = baseName + i.ToString();
i++;
}
return name;
}
///
/// Creates unique alias for data item such as connection, datasource or relation.
///
/// The base alias.
/// The new unique alias.
///
/// Use this method to create unique alias of the data item. It is necessary when you create new
/// items in code to avoid conflicts with existing report items.
/// This example show how to add a new table:
///
/// Report report1;
/// DataConnectionBase conn = report1.Dictionary.Connections.FindByName("Connection1");
/// TableDataSource table = new TableDataSource();
/// table.TableName = "Employees";
/// table.Name = report1.Dictionary.CreateUniqueName("EmployeesTable");
/// table.Alias = report1.Dictionary.CreateUniqueAlias("Employees");
/// conn.Tables.Add(table);
///
///
///
public string CreateUniqueAlias(string alias)
{
string baseAlias = alias;
int i = 1;
while (FindByAlias(alias) != null)
{
alias = baseAlias + i.ToString();
i++;
}
return alias;
}
///
/// Finds a data item such as connection, datasource, relation, parameter or total by its name.
///
/// The item's name.
/// The data item if found; otherwise, null.
public Base FindByName(string name)
{
foreach (Base c in AllObjects)
{
if (c is DataConnectionBase || c is DataSourceBase || c is Relation ||
(c is Parameter && c.Parent == this) || c is Total || c is CubeSourceBase)
{
// check complete match or match without case sensitivity
if (name == c.Name || name.ToLower() == c.Name.ToLower())
return c;
}
}
return null;
}
///
/// Finds a data item such as connection, datasource or relation by its alias.
///
/// The item's alias.
/// The data item if found; otherwise, null.
public DataComponentBase FindByAlias(string alias)
{
foreach (Base c in AllObjects)
{
if (c is DataConnectionBase || c is Relation)
{
// check complete match or match without case sensitivity
if (alias == (c as DataComponentBase).Alias || alias.ToLower() == (c as DataComponentBase).Alias.ToLower())
return c as DataComponentBase;
}
else if (c is DataSourceBase)
{
// check complete match or match without case sensitivity
if (alias == (c as DataSourceBase).FullName || alias.ToLower() == (c as DataSourceBase).FullName.ToLower())
return c as DataSourceBase;
}
}
return null;
}
///
/// Finds a datasource that matches the specified DataTable.
///
/// The DataTable object to check.
/// The DataSourceBase object if found.
///
/// This method is for internal use only.
///
public DataSourceBase FindDataTableSource(DataTable table)
{
foreach (DataSourceBase c in DataSources)
{
if (c is TableDataSource && c.Reference == table)
return c;
}
return null;
}
///
/// Finds a data component that matches the specified reference name.
///
/// The name to check.
/// The DataComponentBase object if found.
///
/// This method is for internal use only.
///
public DataComponentBase FindDataComponent(string referenceName)
{
foreach (Base c in AllObjects)
{
DataComponentBase data = c as DataComponentBase;
if (data != null && data.ReferenceName == referenceName)
return data;
}
return null;
}
///
public override void Serialize(FRWriter writer)
{
writer.ItemName = ClassName;
ObjectCollection childObjects = ChildObjects;
foreach (Base c in childObjects)
{
if (c is Parameter || c is Total || c is CubeSourceBase || (c is DataComponentBase && (c as DataComponentBase).Enabled))
writer.Write(c);
}
}
///
public override void Deserialize(FRReader reader)
{
base.Deserialize(reader);
ReRegisterData();
}
///
/// Saves the dictionary to a stream.
///
/// Stream to save to.
public void Save(Stream stream)
{
using (FRWriter writer = new FRWriter())
{
writer.Write(this);
writer.Save(stream);
}
}
///
/// Saves the dictionary to a file.
///
/// The name of a file to save to.
public void Save(string fileName)
{
using (FileStream f = new FileStream(fileName, FileMode.Create))
{
Save(f);
}
}
///
/// Loads the dictionary from a stream.
///
/// The stream to load from.
public void Load(Stream stream)
{
Clear();
using (FRReader reader = new FRReader(Report))
{
reader.Load(stream);
reader.Read(this);
}
}
///
/// Loads the dictionary from a file.
///
/// The name of a file to load from.
public void Load(string fileName)
{
using (FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Load(f);
}
}
///
/// Merges this dictionary with another Dictionary.
///
/// Another dictionary to merge the data from.
public void Merge(Dictionary source)
{
// Report object is needed to handle save/load of dictionary correctly.
// Some dictionary objects (such as relations) may contain references to other objects.
// In order to clone them correctly, we need a parent Report object, because
// reader uses it to fixup references.
using (Report cloneReport = new Report())
{
Dictionary clone = cloneReport.Dictionary;
clone.AssignAll(source, true);
foreach (Base c in clone.ChildObjects)
{
Base my = FindByName(c.Name);
if (my != null)
my.Dispose();
c.Parent = this;
}
source.ReRegisterData(this);
ReRegisterData();
}
}
#endregion
#region IParent Members
///
public bool CanContain(Base child)
{
return child is DataConnectionBase || child is DataSourceBase || child is Relation || child is Parameter ||
child is Total || child is CubeSourceBase;
}
///
public void GetChildObjects(ObjectCollection list)
{
foreach (DataConnectionBase c in Connections)
{
list.Add(c);
}
foreach (DataSourceBase c in DataSources)
{
list.Add(c);
}
foreach (Relation c in Relations)
{
list.Add(c);
}
foreach (Parameter c in Parameters)
{
list.Add(c);
}
foreach (Total c in Totals)
{
list.Add(c);
}
foreach (CubeSourceBase c in CubeSources)
{
list.Add(c);
}
}
///
public void AddChild(Base child)
{
if (child is DataConnectionBase)
Connections.Add(child as DataConnectionBase);
else if (child is DataSourceBase)
DataSources.Add(child as DataSourceBase);
else if (child is Relation)
Relations.Add(child as Relation);
else if (child is Parameter)
Parameters.Add(child as Parameter);
else if (child is Total)
Totals.Add(child as Total);
else if (child is CubeSourceBase)
CubeSources.Add(child as CubeSourceBase);
}
///
public void RemoveChild(Base child)
{
if (child is DataConnectionBase)
Connections.Remove(child as DataConnectionBase);
else if (child is DataSourceBase)
DataSources.Remove(child as DataSourceBase);
else if (child is Relation)
Relations.Remove(child as Relation);
else if (child is Parameter)
Parameters.Remove(child as Parameter);
else if (child is Total)
Totals.Remove(child as Total);
else if (child is CubeSourceBase)
CubeSources.Remove(child as CubeSourceBase);
}
///
public int GetChildOrder(Base child)
{
return 0;
}
///
public void SetChildOrder(Base child, int order)
{
// do nothing
}
///
public void UpdateLayout(float dx, float dy)
{
// do nothing
}
#endregion
///
/// Initializes a new instance of the class with default settings.
///
public Dictionary()
{
connections = new ConnectionCollection(this);
dataSources = new DataSourceCollection(this);
relations = new RelationCollection(this);
parameters = new ParameterCollection(this);
systemVariables = new SystemVariables(this);
totals = new TotalCollection(this);
cubeSources = new CubeSourceCollection(this);
registeredItems = new List();
}
///
/// Represents the item registered in a dictionary.
///
public class RegDataItem
{
///
/// Gets the item data.
///
public object data;
///
/// Gets the item name.
///
public string name;
internal RegDataItem(object data, string name)
{
this.data = data;
this.name = name;
}
}
}
}