DataSourceBase.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.ComponentModel;
  5. using System.Collections;
  6. using FastReport.Utils;
  7. using System.Drawing.Design;
  8. namespace FastReport.Data
  9. {
  10. /// <summary>
  11. /// Base class for all datasources such as <see cref="TableDataSource"/>.
  12. /// </summary>
  13. [TypeConverter(typeof(FastReport.TypeConverters.DataSourceConverter))]
  14. [Editor("FastReport.TypeEditors.DataSourceEditor, FastReport", typeof(UITypeEditor))]
  15. public abstract class DataSourceBase : Column
  16. {
  17. #region Fields
  18. private ArrayList internalRows;
  19. private ArrayList rows;
  20. private int currentRowNo;
  21. protected object currentRow;
  22. private Hashtable additionalFilter;
  23. private bool forceLoadData;
  24. private Hashtable columnIndices;
  25. private Hashtable rowIndices;
  26. private Hashtable relation_SortedChildRows;
  27. private static bool FShowAccessDataMessage;
  28. #endregion
  29. #region Properties
  30. /// <summary>
  31. /// Occurs when the FastReport engine loads data source with data.
  32. /// </summary>
  33. /// <remarks>
  34. /// Use this event if you want to implement load-on-demand. Event handler must load the data
  35. /// into the data object which this datasource is bound to (for example, the
  36. /// <b>TableDataSource</b> uses data from the <b>DataTable</b> object bound to
  37. /// the <b>Table</b> property).
  38. /// </remarks>
  39. public event EventHandler Load;
  40. /// <summary>
  41. /// Gets or sets alias of this object.
  42. /// </summary>
  43. /// <remarks>
  44. /// Alias is a human-friendly name of this object. It may contain any symbols (including
  45. /// spaces and national symbols).
  46. /// </remarks>
  47. [Category("Design")]
  48. public new string Alias
  49. {
  50. get { return base.Alias; }
  51. set
  52. {
  53. UpdateExpressions(base.Alias, value);
  54. base.Alias = value;
  55. }
  56. }
  57. /// <summary>
  58. /// Gets a number of data rows in this datasource.
  59. /// </summary>
  60. /// <remarks>
  61. /// You should initialize the datasource by the <b>Init</b> method before using this property.
  62. /// </remarks>
  63. [Browsable(false)]
  64. public int RowCount
  65. {
  66. get { return rows.Count; }
  67. }
  68. /// <summary>
  69. /// Gets a value indicating that datasource has more rows, that is the <see cref="CurrentRowNo"/>
  70. /// is less than the <see cref="RowCount"/>.
  71. /// </summary>
  72. /// <remarks>
  73. /// <para>You should initialize the datasource by the <b>Init</b> method before using this property.</para>
  74. /// <para>Usually this property is used with the following code block:</para>
  75. /// <code>
  76. /// dataSource.Init();
  77. /// while (dataSource.HasMoreRows)
  78. /// {
  79. /// // do something...
  80. /// dataSource.Next();
  81. /// }
  82. /// </code>
  83. /// </remarks>
  84. [Browsable(false)]
  85. public bool HasMoreRows
  86. {
  87. get { return CurrentRowNo < RowCount; }
  88. }
  89. /// <summary>
  90. /// Gets the current data row.
  91. /// </summary>
  92. /// <remarks>
  93. /// <para>This property is updated when you call the <see cref="Next"/> method.</para>
  94. /// </remarks>
  95. [Browsable(false)]
  96. public object CurrentRow
  97. {
  98. get
  99. {
  100. // in case we trying to print a datasource column in report title, init the datasource
  101. if (InternalRows.Count == 0)
  102. Init();
  103. return currentRow;
  104. }
  105. }
  106. /// <summary>
  107. /// Gets an index of current data row.
  108. /// </summary>
  109. /// <remarks>
  110. /// <para>You should initialize the datasource by the <b>Init</b> method before using this property.</para>
  111. /// <para>This property is updated when you call the <see cref="Next"/> method.</para>
  112. /// </remarks>
  113. [Browsable(false)]
  114. public int CurrentRowNo
  115. {
  116. get { return currentRowNo; }
  117. set
  118. {
  119. currentRowNo = value;
  120. if (value >= 0 && value < rows.Count)
  121. currentRow = rows[value];
  122. else
  123. currentRow = null;
  124. }
  125. }
  126. /// <summary>
  127. /// Gets data stored in a specified column.
  128. /// </summary>
  129. /// <param name="columnAlias">Alias of a column.</param>
  130. /// <returns>The column's value.</returns>
  131. /// <remarks>
  132. /// You should initialize the datasource by the <b>Init</b> method before using this property.
  133. /// </remarks>
  134. [Browsable(false)]
  135. public object this[string columnAlias]
  136. {
  137. get
  138. {
  139. return GetValue(columnAlias);
  140. }
  141. }
  142. /// <summary>
  143. /// Gets data stored in a specified column.
  144. /// </summary>
  145. /// <param name="column">The column.</param>
  146. /// <returns>The column's value.</returns>
  147. /// <remarks>
  148. /// You should initialize the datasource by the <b>Init</b> method before using this property.
  149. /// </remarks>
  150. [Browsable(false)]
  151. public object this[Column column]
  152. {
  153. get
  154. {
  155. if (InternalRows.Count == 0)
  156. Init();
  157. if (column.Calculated)
  158. return column.Value;
  159. return GetValue(column);
  160. }
  161. }
  162. /// <summary>
  163. /// Forces loading of data for this datasource.
  164. /// </summary>
  165. /// <remarks>
  166. /// This property is <b>false</b> by default. Set it to <b>true</b> if you need to reload data
  167. /// each time when the datasource initialized. Note that this may slow down the performance.
  168. /// </remarks>
  169. [DefaultValue(false)]
  170. public bool ForceLoadData
  171. {
  172. get { return forceLoadData; }
  173. set { forceLoadData = value; }
  174. }
  175. /// <summary>
  176. /// This property is not relevant to this class.
  177. /// </summary>
  178. [Browsable(false)]
  179. public new Type DataType
  180. {
  181. get { return base.DataType; }
  182. set { base.DataType = value; }
  183. }
  184. /// <summary>
  185. /// This property is not relevant to this class.
  186. /// </summary>
  187. [Browsable(false)]
  188. public new ColumnBindableControl BindableControl
  189. {
  190. get { return base.BindableControl; }
  191. set { base.BindableControl = value; }
  192. }
  193. /// <summary>
  194. /// This property is not relevant to this class.
  195. /// </summary>
  196. [Browsable(false)]
  197. public new string CustomBindableControl
  198. {
  199. get { return base.CustomBindableControl; }
  200. set { base.CustomBindableControl = value; }
  201. }
  202. /// <summary>
  203. /// This property is not relevant to this class.
  204. /// </summary>
  205. [Browsable(false)]
  206. public new ColumnFormat Format
  207. {
  208. get { return base.Format; }
  209. set { base.Format = value; }
  210. }
  211. /// <summary>
  212. /// This property is not relevant to this class.
  213. /// </summary>
  214. [Browsable(false)]
  215. public new string Expression
  216. {
  217. get { return base.Expression; }
  218. set { base.Expression = value; }
  219. }
  220. /// <summary>
  221. /// This property is not relevant to this class.
  222. /// </summary>
  223. [Browsable(false)]
  224. public new bool Calculated
  225. {
  226. get { return base.Calculated; }
  227. set { base.Calculated = value; }
  228. }
  229. /// <summary>
  230. /// Gets the additional filter settings.
  231. /// </summary>
  232. internal Hashtable AdditionalFilter
  233. {
  234. get { return additionalFilter; }
  235. }
  236. internal ArrayList Rows
  237. {
  238. get { return rows; }
  239. }
  240. internal ArrayList InternalRows
  241. {
  242. get { return internalRows; }
  243. }
  244. #endregion
  245. #region Private Methods
  246. private void GetChildRows(Relation relation)
  247. {
  248. // prepare consts
  249. object parentRow = relation.ParentDataSource.CurrentRow;
  250. int columnsCount = relation.ParentColumns.Length;
  251. object[] parentValues = new object[columnsCount];
  252. Column[] childColumns = new Column[columnsCount];
  253. for (int i = 0; i < columnsCount; i++)
  254. {
  255. parentValues[i] = relation.ParentDataSource[relation.ParentColumns[i]];
  256. childColumns[i] = Columns.FindByAlias(relation.ChildColumns[i]);
  257. }
  258. if (relation_SortedChildRows == null)
  259. relation_SortedChildRows = new Hashtable();
  260. // sort the child table at the first run. Use relation columns to sort.
  261. SortedList<Indices, ArrayList> sortedChildRows = relation_SortedChildRows[relation] as SortedList<Indices, ArrayList>;
  262. if (sortedChildRows == null)
  263. {
  264. sortedChildRows = new SortedList<Indices, ArrayList>();
  265. relation_SortedChildRows[relation] = sortedChildRows;
  266. foreach (object row in InternalRows)
  267. {
  268. SetCurrentRow(row);
  269. object[] values = new object[columnsCount];
  270. for (int i = 0; i < columnsCount; i++)
  271. {
  272. values[i] = this[childColumns[i]];
  273. }
  274. Indices indices = new Indices(values);
  275. ArrayList rows = null;
  276. int index = sortedChildRows.IndexOfKey(indices);
  277. if (index == -1)
  278. {
  279. rows = new ArrayList();
  280. sortedChildRows.Add(indices, rows);
  281. }
  282. else
  283. rows = sortedChildRows.Values[index];
  284. rows.Add(row);
  285. }
  286. }
  287. int indexOfKey = sortedChildRows.IndexOfKey(new Indices(parentValues));
  288. if (indexOfKey != -1)
  289. {
  290. ArrayList rows = sortedChildRows.Values[indexOfKey];
  291. foreach (object row in rows)
  292. {
  293. this.rows.Add(row);
  294. }
  295. }
  296. }
  297. private void ApplyAdditionalFilter()
  298. {
  299. for (int i = 0; i < rows.Count; i++)
  300. {
  301. CurrentRowNo = i;
  302. foreach (DictionaryEntry de in AdditionalFilter)
  303. {
  304. object value = Report.GetColumnValueNullable((string)de.Key);
  305. DataSourceFilter filter = de.Value as DataSourceFilter;
  306. if (!filter.ValueMatch(value))
  307. {
  308. rows.RemoveAt(i);
  309. i--;
  310. break;
  311. }
  312. }
  313. }
  314. }
  315. private void UpdateExpressions(string oldAlias, string newAlias)
  316. {
  317. if (Report != null)
  318. {
  319. // Update expressions in components.
  320. foreach (Base component in Report.AllObjects)
  321. {
  322. // Update Text in TextObject instances.
  323. if (component is TextObject)
  324. {
  325. TextObject text = component as TextObject;
  326. string bracket = text.Brackets.Split(new char[] { ',' })[0];
  327. if (String.IsNullOrEmpty(bracket))
  328. {
  329. bracket = "[";
  330. }
  331. text.Text = text.Text.Replace(bracket + oldAlias + ".", bracket + newAlias + ".");
  332. }
  333. // Update DataColumn in PictureObject instances.
  334. else if (component is PictureObject)
  335. {
  336. PictureObject picture = component as PictureObject;
  337. picture.DataColumn = picture.DataColumn.Replace(oldAlias + ".", newAlias + ".");
  338. }
  339. // Update Filter and Sort in DataBand instances.
  340. else if (component is DataBand)
  341. {
  342. DataBand data = component as DataBand;
  343. data.Filter = data.Filter.Replace("[" + oldAlias + ".", "[" + newAlias + ".");
  344. foreach (Sort sort in data.Sort)
  345. {
  346. sort.Expression = sort.Expression.Replace("[" + oldAlias + ".", "[" + newAlias + ".");
  347. }
  348. }
  349. }
  350. }
  351. }
  352. #endregion
  353. #region Protected Methods
  354. /// <summary>
  355. /// Gets data stored in a specified column.
  356. /// </summary>
  357. /// <param name="alias">The column alias.</param>
  358. /// <returns>An object that contains the data.</returns>
  359. protected virtual object GetValue(string alias)
  360. {
  361. Column column = columnIndices[alias] as Column;
  362. if (column == null)
  363. {
  364. column = Columns.FindByAlias(alias);
  365. columnIndices[alias] = column;
  366. }
  367. return GetValue(column);
  368. }
  369. /// <summary>
  370. /// Gets data stored in a specified column.
  371. /// </summary>
  372. /// <param name="column">The column.</param>
  373. /// <returns>An object that contains the data.</returns>
  374. protected abstract object GetValue(Column column);
  375. #endregion
  376. #region Public Methods
  377. /// <summary>
  378. /// Initializes the datasource schema.
  379. /// </summary>
  380. /// <remarks>
  381. /// This method is used to support the FastReport.Net infrastructure. Do not call it directly.
  382. /// </remarks>
  383. public abstract void InitSchema();
  384. /// <summary>
  385. /// Loads the datasource with data.
  386. /// </summary>
  387. /// <remarks>
  388. /// This method is used to support the FastReport.Net infrastructure. Do not call it directly.
  389. /// </remarks>
  390. /// <param name="rows">Rows to fill with data.</param>
  391. public abstract void LoadData(ArrayList rows);
  392. internal void LoadData()
  393. {
  394. LoadData(InternalRows);
  395. }
  396. internal void OnLoad()
  397. {
  398. if (Load != null)
  399. {
  400. // clear internal rows to force reload data
  401. InternalRows.Clear();
  402. Load(this, EventArgs.Empty);
  403. }
  404. }
  405. internal void SetCurrentRow(object row)
  406. {
  407. currentRow = row;
  408. }
  409. internal void FindParentRow(Relation relation)
  410. {
  411. InitSchema();
  412. LoadData();
  413. int columnCount = relation.ChildColumns.Length;
  414. object[] childValues = new object[columnCount];
  415. for (int i = 0; i < columnCount; i++)
  416. {
  417. childValues[i] = relation.ChildDataSource[relation.ChildColumns[i]];
  418. }
  419. object result = null;
  420. if (childValues[0] == null)
  421. {
  422. SetCurrentRow(null);
  423. return;
  424. }
  425. // improve performance for single column index
  426. if (columnCount == 1)
  427. {
  428. if (rowIndices.Count == 0)
  429. {
  430. foreach (object row in InternalRows)
  431. {
  432. SetCurrentRow(row);
  433. rowIndices[this[relation.ParentColumns[0]]] = row;
  434. }
  435. }
  436. result = rowIndices[childValues[0]];
  437. if (result != null)
  438. {
  439. SetCurrentRow(result);
  440. return;
  441. }
  442. }
  443. foreach (object row in InternalRows)
  444. {
  445. SetCurrentRow(row);
  446. bool found = true;
  447. for (int i = 0; i < columnCount; i++)
  448. {
  449. if (!this[relation.ParentColumns[i]].Equals(childValues[i]))
  450. {
  451. found = false;
  452. break;
  453. }
  454. }
  455. if (found)
  456. {
  457. result = row;
  458. break;
  459. }
  460. }
  461. if (columnCount == 1)
  462. rowIndices[childValues[0]] = result;
  463. SetCurrentRow(result);
  464. }
  465. /// <summary>
  466. /// Initializes this datasource.
  467. /// </summary>
  468. /// <remarks>
  469. /// This method fills the table with data. You should always call it before using most of
  470. /// datasource properties.
  471. /// </remarks>
  472. public void Init()
  473. {
  474. Init("");
  475. }
  476. /// <summary>
  477. /// Initializes this datasource and applies the specified filter.
  478. /// </summary>
  479. /// <param name="filter">The filter expression.</param>
  480. public void Init(string filter)
  481. {
  482. Init(filter, null);
  483. }
  484. /// <summary>
  485. /// Initializes this datasource, applies the specified filter and sorts the rows.
  486. /// </summary>
  487. /// <param name="filter">The filter expression.</param>
  488. /// <param name="sort">The collection of sort descriptors.</param>
  489. public void Init(string filter, SortCollection sort)
  490. {
  491. DataSourceBase parentData = null;
  492. Init(parentData, filter, sort);
  493. }
  494. /// <summary>
  495. /// Initializes this datasource and filters data rows according to the master-detail relation between
  496. /// this datasource and <b>parentData</b>.
  497. /// </summary>
  498. /// <param name="parentData">Parent datasource.</param>
  499. /// <remarks>
  500. /// To use master-detail relation, you must define the <see cref="Relation"/> object that describes
  501. /// the relation, and add it to the <b>Report.Dictionary.Relations</b> collection.
  502. /// </remarks>
  503. public void Init(DataSourceBase parentData)
  504. {
  505. Init(parentData, "", null);
  506. }
  507. /// <summary>
  508. /// Initializes this datasource and filters data rows according to the master-detail relation between
  509. /// this datasource and <b>parentData</b>. Also applies the specified filter and sorts the rows.
  510. /// </summary>
  511. /// <param name="parentData">Parent datasource.</param>
  512. /// <param name="filter">The filter expression.</param>
  513. /// <param name="sort">The collection of sort descriptors.</param>
  514. /// <remarks>
  515. /// To use master-detail relation, you must define the <see cref="Relation"/> object that describes
  516. /// the relation, and add it to the <b>Report.Dictionary.Relations</b> collection.
  517. /// </remarks>
  518. public void Init(DataSourceBase parentData, string filter, SortCollection sort)
  519. {
  520. Init(parentData, filter, sort, false);
  521. }
  522. /// <summary>
  523. /// Initializes this datasource and filters data rows according to the master-detail relation.
  524. /// Also applies the specified filter and sorts the rows.
  525. /// </summary>
  526. /// <param name="relation">The master-detail relation.</param>
  527. /// <param name="filter">The filter expression.</param>
  528. /// <param name="sort">The collection of sort descriptors.</param>
  529. /// <remarks>
  530. /// To use master-detail relation, you must define the <see cref="Relation"/> object that describes
  531. /// the relation, and add it to the <b>Report.Dictionary.Relations</b> collection.
  532. /// </remarks>
  533. public void Init(Relation relation, string filter, SortCollection sort)
  534. {
  535. Init(relation, filter, sort, false);
  536. }
  537. internal void Init(DataSourceBase parentData, string filter, SortCollection sort, bool useAllParentRows)
  538. {
  539. Relation relation = parentData != null ? DataHelper.FindRelation(Report.Dictionary, parentData, this) : null;
  540. Init(relation, filter, sort, useAllParentRows);
  541. }
  542. internal void Init(Relation relation, string filter, SortCollection sort, bool useAllParentRows)
  543. {
  544. if (FShowAccessDataMessage)
  545. Config.ReportSettings.OnProgress(Report, Res.Get("Messages,AccessingData"));
  546. // InitSchema may fail sometimes (for example, when using OracleConnection with nested select).
  547. try
  548. {
  549. InitSchema();
  550. }
  551. catch
  552. {
  553. }
  554. LoadData();
  555. // fill rows, emulate relation
  556. rows.Clear();
  557. if (relation != null && relation.Enabled)
  558. {
  559. if (useAllParentRows)
  560. {
  561. DataSourceBase parentData = relation.ParentDataSource;
  562. // parentData must be initialized prior to calling this method!
  563. parentData.First();
  564. while (parentData.HasMoreRows)
  565. {
  566. GetChildRows(relation);
  567. parentData.Next();
  568. }
  569. }
  570. else
  571. GetChildRows(relation);
  572. }
  573. else
  574. {
  575. foreach (object row in InternalRows)
  576. {
  577. rows.Add(row);
  578. }
  579. }
  580. // filter data rows
  581. if (FShowAccessDataMessage && rows.Count > 10000)
  582. Config.ReportSettings.OnProgress(Report, Res.Get("Messages,PreparingData"));
  583. if (filter != null && filter.Trim() != "")
  584. {
  585. for (int i = 0; i < rows.Count; i++)
  586. {
  587. CurrentRowNo = i;
  588. object match = Report.Calc(filter);
  589. if (match is bool && !(bool)match)
  590. {
  591. rows.RemoveAt(i);
  592. i--;
  593. }
  594. }
  595. }
  596. // additional filter
  597. if (AdditionalFilter.Count > 0)
  598. ApplyAdditionalFilter();
  599. // sort data rows
  600. if (sort != null && sort.Count > 0)
  601. {
  602. string[] expressions = new string[sort.Count];
  603. bool[] descending = new bool[sort.Count];
  604. for (int i = 0; i < sort.Count; i++)
  605. {
  606. expressions[i] = sort[i].Expression;
  607. descending[i] = sort[i].Descending;
  608. }
  609. rows.Sort(new RowComparer(Report, this, expressions, descending));
  610. }
  611. FShowAccessDataMessage = false;
  612. First();
  613. }
  614. /// <summary>
  615. /// Initializes the data source if it is not initialized yet.
  616. /// </summary>
  617. public void EnsureInit()
  618. {
  619. if (InternalRows.Count == 0)
  620. Init();
  621. }
  622. /// <summary>
  623. /// Navigates to the first row.
  624. /// </summary>
  625. /// <remarks>
  626. /// You should initialize the datasource by the <b>Init</b> method before using this method.
  627. /// </remarks>
  628. public void First()
  629. {
  630. CurrentRowNo = 0;
  631. }
  632. /// <summary>
  633. /// Navigates to the next row.
  634. /// </summary>
  635. /// <remarks>
  636. /// You should initialize the datasource by the <b>Init</b> method before using this method.
  637. /// </remarks>
  638. public void Next()
  639. {
  640. CurrentRowNo++;
  641. }
  642. /// <summary>
  643. /// Navigates to the prior row.
  644. /// </summary>
  645. /// <remarks>
  646. /// You should initialize the datasource by the <b>Init</b> method before using this method.
  647. /// </remarks>
  648. public void Prior()
  649. {
  650. CurrentRowNo--;
  651. }
  652. /// <inheritdoc/>
  653. public override void Serialize(FRWriter writer)
  654. {
  655. base.Serialize(writer);
  656. if (Enabled)
  657. writer.WriteBool("Enabled", Enabled);
  658. if (ForceLoadData)
  659. writer.WriteBool("ForceLoadData", ForceLoadData);
  660. }
  661. /// <inheritdoc/>
  662. public override void Deserialize(FRReader reader)
  663. {
  664. // the Clear is needed to avoid duplicate columns in the inherited report
  665. // when the same datasource is exists in both base and inherited report
  666. Clear();
  667. base.Deserialize(reader);
  668. }
  669. internal void ClearData()
  670. {
  671. columnIndices.Clear();
  672. rowIndices.Clear();
  673. InternalRows.Clear();
  674. Rows.Clear();
  675. additionalFilter.Clear();
  676. relation_SortedChildRows = null;
  677. FShowAccessDataMessage = true;
  678. }
  679. /// <inheritdoc/>
  680. public override void InitializeComponent()
  681. {
  682. ClearData();
  683. }
  684. #endregion
  685. /// <summary>
  686. /// Initializes a new instance of the <see cref="DataSourceBase"/> class with default settings.
  687. /// </summary>
  688. public DataSourceBase()
  689. {
  690. internalRows = new ArrayList();
  691. rows = new ArrayList();
  692. additionalFilter = new Hashtable();
  693. columnIndices = new Hashtable();
  694. rowIndices = new Hashtable();
  695. SetFlags(Flags.HasGlobalName, true);
  696. }
  697. private class RowComparer : IComparer
  698. {
  699. private Report report;
  700. private DataSourceBase dataSource;
  701. private string[] expressions;
  702. private bool[] descending;
  703. private Column[] columns;
  704. public int Compare(object x, object y)
  705. {
  706. int result = 0;
  707. for (int i = 0; i < expressions.Length; i++)
  708. {
  709. IComparable i1;
  710. IComparable i2;
  711. if (columns[i] == null)
  712. {
  713. dataSource.SetCurrentRow(x);
  714. i1 = report.Calc(expressions[i]) as IComparable;
  715. dataSource.SetCurrentRow(y);
  716. i2 = report.Calc(expressions[i]) as IComparable;
  717. }
  718. else
  719. {
  720. dataSource.SetCurrentRow(x);
  721. i1 = columns[i].Value as IComparable;
  722. dataSource.SetCurrentRow(y);
  723. i2 = columns[i].Value as IComparable;
  724. }
  725. if (i1 != null)
  726. result = i1.CompareTo(i2);
  727. else if (i2 != null)
  728. result = -1;
  729. if (descending[i])
  730. result = -result;
  731. if (result != 0)
  732. break;
  733. }
  734. return result;
  735. }
  736. public RowComparer(Report report, DataSourceBase dataSource, string[] expressions, bool[] descending)
  737. {
  738. this.report = report;
  739. this.dataSource = dataSource;
  740. this.expressions = expressions;
  741. this.descending = descending;
  742. // optimize performance if expression is a single data column
  743. columns = new Column[expressions.Length];
  744. for (int i = 0; i < expressions.Length; i++)
  745. {
  746. string expr = expressions[i];
  747. if (expr.StartsWith("[") && expr.EndsWith("]"))
  748. expr = expr.Substring(1, expr.Length - 2);
  749. Column column = DataHelper.GetColumn(this.report.Dictionary, expr);
  750. DataSourceBase datasource = DataHelper.GetDataSource(this.report.Dictionary, expr);
  751. if (column != null && column.Parent == datasource)
  752. columns[i] = column;
  753. else
  754. columns[i] = null;
  755. }
  756. }
  757. }
  758. private class Indices : IComparable
  759. {
  760. private object[] values;
  761. public int CompareTo(object obj)
  762. {
  763. Indices indices = obj as Indices;
  764. int result = 0;
  765. for (int i = 0; i < values.Length; i++)
  766. {
  767. IComparable i1 = indices.values[i] as IComparable;
  768. IComparable i2 = values[i] as IComparable;
  769. if (i1 != null)
  770. result = i1.CompareTo(i2);
  771. else if (i2 != null)
  772. result = -1;
  773. if (result != 0)
  774. break;
  775. }
  776. return result;
  777. }
  778. public Indices(object[] values)
  779. {
  780. this.values = values;
  781. }
  782. }
  783. }
  784. }