DataSourceBase.cs 25 KB

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