DataColumnCollection.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DataColumnCollection.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. // <owner current="false" primary="false">[....]</owner>
  8. //------------------------------------------------------------------------------
  9. namespace System.Data {
  10. using System;
  11. using System.Xml;
  12. using System.Collections;
  13. using System.Collections.Generic;
  14. using System.ComponentModel;
  15. using System.Data.Common;
  16. using System.Diagnostics;
  17. /// <devdoc>
  18. /// <para>Represents a collection of <see cref='System.Data.DataColumn'/>
  19. /// objects for a <see cref='System.Data.DataTable'/>.</para>
  20. /// </devdoc>
  21. [
  22. DefaultEvent("CollectionChanged"),
  23. Editor("Microsoft.VSDesigner.Data.Design.ColumnsCollectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
  24. ]
  25. public sealed class DataColumnCollection : InternalDataCollectionBase {
  26. private readonly DataTable table;
  27. private readonly ArrayList _list = new ArrayList();
  28. private int defaultNameIndex = 1;
  29. private DataColumn[] delayedAddRangeColumns;
  30. private readonly Dictionary<string, DataColumn> columnFromName; // Links names to columns
  31. private CollectionChangeEventHandler onCollectionChangedDelegate;
  32. private CollectionChangeEventHandler onCollectionChangingDelegate;
  33. private CollectionChangeEventHandler onColumnPropertyChangedDelegate;
  34. private bool fInClear;
  35. private DataColumn[] columnsImplementingIChangeTracking = DataTable.zeroColumns;
  36. private int nColumnsImplementingIChangeTracking = 0;
  37. private int nColumnsImplementingIRevertibleChangeTracking = 0;
  38. /// <devdoc>
  39. /// DataColumnCollection constructor. Used only by DataTable.
  40. /// </devdoc>
  41. internal DataColumnCollection(DataTable table) {
  42. this.table = table;
  43. columnFromName = new Dictionary<string, DataColumn>();
  44. }
  45. /// <devdoc>
  46. /// <para>Gets the list of the collection items.</para>
  47. /// </devdoc>
  48. protected override ArrayList List {
  49. get {
  50. return _list;
  51. }
  52. }
  53. internal DataColumn[] ColumnsImplementingIChangeTracking {
  54. get {
  55. return columnsImplementingIChangeTracking;
  56. }
  57. }
  58. internal int ColumnsImplementingIChangeTrackingCount{
  59. get {
  60. return nColumnsImplementingIChangeTracking;
  61. }
  62. }
  63. internal int ColumnsImplementingIRevertibleChangeTrackingCount {
  64. get {
  65. return nColumnsImplementingIRevertibleChangeTracking;
  66. }
  67. }
  68. /// <devdoc>
  69. /// <para>
  70. /// Gets the <see cref='System.Data.DataColumn'/>
  71. /// from the collection at the specified index.
  72. /// </para>
  73. /// </devdoc>
  74. public DataColumn this[int index] {
  75. get {
  76. try { // Perf: use the readonly _list field directly and let ArrayList check the range
  77. return (DataColumn)_list[index];
  78. }
  79. catch(ArgumentOutOfRangeException) {
  80. throw ExceptionBuilder.ColumnOutOfRange(index);
  81. }
  82. }
  83. }
  84. /// <devdoc>
  85. /// <para>Gets the <see cref='System.Data.DataColumn'/> from the collection with the specified name.</para>
  86. /// </devdoc>
  87. public DataColumn this[string name] {
  88. get {
  89. if (null == name) {
  90. throw ExceptionBuilder.ArgumentNull("name");
  91. }
  92. DataColumn column;
  93. if ((!columnFromName.TryGetValue(name, out column)) || (column == null)) {
  94. // Case-Insensitive compares
  95. int index = IndexOfCaseInsensitive(name);
  96. if (0 <= index) {
  97. column = (DataColumn)_list[index];
  98. }
  99. else if (-2 == index) {
  100. throw ExceptionBuilder.CaseInsensitiveNameConflict(name);
  101. }
  102. }
  103. return column;
  104. }
  105. }
  106. internal DataColumn this[string name, string ns] {
  107. get {
  108. DataColumn column;
  109. if ((columnFromName.TryGetValue(name, out column)) && (column != null) && (column.Namespace == ns)) {
  110. return column;
  111. }
  112. return null;
  113. }
  114. }
  115. internal void EnsureAdditionalCapacity(int capacity) {
  116. if (_list.Capacity < capacity + _list.Count) {
  117. _list.Capacity = capacity + _list.Count;
  118. }
  119. }
  120. /// <devdoc>
  121. /// <para>Adds the specified <see cref='System.Data.DataColumn'/>
  122. /// to the columns collection.</para>
  123. /// </devdoc>
  124. public void Add(DataColumn column) {
  125. AddAt(-1, column);
  126. }
  127. internal void AddAt(int index, DataColumn column) {
  128. if (column != null && column.ColumnMapping == MappingType.SimpleContent) {
  129. if (table.XmlText != null && table.XmlText != column)
  130. throw ExceptionBuilder.CannotAddColumn3();
  131. if (table.ElementColumnCount > 0)
  132. throw ExceptionBuilder.CannotAddColumn4(column.ColumnName);
  133. OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
  134. BaseAdd(column);
  135. if (index != -1)
  136. ArrayAdd(index, column);
  137. else
  138. ArrayAdd(column);
  139. table.XmlText = column;
  140. }
  141. else {
  142. OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
  143. BaseAdd(column);
  144. if (index != -1)
  145. ArrayAdd(index, column);
  146. else
  147. ArrayAdd(column);
  148. // if the column is an element increase the internal dataTable counter
  149. if (column.ColumnMapping == MappingType.Element)
  150. table.ElementColumnCount ++;
  151. }
  152. if (!table.fInitInProgress && column != null && column.Computed) {
  153. column.Expression = column.Expression;
  154. }
  155. OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, column));
  156. }
  157. /// <devdoc>
  158. /// <para>[To be supplied.]</para>
  159. /// </devdoc>
  160. public void AddRange(DataColumn[] columns) {
  161. if (table.fInitInProgress) {
  162. delayedAddRangeColumns = columns;
  163. return;
  164. }
  165. if (columns != null) {
  166. foreach(DataColumn column in columns) {
  167. if (column != null) {
  168. Add(column);
  169. }
  170. }
  171. }
  172. }
  173. /// <devdoc>
  174. /// <para>Creates and adds a <see cref='System.Data.DataColumn'/>
  175. /// with
  176. /// the specified name, type, and compute expression to the columns collection.</para>
  177. /// </devdoc>
  178. public DataColumn Add(string columnName, Type type, string expression) {
  179. DataColumn column = new DataColumn(columnName, type, expression);
  180. Add(column);
  181. return column;
  182. }
  183. /// <devdoc>
  184. /// <para>Creates and adds a <see cref='System.Data.DataColumn'/>
  185. /// with the
  186. /// specified name and type to the columns collection.</para>
  187. /// </devdoc>
  188. public DataColumn Add(string columnName, Type type) {
  189. DataColumn column = new DataColumn(columnName, type);
  190. Add(column);
  191. return column;
  192. }
  193. /// <devdoc>
  194. /// <para>Creates and adds a <see cref='System.Data.DataColumn'/>
  195. /// with the specified name to the columns collection.</para>
  196. /// </devdoc>
  197. public DataColumn Add(string columnName) {
  198. DataColumn column = new DataColumn(columnName);
  199. Add(column);
  200. return column;
  201. }
  202. /// <devdoc>
  203. /// <para>Creates and adds a <see cref='System.Data.DataColumn'/> to a columns collection.</para>
  204. /// </devdoc>
  205. public DataColumn Add() {
  206. DataColumn column = new DataColumn();
  207. Add(column);
  208. return column;
  209. }
  210. /// <devdoc>
  211. /// <para>Occurs when the columns collection changes, either by adding or removing a column.</para>
  212. /// </devdoc>
  213. [ResDescriptionAttribute(Res.collectionChangedEventDescr)]
  214. public event CollectionChangeEventHandler CollectionChanged {
  215. add {
  216. onCollectionChangedDelegate += value;
  217. }
  218. remove {
  219. onCollectionChangedDelegate -= value;
  220. }
  221. }
  222. internal event CollectionChangeEventHandler CollectionChanging {
  223. add {
  224. onCollectionChangingDelegate += value;
  225. }
  226. remove {
  227. onCollectionChangingDelegate -= value;
  228. }
  229. }
  230. internal event CollectionChangeEventHandler ColumnPropertyChanged {
  231. add {
  232. onColumnPropertyChangedDelegate += value;
  233. }
  234. remove {
  235. onColumnPropertyChangedDelegate -= value;
  236. }
  237. }
  238. /// <devdoc>
  239. /// Adds the column to the columns array.
  240. /// </devdoc>
  241. private void ArrayAdd(DataColumn column) {
  242. _list.Add(column);
  243. column.SetOrdinalInternal(_list.Count - 1);
  244. CheckIChangeTracking(column);
  245. }
  246. private void ArrayAdd(int index, DataColumn column) {
  247. _list.Insert(index, column);
  248. CheckIChangeTracking(column);
  249. }
  250. private void ArrayRemove(DataColumn column) {
  251. column.SetOrdinalInternal(-1);
  252. _list.Remove(column);
  253. int count = _list.Count;
  254. for (int i =0; i < count; i++) {
  255. ((DataColumn) _list[i]).SetOrdinalInternal(i);
  256. }
  257. if (column.ImplementsIChangeTracking) {
  258. RemoveColumnsImplementingIChangeTrackingList(column);
  259. }
  260. }
  261. /// <devdoc>
  262. /// Creates a new default name.
  263. /// </devdoc>
  264. internal string AssignName() {
  265. string newName = MakeName(defaultNameIndex++);
  266. while (columnFromName.ContainsKey(newName)) {
  267. newName = MakeName(defaultNameIndex++);
  268. }
  269. return newName;
  270. }
  271. /// <devdoc>
  272. /// Does verification on the column and it's name, and points the column at the dataSet that owns this collection.
  273. /// An ArgumentNullException is thrown if this column is null. An ArgumentException is thrown if this column
  274. /// already belongs to this collection, belongs to another collection.
  275. /// A DuplicateNameException is thrown if this collection already has a column with the same
  276. /// name (case insensitive).
  277. /// </devdoc>
  278. private void BaseAdd(DataColumn column) {
  279. if (column == null)
  280. throw ExceptionBuilder.ArgumentNull("column");
  281. if (column.table == table)
  282. throw ExceptionBuilder.CannotAddColumn1(column.ColumnName);
  283. if (column.table != null)
  284. throw ExceptionBuilder.CannotAddColumn2(column.ColumnName);
  285. if (column.ColumnName.Length == 0) {
  286. column.ColumnName = AssignName();
  287. }
  288. RegisterColumnName(column.ColumnName, column);
  289. try {
  290. column.SetTable(table);
  291. if (!table.fInitInProgress && column.Computed) {
  292. if (column.DataExpression.DependsOn(column)) {
  293. throw ExceptionBuilder.ExpressionCircular();
  294. }
  295. }
  296. if (0 < table.RecordCapacity) {
  297. // adding a column to table with existing rows
  298. column.SetCapacity(table.RecordCapacity);
  299. }
  300. // fill column with default value.
  301. for (int record = 0; record < table.RecordCapacity; record++) {
  302. column.InitializeRecord(record);
  303. }
  304. if (table.DataSet != null) {
  305. column.OnSetDataSet();
  306. }
  307. }
  308. catch (Exception e) {
  309. //
  310. if (ADP.IsCatchableOrSecurityExceptionType(e)) {
  311. UnregisterName(column.ColumnName);
  312. }
  313. throw;
  314. }
  315. }
  316. /// <devdoc>
  317. /// BaseGroupSwitch will intelligently remove and add tables from the collection.
  318. /// </devdoc>
  319. private void BaseGroupSwitch(DataColumn[] oldArray, int oldLength, DataColumn[] newArray, int newLength) {
  320. // We're doing a smart diff of oldArray and newArray to find out what
  321. // should be removed. We'll pass through oldArray and see if it exists
  322. // in newArray, and if not, do remove work. newBase is an opt. in case
  323. // the arrays have similar prefixes.
  324. int newBase = 0;
  325. for (int oldCur = 0; oldCur < oldLength; oldCur++) {
  326. bool found = false;
  327. for (int newCur = newBase; newCur < newLength; newCur++) {
  328. if (oldArray[oldCur] == newArray[newCur]) {
  329. if (newBase == newCur) {
  330. newBase++;
  331. }
  332. found = true;
  333. break;
  334. }
  335. }
  336. if (!found) {
  337. // This means it's in oldArray and not newArray. Remove it.
  338. if (oldArray[oldCur].Table == table) {
  339. BaseRemove(oldArray[oldCur]);
  340. _list.Remove(oldArray[oldCur]);
  341. oldArray[oldCur].SetOrdinalInternal(-1);
  342. }
  343. }
  344. }
  345. // Now, let's pass through news and those that don't belong, add them.
  346. for (int newCur = 0; newCur < newLength; newCur++) {
  347. if (newArray[newCur].Table != table) {
  348. BaseAdd(newArray[newCur]);
  349. _list.Add(newArray[newCur]);
  350. }
  351. newArray[newCur].SetOrdinalInternal(newCur);
  352. }
  353. }
  354. /// <devdoc>
  355. /// Does verification on the column and it's name, and clears the column's dataSet pointer.
  356. /// An ArgumentNullException is thrown if this column is null. An ArgumentException is thrown
  357. /// if this column doesn't belong to this collection or if this column is part of a relationship.
  358. /// An ArgumentException is thrown if another column's compute expression depends on this column.
  359. /// </devdoc>
  360. private void BaseRemove(DataColumn column) {
  361. if (CanRemove(column, true)) {
  362. // remove
  363. if (column.errors > 0) {
  364. for (int i = 0; i < table.Rows.Count; i++) {
  365. table.Rows[i].ClearError(column);
  366. }
  367. }
  368. UnregisterName(column.ColumnName);
  369. column.SetTable(null);
  370. }
  371. }
  372. /// <devdoc>
  373. /// <para>Checks
  374. /// if
  375. /// a given column can be removed from the collection.</para>
  376. /// </devdoc>
  377. public bool CanRemove(DataColumn column) {
  378. return CanRemove(column, false);
  379. }
  380. internal bool CanRemove(DataColumn column, bool fThrowException) {
  381. if (column == null) {
  382. if (!fThrowException)
  383. return false;
  384. else
  385. throw ExceptionBuilder.ArgumentNull("column");
  386. }
  387. if (column.table != table) {
  388. if (!fThrowException)
  389. return false;
  390. else
  391. throw ExceptionBuilder.CannotRemoveColumn();
  392. }
  393. // allow subclasses to complain first.
  394. table.OnRemoveColumnInternal(column);
  395. // We need to make sure the column is not involved in any Relations or Constriants
  396. if (table.primaryKey != null && table.primaryKey.Key.ContainsColumn(column)) {
  397. if (!fThrowException)
  398. return false;
  399. else
  400. throw ExceptionBuilder.CannotRemovePrimaryKey();
  401. }
  402. for (int i = 0; i < table.ParentRelations.Count; i++) {
  403. if (table.ParentRelations[i].ChildKey.ContainsColumn(column)) {
  404. if (!fThrowException)
  405. return false;
  406. else
  407. throw ExceptionBuilder.CannotRemoveChildKey(table.ParentRelations[i].RelationName);
  408. }
  409. }
  410. for (int i = 0; i < table.ChildRelations.Count; i++) {
  411. if (table.ChildRelations[i].ParentKey.ContainsColumn(column)) {
  412. if (!fThrowException)
  413. return false;
  414. else
  415. throw ExceptionBuilder.CannotRemoveChildKey(table.ChildRelations[i].RelationName);
  416. }
  417. }
  418. for (int i = 0; i < table.Constraints.Count; i++) {
  419. if (table.Constraints[i].ContainsColumn(column))
  420. if (!fThrowException)
  421. return false;
  422. else
  423. throw ExceptionBuilder.CannotRemoveConstraint(table.Constraints[i].ConstraintName, table.Constraints[i].Table.TableName);
  424. }
  425. if (table.DataSet != null) {
  426. for (ParentForeignKeyConstraintEnumerator en = new ParentForeignKeyConstraintEnumerator(table.DataSet, table); en.GetNext();) {
  427. Constraint constraint = en.GetConstraint();
  428. if (((ForeignKeyConstraint)constraint).ParentKey.ContainsColumn(column))
  429. if (!fThrowException)
  430. return false;
  431. else
  432. throw ExceptionBuilder.CannotRemoveConstraint(constraint.ConstraintName, constraint.Table.TableName);
  433. }
  434. }
  435. if (column.dependentColumns != null) {
  436. for (int i = 0; i < column.dependentColumns.Count; i++) {
  437. DataColumn col = column.dependentColumns[i];
  438. if (fInClear && (col.Table == table || col.Table == null))
  439. continue;
  440. if (col.Table == null)
  441. continue;
  442. Debug.Assert(col.Computed, "invalid (non an expression) column in the expression dependent columns");
  443. DataExpression expr = col.DataExpression;
  444. if ((expr!= null) && (expr.DependsOn(column))) {
  445. if (!fThrowException)
  446. return false;
  447. else
  448. throw ExceptionBuilder.CannotRemoveExpression(col.ColumnName, col.Expression);
  449. }
  450. }
  451. }
  452. // SQLBU 429176: you can't remove a column participating in an index,
  453. // while index events are suspended else the indexes won't be properly maintained.
  454. // However, all the above checks should catch those participating columns.
  455. // except when a column is in a DataView RowFilter or Sort clause
  456. foreach (Index index in table.LiveIndexes) {
  457. #if false
  458. if (!Object.ReferenceEquals(index, column.sortIndex)) {
  459. foreach (IndexField field in index.IndexFields) {
  460. if (Object.ReferenceEquals(field.Column, column)) {
  461. if (fThrowException) {
  462. throw ExceptionBuilder.CannotRemoveExpression("DataView", column.ColumnName);
  463. }
  464. return false;
  465. }
  466. }
  467. }
  468. #endif
  469. }
  470. return true;
  471. }
  472. private void CheckIChangeTracking(DataColumn column) {
  473. if (column.ImplementsIRevertibleChangeTracking) {
  474. nColumnsImplementingIRevertibleChangeTracking++;
  475. nColumnsImplementingIChangeTracking++;
  476. AddColumnsImplementingIChangeTrackingList(column);
  477. }
  478. else if (column.ImplementsIChangeTracking) {
  479. nColumnsImplementingIChangeTracking++;
  480. AddColumnsImplementingIChangeTrackingList(column);
  481. }
  482. }
  483. /// <devdoc>
  484. /// <para>
  485. /// Clears the collection of any columns.
  486. /// </para>
  487. /// </devdoc>
  488. public void Clear() {
  489. int oldLength = _list.Count;
  490. DataColumn[] columns = new DataColumn[_list.Count];
  491. _list.CopyTo(columns, 0);
  492. OnCollectionChanging(RefreshEventArgs);
  493. if (table.fInitInProgress && delayedAddRangeColumns != null) {
  494. delayedAddRangeColumns = null;
  495. }
  496. try {
  497. // this will smartly add and remove the appropriate tables.
  498. fInClear = true;
  499. BaseGroupSwitch(columns, oldLength, null, 0);
  500. fInClear = false;
  501. }
  502. catch (Exception e) {
  503. //
  504. if (ADP.IsCatchableOrSecurityExceptionType(e)) {
  505. // something messed up: restore to old values and throw
  506. fInClear = false;
  507. BaseGroupSwitch(null, 0, columns, oldLength);
  508. _list.Clear();
  509. for (int i = 0; i < oldLength; i++)
  510. _list.Add(columns[i]);
  511. }
  512. throw;
  513. }
  514. _list.Clear();
  515. table.ElementColumnCount = 0;
  516. OnCollectionChanged(RefreshEventArgs);
  517. }
  518. /// <devdoc>
  519. /// <para>Checks whether the collection contains a column with the specified name.</para>
  520. /// </devdoc>
  521. public bool Contains(string name) {
  522. DataColumn column;
  523. if ((columnFromName.TryGetValue(name, out column)) && (column != null)) {
  524. return true;
  525. }
  526. return (IndexOfCaseInsensitive(name) >= 0);
  527. }
  528. internal bool Contains(string name, bool caseSensitive) {
  529. DataColumn column;
  530. if ((columnFromName.TryGetValue(name, out column)) && (column != null)) {
  531. return true;
  532. }
  533. if (caseSensitive) { // above check did case sensitive check
  534. return false;
  535. }
  536. else {
  537. return (IndexOfCaseInsensitive(name) >= 0);
  538. }
  539. }
  540. public void CopyTo(DataColumn[] array, int index) {
  541. if (array==null)
  542. throw ExceptionBuilder.ArgumentNull("array");
  543. if (index < 0)
  544. throw ExceptionBuilder.ArgumentOutOfRange("index");
  545. if (array.Length - index < _list.Count)
  546. throw ExceptionBuilder.InvalidOffsetLength();
  547. for(int i = 0; i < _list.Count; ++i) {
  548. array[index + i] = (DataColumn)_list[i];
  549. }
  550. }
  551. /// <devdoc>
  552. /// <para>
  553. /// Returns the index of a specified <see cref='System.Data.DataColumn'/>.
  554. /// </para>
  555. /// </devdoc>
  556. public int IndexOf(DataColumn column) {
  557. int columnCount = _list.Count;
  558. for (int i = 0; i < columnCount; ++i) {
  559. if (column == (DataColumn) _list[i]) {
  560. return i;
  561. }
  562. }
  563. return -1;
  564. }
  565. /// <devdoc>
  566. /// <para>Returns the index of
  567. /// a column specified by name.</para>
  568. /// </devdoc>
  569. public int IndexOf(string columnName) {
  570. if ((null != columnName) && (0 < columnName.Length)) {
  571. int count = Count;
  572. DataColumn column;
  573. if ((columnFromName.TryGetValue(columnName, out column)) && (column != null)) {
  574. for (int j = 0; j < count; j++)
  575. if (column == _list[j]) {
  576. return j;
  577. }
  578. }
  579. else {
  580. int res = IndexOfCaseInsensitive(columnName);
  581. return (res < 0) ? -1 : res;
  582. }
  583. }
  584. return -1;
  585. }
  586. internal int IndexOfCaseInsensitive (string name) {
  587. int hashcode = table.GetSpecialHashCode(name);
  588. int cachedI = -1;
  589. DataColumn column = null;
  590. for (int i = 0; i < Count; i++) {
  591. column = (DataColumn) _list[i];
  592. if ( (hashcode == 0 || column._hashCode == 0 || column._hashCode == hashcode) &&
  593. NamesEqual(column.ColumnName, name, false, table.Locale) != 0 ) {
  594. if (cachedI == -1)
  595. cachedI = i;
  596. else
  597. return -2;
  598. }
  599. }
  600. return cachedI;
  601. }
  602. internal void FinishInitCollection() {
  603. if (delayedAddRangeColumns != null) {
  604. foreach(DataColumn column in delayedAddRangeColumns) {
  605. if (column != null) {
  606. Add(column);
  607. }
  608. }
  609. foreach(DataColumn column in delayedAddRangeColumns) {
  610. if (column != null) {
  611. column.FinishInitInProgress();
  612. }
  613. }
  614. delayedAddRangeColumns = null;
  615. }
  616. }
  617. /// <devdoc>
  618. /// Makes a default name with the given index. e.g. Column1, Column2, ... Columni
  619. /// </devdoc>
  620. private string MakeName(int index) {
  621. if (1 == index) {
  622. return "Column1";
  623. }
  624. return "Column" + index.ToString(System.Globalization.CultureInfo.InvariantCulture);
  625. }
  626. internal void MoveTo(DataColumn column, int newPosition) {
  627. if (0 > newPosition || newPosition > Count -1) {
  628. throw ExceptionBuilder.InvalidOrdinal("ordinal", newPosition);
  629. }
  630. if (column.ImplementsIChangeTracking) {
  631. RemoveColumnsImplementingIChangeTrackingList(column);
  632. }
  633. _list.Remove(column);
  634. _list.Insert(newPosition, column);
  635. int count = _list.Count;
  636. for (int i =0; i < count; i++) {
  637. ((DataColumn) _list[i]).SetOrdinalInternal(i);
  638. }
  639. CheckIChangeTracking(column);
  640. OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, column));
  641. }
  642. /// <devdoc>
  643. /// <para>
  644. /// Raises the <see cref='System.Data.DataColumnCollection.OnCollectionChanged'/> event.
  645. /// </para>
  646. /// </devdoc>
  647. private void OnCollectionChanged(CollectionChangeEventArgs ccevent) {
  648. table.UpdatePropertyDescriptorCollectionCache();
  649. if ((null != ccevent) && !table.SchemaLoading && !table.fInitInProgress) {
  650. DataColumn column = (DataColumn)ccevent.Element;
  651. }
  652. if (onCollectionChangedDelegate != null) {
  653. onCollectionChangedDelegate(this, ccevent);
  654. }
  655. }
  656. /// <devdoc>
  657. /// <para>[To be supplied.]</para>
  658. /// </devdoc>
  659. private void OnCollectionChanging(CollectionChangeEventArgs ccevent) {
  660. if (onCollectionChangingDelegate != null) {
  661. onCollectionChangingDelegate(this, ccevent);
  662. }
  663. }
  664. internal void OnColumnPropertyChanged(CollectionChangeEventArgs ccevent) {
  665. table.UpdatePropertyDescriptorCollectionCache();
  666. if (onColumnPropertyChangedDelegate != null) {
  667. onColumnPropertyChangedDelegate(this, ccevent);
  668. }
  669. }
  670. /// <devdoc>
  671. /// Registers this name as being used in the collection. Will throw an ArgumentException
  672. /// if the name is already being used. Called by Add, All property, and Column.ColumnName property.
  673. /// if the name is equivalent to the next default name to hand out, we increment our defaultNameIndex.
  674. /// NOTE: To add a child table, pass column as null
  675. /// </devdoc>
  676. internal void RegisterColumnName(string name, DataColumn column) {
  677. Debug.Assert (name != null);
  678. try {
  679. columnFromName.Add(name, column);
  680. if (null != column) {
  681. column._hashCode = table.GetSpecialHashCode(name);
  682. }
  683. }
  684. catch (ArgumentException) { // Argument exception means that there is already an existing key
  685. if (columnFromName[name] != null) {
  686. if (column != null) {
  687. throw ExceptionBuilder.CannotAddDuplicate(name);
  688. }
  689. else {
  690. throw ExceptionBuilder.CannotAddDuplicate3(name);
  691. }
  692. }
  693. throw ExceptionBuilder.CannotAddDuplicate2(name);
  694. }
  695. // If we're adding a child table, then update defaultNameIndex to avoid colisions between the child table and auto-generated column names
  696. if ((column == null) && NamesEqual(name, MakeName(defaultNameIndex), true, table.Locale) != 0) {
  697. do {
  698. defaultNameIndex++;
  699. } while (Contains(MakeName(defaultNameIndex)));
  700. }
  701. }
  702. internal bool CanRegisterName(string name) {
  703. Debug.Assert (name != null, "Must specify a name");
  704. return (!columnFromName.ContainsKey(name));
  705. }
  706. /// <devdoc>
  707. /// <para>Removes the specified <see cref='System.Data.DataColumn'/>
  708. /// from the collection.</para>
  709. /// </devdoc>
  710. public void Remove(DataColumn column) {
  711. OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Remove, column));
  712. BaseRemove(column);
  713. ArrayRemove(column);
  714. OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, column));
  715. // if the column is an element decrease the internal dataTable counter
  716. if (column.ColumnMapping == MappingType.Element)
  717. table.ElementColumnCount --;
  718. }
  719. /// <devdoc>
  720. /// <para>Removes the
  721. /// column at the specified index from the collection.</para>
  722. /// </devdoc>
  723. public void RemoveAt(int index) {
  724. DataColumn dc = this[index];
  725. if (dc == null)
  726. throw ExceptionBuilder.ColumnOutOfRange(index);
  727. Remove(dc);
  728. }
  729. /// <devdoc>
  730. /// <para>Removes the
  731. /// column with the specified name from the collection.</para>
  732. /// </devdoc>
  733. public void Remove(string name) {
  734. DataColumn dc = this[name];
  735. if (dc == null)
  736. throw ExceptionBuilder.ColumnNotInTheTable(name, table.TableName);
  737. Remove(dc);
  738. }
  739. /// <devdoc>
  740. /// Unregisters this name as no longer being used in the collection. Called by Remove, All property, and
  741. /// Column.ColumnName property. If the name is equivalent to the last proposed default name, we walk backwards
  742. /// to find the next proper default name to use.
  743. /// </devdoc>
  744. internal void UnregisterName(string name) {
  745. columnFromName.Remove(name);
  746. if (NamesEqual(name, MakeName(defaultNameIndex - 1), true, table.Locale) != 0) {
  747. do {
  748. defaultNameIndex--;
  749. } while (defaultNameIndex > 1 &&
  750. !Contains(MakeName(defaultNameIndex - 1)));
  751. }
  752. }
  753. private void AddColumnsImplementingIChangeTrackingList(DataColumn dataColumn) {
  754. DataColumn[] columns = columnsImplementingIChangeTracking;
  755. DataColumn[] tempColumns = new DataColumn[columns.Length +1];
  756. columns.CopyTo(tempColumns, 0);
  757. tempColumns[columns.Length] = dataColumn;
  758. columnsImplementingIChangeTracking = tempColumns;
  759. }
  760. private void RemoveColumnsImplementingIChangeTrackingList(DataColumn dataColumn) {
  761. DataColumn[] columns = columnsImplementingIChangeTracking;
  762. DataColumn[] tempColumns = new DataColumn[columns.Length - 1];
  763. for(int i = 0, j = 0; i < columns.Length; i++) {
  764. if (columns[i] != dataColumn) {
  765. tempColumns[j++] = columns[i];
  766. }
  767. }
  768. columnsImplementingIChangeTracking = tempColumns;
  769. }
  770. }
  771. }