2 // System.Data.ConstraintCollection.cs
5 // Franklin Wise <gracenote@earthlink.net>
8 // (C) Ximian, Inc. 2002
9 // (C) 2002 Franklin Wise
10 // (C) 2002 Daniel Morgan
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.Collections;
38 using System.ComponentModel;
40 namespace System.Data {
43 internal delegate void DelegateValidateRemoveConstraint (ConstraintCollection sender, Constraint constraintToRemove, ref bool fail,ref string failReason);
46 /// hold collection of constraints for data table
48 [DefaultEvent ("CollectionChanged")]
49 [Editor ("Microsoft.VSDesigner.Data.Design.ConstraintsCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
50 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
51 public partial class ConstraintCollection : InternalDataCollectionBase {
52 public event CollectionChangeEventHandler CollectionChanged;
53 private DataTable table;
55 // Keep reference to most recent constraints passed to AddRange()
56 // so that they can be added when EndInit() is called.
57 private Constraint [] _mostRecentConstraints;
59 //Don't allow public instantiation
60 //Will be instantianted from DataTable
61 internal ConstraintCollection(DataTable table)
66 internal DataTable Table {
67 get { return this.table; }
74 Constraint this [string name] {
76 int index = IndexOf (name);
77 return -1 == index ? null : (Constraint) List [index];
85 Constraint this [int index] {
87 if (index < 0 || index >= List.Count)
88 throw new IndexOutOfRangeException ();
89 return (Constraint) List [index];
93 private void _handleBeforeConstraintNameChange (object sender, string newName)
95 if (newName == null || newName == "")
96 throw new ArgumentException (
97 "ConstraintName cannot be set to null or empty after adding it to a ConstraintCollection.");
99 if (_isDuplicateConstraintName (newName, (Constraint) sender))
100 throw new DuplicateNameException ("Constraint name already exists.");
103 private bool _isDuplicateConstraintName (string constraintName, Constraint excludeFromComparison)
105 foreach (Constraint cst in List) {
106 if (cst == excludeFromComparison)
108 if (String.Compare (constraintName, cst.ConstraintName, false, Table.Locale) == 0)
115 //finds an open name slot of ConstraintXX
116 //where XX is a number
117 private string _createNewConstraintName ()
119 // FIXME: Do constraint id's need to be reused? This loop is horrendously slow.
120 for (int i = 1; ; ++i) {
121 string new_name = "Constraint" + i;
122 if (IndexOf (new_name) == -1)
128 // Overloaded Add method (5 of them)
129 // to add Constraint object to the collection
131 public void Add (Constraint constraint)
134 if (null == constraint)
135 throw new ArgumentNullException ("Can not add null.");
137 if (constraint.InitInProgress)
138 throw new ArgumentException ("Hmm .. Failed to Add to collection");
140 //check constraint membership
141 //can't already exist in this collection or any other
142 if (this == constraint.ConstraintCollection)
143 throw new ArgumentException ("Constraint already belongs to this collection.");
144 if (null != constraint.ConstraintCollection)
145 throw new ArgumentException ("Constraint already belongs to another collection.");
147 //check if a constraint already exists for the datacolums
148 foreach (Constraint c in this) {
149 if (c.Equals (constraint))
150 throw new DataException (
151 "Constraint matches contraint named '" + c.ConstraintName + "' already in collection");
154 //check for duplicate name
155 if (_isDuplicateConstraintName (constraint.ConstraintName, null))
156 throw new DuplicateNameException ("Constraint name already exists.");
158 //Allow constraint to run validation rules and setup
159 constraint.AddToConstraintCollectionSetup (this); //may throw if it can't setup
161 //if name is null or empty give it a name
162 if (constraint.ConstraintName == null || constraint.ConstraintName == "")
163 constraint.ConstraintName = _createNewConstraintName ();
165 //Add event handler for ConstraintName change
166 constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange (_handleBeforeConstraintNameChange);
168 constraint.ConstraintCollection = this;
169 List.Add (constraint);
171 if (constraint is UniqueConstraint && ((UniqueConstraint) constraint).IsPrimaryKey)
172 table.PrimaryKey = ((UniqueConstraint) constraint).Columns;
174 OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, this));
181 Constraint Add (string name, DataColumn column, bool primaryKey)
183 UniqueConstraint uc = new UniqueConstraint (name, column, primaryKey);
192 Constraint Add (string name, DataColumn primaryKeyColumn, DataColumn foreignKeyColumn)
194 ForeignKeyConstraint fc = new ForeignKeyConstraint (name, primaryKeyColumn, foreignKeyColumn);
203 Constraint Add (string name, DataColumn[] columns, bool primaryKey)
205 UniqueConstraint uc = new UniqueConstraint (name, columns, primaryKey);
214 Constraint Add (string name, DataColumn[] primaryKeyColumns, DataColumn[] foreignKeyColumns)
216 ForeignKeyConstraint fc = new ForeignKeyConstraint (name, primaryKeyColumns, foreignKeyColumns);
221 public void AddRange (Constraint[] constraints)
223 //When AddRange() occurs after BeginInit,
224 //it does not add any elements to the collection until EndInit is called.
225 if (Table.InitInProgress) {
226 // Keep reference so that they can be added when EndInit() is called.
227 _mostRecentConstraints = constraints;
231 if (constraints == null)
234 for (int i = 0; i < constraints.Length; ++i) {
235 if (constraints [i] != null)
236 Add (constraints [i]);
240 // Helper AddRange() - Call this function when EndInit is called
241 // keeps track of the Constraints most recently added and adds them
243 internal void PostAddRange ()
245 if (_mostRecentConstraints == null)
248 // Check whether the constraint is Initialized
249 // If not, initialize before adding to collection
250 for (int i = 0; i < _mostRecentConstraints.Length; i++) {
251 Constraint c = _mostRecentConstraints [i];
254 if (c.InitInProgress)
255 c.FinishInit (Table);
258 _mostRecentConstraints = null;
261 public bool CanRemove (Constraint constraint)
263 return constraint.CanRemoveFromCollection (this, false);
268 // Clear should also remove PrimaryKey
269 Table.PrimaryKey = null;
271 //CanRemove? See Lamespec below.
272 //the Constraints have a reference to us
273 //and we listen to name change events
274 //we should remove these before clearing
275 foreach (Constraint con in List) {
276 con.ConstraintCollection = null;
277 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange (_handleBeforeConstraintNameChange);
280 //LAMESPEC: MSFT implementation allows this
281 //even when a ForeignKeyConstraint exist for a UniqueConstraint
282 //thus violating the CanRemove logic
283 //CanRemove will throws Exception incase of the above
284 List.Clear (); //Will violate CanRemove rule
285 OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, this));
288 public bool Contains (string name)
290 return -1 != IndexOf (name);
293 public int IndexOf (Constraint constraint)
296 foreach (Constraint c in this) {
308 int IndexOf (string constraintName)
310 //LAMESPEC: Spec doesn't say case insensitive
311 //it should to be consistant with the other
312 //case insensitive comparisons in this class
315 foreach (Constraint con in List) {
316 if (String.Compare (constraintName, con.ConstraintName, !Table.CaseSensitive, Table.Locale) == 0)
320 return -1; //not found
323 public void Remove (Constraint constraint)
325 //LAMESPEC: spec doesn't document the ArgumentException the
326 //will be thrown if the CanRemove rule is violated
328 //LAMESPEC: spec says an exception will be thrown
329 //if the element is not in the collection. The implementation
330 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
331 //element doesn't exist
332 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
335 if (null == constraint)
336 throw new ArgumentNullException();
338 if (!constraint.CanRemoveFromCollection (this, true))
341 constraint.RemoveFromConstraintCollectionCleanup (this);
342 constraint.ConstraintCollection = null;
343 List.Remove (constraint);
344 OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, this));
347 public void Remove (string name)
349 int index = IndexOf (name);
351 throw new ArgumentException ("Constraint '" + name + "' does not belong to this DataTable.");
353 Remove (this [index]);
356 public void RemoveAt(int index)
358 Remove (this [index]);
361 protected override ArrayList List {
362 get { return base.List; }
371 void OnCollectionChanged (CollectionChangeEventArgs ccevent)
373 if (null != CollectionChanged)
374 CollectionChanged(this, ccevent);
379 sealed partial class ConstraintCollection {
380 public void CopyTo (Constraint [] array, int index)
382 base.CopyTo (array, index);
387 partial class ConstraintCollection {