ConstraintCollection.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. //
  2. // System.Data.ConstraintCollection.cs
  3. //
  4. // Author:
  5. // Franklin Wise <[email protected]>
  6. // Daniel Morgan
  7. //
  8. // (C) Ximian, Inc. 2002
  9. // (C) 2002 Franklin Wise
  10. // (C) 2002 Daniel Morgan
  11. //
  12. //
  13. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  14. //
  15. // Permission is hereby granted, free of charge, to any person obtaining
  16. // a copy of this software and associated documentation files (the
  17. // "Software"), to deal in the Software without restriction, including
  18. // without limitation the rights to use, copy, modify, merge, publish,
  19. // distribute, sublicense, and/or sell copies of the Software, and to
  20. // permit persons to whom the Software is furnished to do so, subject to
  21. // the following conditions:
  22. //
  23. // The above copyright notice and this permission notice shall be
  24. // included in all copies or substantial portions of the Software.
  25. //
  26. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  27. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  28. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  29. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  30. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  31. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  32. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  33. //
  34. using System;
  35. using System.Collections;
  36. using System.ComponentModel;
  37. namespace System.Data {
  38. [Editor]
  39. [Serializable]
  40. internal delegate void DelegateValidateRemoveConstraint(ConstraintCollection sender, Constraint constraintToRemove, ref bool fail,ref string failReason);
  41. /// <summary>
  42. /// hold collection of constraints for data table
  43. /// </summary>
  44. [DefaultEvent ("CollectionChanged")]
  45. [EditorAttribute("Microsoft.VSDesigner.Data.Design.ConstraintsCollectionEditor, "+Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+Consts.AssemblySystem_Drawing )]
  46. #if !NET_2_0
  47. [Serializable]
  48. #endif
  49. public
  50. #if NET_2_0
  51. sealed
  52. #endif
  53. class ConstraintCollection : InternalDataCollectionBase
  54. {
  55. //private bool beginInit = false;
  56. public event CollectionChangeEventHandler CollectionChanged;
  57. private DataTable table;
  58. // Keep reference to most recent constraints passed to AddRange()
  59. // so that they can be added when EndInit() is called.
  60. private Constraint [] _mostRecentConstraints;
  61. //Don't allow public instantiation
  62. //Will be instantianted from DataTable
  63. internal ConstraintCollection(DataTable table){
  64. this.table = table;
  65. }
  66. internal DataTable Table{
  67. get{
  68. return this.table;
  69. }
  70. }
  71. public
  72. #if !NET_2_0
  73. virtual
  74. #endif
  75. Constraint this[string name] {
  76. get {
  77. //If the name is not found we just return null
  78. int index = IndexOf(name); //case insensitive
  79. if (-1 == index) return null;
  80. return this[index];
  81. }
  82. }
  83. public
  84. #if !NET_2_0
  85. virtual
  86. #endif
  87. Constraint this[int index] {
  88. get {
  89. if (index < 0 || index >= List.Count)
  90. throw new IndexOutOfRangeException();
  91. return (Constraint)List[index];
  92. }
  93. }
  94. private void _handleBeforeConstraintNameChange(object sender, string newName)
  95. {
  96. //null or empty
  97. if (newName == null || newName == "")
  98. throw new ArgumentException("ConstraintName cannot be set to null or empty " +
  99. " after it has been added to a ConstraintCollection.");
  100. if (_isDuplicateConstraintName(newName,(Constraint)sender))
  101. throw new DuplicateNameException("Constraint name already exists.");
  102. }
  103. private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison)
  104. {
  105. foreach (Constraint cst in List) {
  106. if (String.Compare (constraintName, cst.ConstraintName, false, Table.Locale) == 0 && cst != excludeFromComparison)
  107. return true;
  108. }
  109. return false;
  110. }
  111. //finds an open name slot of ConstraintXX
  112. //where XX is a number
  113. private string _createNewConstraintName()
  114. {
  115. bool loopAgain = false;
  116. int index = 1;
  117. do
  118. {
  119. loopAgain = false;
  120. foreach (Constraint cst in List)
  121. {
  122. //Case insensitive
  123. if (String.Compare (cst.ConstraintName,
  124. "Constraint" + index,
  125. !Table.CaseSensitive,
  126. Table.Locale)
  127. == 0)
  128. {
  129. loopAgain = true;
  130. index++;
  131. break;
  132. }
  133. }
  134. } while (loopAgain);
  135. return "Constraint" + index.ToString();
  136. }
  137. // Overloaded Add method (5 of them)
  138. // to add Constraint object to the collection
  139. public void Add(Constraint constraint)
  140. {
  141. //not null
  142. if (null == constraint) throw new ArgumentNullException("Can not add null.");
  143. if (constraint.InitInProgress)
  144. throw new ArgumentException ("Hmm .. Failed to Add to collection");
  145. //check constraint membership
  146. //can't already exist in this collection or any other
  147. if (this == constraint.ConstraintCollection)
  148. throw new ArgumentException("Constraint already belongs to this collection.");
  149. if (null != constraint.ConstraintCollection)
  150. throw new ArgumentException("Constraint already belongs to another collection.");
  151. //check for duplicate name
  152. if (_isDuplicateConstraintName(constraint.ConstraintName,null) )
  153. throw new DuplicateNameException("Constraint name already exists.");
  154. //Allow constraint to run validation rules and setup
  155. constraint.AddToConstraintCollectionSetup(this); //may throw if it can't setup
  156. //if name is null or empty give it a name
  157. if (constraint.ConstraintName == null ||
  158. constraint.ConstraintName == "" )
  159. {
  160. constraint.ConstraintName = _createNewConstraintName();
  161. }
  162. //Add event handler for ConstraintName change
  163. constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
  164. _handleBeforeConstraintNameChange);
  165. constraint.ConstraintCollection = this;
  166. List.Add(constraint);
  167. if (constraint is UniqueConstraint && ((UniqueConstraint)constraint).IsPrimaryKey)
  168. table.PrimaryKey = ((UniqueConstraint)constraint).Columns;
  169. OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
  170. }
  171. public
  172. #if !NET_2_0
  173. virtual
  174. #endif
  175. Constraint Add(string name, DataColumn column, bool primaryKey)
  176. {
  177. UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
  178. Add(uc);
  179. return uc;
  180. }
  181. public
  182. #if !NET_2_0
  183. virtual
  184. #endif
  185. Constraint Add(string name, DataColumn primaryKeyColumn,
  186. DataColumn foreignKeyColumn)
  187. {
  188. ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn,
  189. foreignKeyColumn);
  190. Add(fc);
  191. return fc;
  192. }
  193. public
  194. #if !NET_2_0
  195. virtual
  196. #endif
  197. Constraint Add(string name, DataColumn[] columns, bool primaryKey)
  198. {
  199. UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
  200. Add(uc);
  201. return uc;
  202. }
  203. public
  204. #if !NET_2_0
  205. virtual
  206. #endif
  207. Constraint Add(string name, DataColumn[] primaryKeyColumns,
  208. DataColumn[] foreignKeyColumns)
  209. {
  210. ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns,
  211. foreignKeyColumns);
  212. Add(fc);
  213. return fc;
  214. }
  215. public void AddRange(Constraint[] constraints)
  216. {
  217. //When AddRange() occurs after BeginInit,
  218. //it does not add any elements to the collection until EndInit is called.
  219. if (Table.InitInProgress) {
  220. // Keep reference so that they can be added when EndInit() is called.
  221. _mostRecentConstraints = constraints;
  222. return;
  223. }
  224. if (constraints == null)
  225. return;
  226. for (int i=0; i < constraints.Length; ++i) {
  227. if (constraints [i] == null)
  228. continue;
  229. Add (constraints [i]);
  230. }
  231. }
  232. // Helper AddRange() - Call this function when EndInit is called
  233. // keeps track of the Constraints most recently added and adds them
  234. // to the collection
  235. internal void PostAddRange ()
  236. {
  237. if (_mostRecentConstraints == null)
  238. return;
  239. // Check whether the constraint is Initialized
  240. // If not, initialize before adding to collection
  241. for (int i = 0; i < _mostRecentConstraints.Length; i++) {
  242. Constraint c = _mostRecentConstraints [i];
  243. if (c == null)
  244. continue;
  245. if (c.InitInProgress)
  246. c.FinishInit (Table);
  247. Add (c);
  248. }
  249. _mostRecentConstraints = null;
  250. }
  251. public bool CanRemove(Constraint constraint)
  252. {
  253. return constraint.CanRemoveFromCollection(this, false);
  254. }
  255. public void Clear()
  256. {
  257. // Clear should also remove PrimaryKey
  258. Table.PrimaryKey = null;
  259. //CanRemove? See Lamespec below.
  260. //the Constraints have a reference to us
  261. //and we listen to name change events
  262. //we should remove these before clearing
  263. foreach (Constraint con in List)
  264. {
  265. con.ConstraintCollection = null;
  266. con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
  267. _handleBeforeConstraintNameChange);
  268. }
  269. //LAMESPEC: MSFT implementation allows this
  270. //even when a ForeignKeyConstraint exist for a UniqueConstraint
  271. //thus violating the CanRemove logic
  272. //CanRemove will throws Exception incase of the above
  273. List.Clear(); //Will violate CanRemove rule
  274. OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this) );
  275. }
  276. public bool Contains(string name)
  277. {
  278. return (-1 != IndexOf(name));
  279. }
  280. public int IndexOf(Constraint constraint)
  281. {
  282. return List.IndexOf(constraint);
  283. }
  284. public
  285. #if !NET_2_0
  286. virtual
  287. #endif
  288. int IndexOf(string constraintName)
  289. {
  290. //LAMESPEC: Spec doesn't say case insensitive
  291. //it should to be consistant with the other
  292. //case insensitive comparisons in this class
  293. int index = 0;
  294. foreach (Constraint con in List)
  295. {
  296. if (String.Compare (constraintName, con.ConstraintName, !Table.CaseSensitive, Table.Locale) == 0)
  297. {
  298. return index;
  299. }
  300. index++;
  301. }
  302. return -1; //not found
  303. }
  304. public void Remove(Constraint constraint) {
  305. //LAMESPEC: spec doesn't document the ArgumentException the
  306. //will be thrown if the CanRemove rule is violated
  307. //LAMESPEC: spec says an exception will be thrown
  308. //if the element is not in the collection. The implementation
  309. //doesn't throw an exception. ArrayList.Remove doesn't throw if the
  310. //element doesn't exist
  311. //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
  312. //not null
  313. if (null == constraint) throw new ArgumentNullException();
  314. if (!constraint.CanRemoveFromCollection(this, true))
  315. return;
  316. constraint.RemoveFromConstraintCollectionCleanup(this);
  317. List.Remove(constraint);
  318. OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
  319. }
  320. public void Remove(string name)
  321. {
  322. //if doesn't exist fail quietly
  323. int index = IndexOf(name);
  324. if (-1 == index) return;
  325. Remove(this[index]);
  326. }
  327. public void RemoveAt(int index)
  328. {
  329. Remove(this[index]);
  330. }
  331. protected override ArrayList List {
  332. get{
  333. return base.List;
  334. }
  335. }
  336. protected
  337. #if !NET_2_0
  338. virtual
  339. #endif
  340. void OnCollectionChanged( CollectionChangeEventArgs ccevent)
  341. {
  342. if (null != CollectionChanged)
  343. {
  344. CollectionChanged(this, ccevent);
  345. }
  346. }
  347. }
  348. }