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