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 [EditorAttribute("Microsoft.VSDesigner.Data.Design.ConstraintsCollectionEditor, "+Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+Consts.AssemblySystem_Drawing )]
57 class ConstraintCollection : InternalDataCollectionBase
59 //private bool beginInit = false;
61 public event CollectionChangeEventHandler CollectionChanged;
62 private DataTable table;
64 // Keep reference to most recent constraints passed to AddRange()
65 // so that they can be added when EndInit() is called.
66 private Constraint [] _mostRecentConstraints;
68 //Don't allow public instantiation
69 //Will be instantianted from DataTable
70 internal ConstraintCollection(DataTable table){
74 internal DataTable Table{
84 Constraint this[string name] {
86 //If the name is not found we just return null
87 int index = IndexOf(name); //case insensitive
88 if (-1 == index) return null;
97 Constraint this[int index] {
99 if (index < 0 || index >= List.Count)
100 throw new IndexOutOfRangeException();
101 return (Constraint)List[index];
105 private void _handleBeforeConstraintNameChange(object sender, string newName)
108 if (newName == null || newName == "")
109 throw new ArgumentException("ConstraintName cannot be set to null or empty " +
110 " after it has been added to a ConstraintCollection.");
112 if (_isDuplicateConstraintName(newName,(Constraint)sender))
113 throw new DuplicateNameException("Constraint name already exists.");
116 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison)
118 foreach (Constraint cst in List) {
119 if (String.Compare (constraintName, cst.ConstraintName, false, Table.Locale) == 0 && cst != excludeFromComparison)
126 //finds an open name slot of ConstraintXX
127 //where XX is a number
128 private string _createNewConstraintName()
130 bool loopAgain = false;
136 foreach (Constraint cst in List)
139 if (String.Compare (cst.ConstraintName,
140 "Constraint" + index,
141 !Table.CaseSensitive,
152 return "Constraint" + index.ToString();
157 // Overloaded Add method (5 of them)
158 // to add Constraint object to the collection
160 public void Add(Constraint constraint)
163 if (null == constraint) throw new ArgumentNullException("Can not add null.");
165 if (constraint.InitInProgress)
166 throw new ArgumentException ("Hmm .. Failed to Add to collection");
168 //check constraint membership
169 //can't already exist in this collection or any other
170 if (this == constraint.ConstraintCollection)
171 throw new ArgumentException("Constraint already belongs to this collection.");
172 if (null != constraint.ConstraintCollection)
173 throw new ArgumentException("Constraint already belongs to another collection.");
175 //check if a constraint already exists for the datacolums
176 foreach (Constraint c in this) {
177 if (!c.Equals (constraint))
179 throw new DataException ("Constraint matches contraint named '" + c.ConstraintName
180 + "' already in collection");
183 //check for duplicate name
184 if (_isDuplicateConstraintName(constraint.ConstraintName,null) )
185 throw new DuplicateNameException("Constraint name already exists.");
187 //Allow constraint to run validation rules and setup
188 constraint.AddToConstraintCollectionSetup(this); //may throw if it can't setup
190 //if name is null or empty give it a name
191 if (constraint.ConstraintName == null ||
192 constraint.ConstraintName == "" )
194 constraint.ConstraintName = _createNewConstraintName();
197 //Add event handler for ConstraintName change
198 constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
199 _handleBeforeConstraintNameChange);
201 constraint.ConstraintCollection = this;
202 List.Add(constraint);
204 if (constraint is UniqueConstraint && ((UniqueConstraint)constraint).IsPrimaryKey)
205 table.PrimaryKey = ((UniqueConstraint)constraint).Columns;
207 OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
214 Constraint Add(string name, DataColumn column, bool primaryKey)
217 UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
227 Constraint Add(string name, DataColumn primaryKeyColumn,
228 DataColumn foreignKeyColumn)
230 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn,
241 Constraint Add(string name, DataColumn[] columns, bool primaryKey)
243 UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
253 Constraint Add(string name, DataColumn[] primaryKeyColumns,
254 DataColumn[] foreignKeyColumns)
256 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns,
263 public void AddRange(Constraint[] constraints)
265 //When AddRange() occurs after BeginInit,
266 //it does not add any elements to the collection until EndInit is called.
267 if (Table.InitInProgress) {
268 // Keep reference so that they can be added when EndInit() is called.
269 _mostRecentConstraints = constraints;
273 if (constraints == null)
276 for (int i=0; i < constraints.Length; ++i) {
277 if (constraints [i] == null)
279 Add (constraints [i]);
283 // Helper AddRange() - Call this function when EndInit is called
284 // keeps track of the Constraints most recently added and adds them
286 internal void PostAddRange ()
288 if (_mostRecentConstraints == null)
291 // Check whether the constraint is Initialized
292 // If not, initialize before adding to collection
293 for (int i = 0; i < _mostRecentConstraints.Length; i++) {
294 Constraint c = _mostRecentConstraints [i];
297 if (c.InitInProgress)
298 c.FinishInit (Table);
301 _mostRecentConstraints = null;
304 public bool CanRemove(Constraint constraint)
306 return constraint.CanRemoveFromCollection(this, false);
311 // Clear should also remove PrimaryKey
312 Table.PrimaryKey = null;
314 //CanRemove? See Lamespec below.
315 //the Constraints have a reference to us
316 //and we listen to name change events
317 //we should remove these before clearing
318 foreach (Constraint con in List)
320 con.ConstraintCollection = null;
321 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
322 _handleBeforeConstraintNameChange);
325 //LAMESPEC: MSFT implementation allows this
326 //even when a ForeignKeyConstraint exist for a UniqueConstraint
327 //thus violating the CanRemove logic
328 //CanRemove will throws Exception incase of the above
329 List.Clear(); //Will violate CanRemove rule
330 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this) );
333 public bool Contains(string name)
335 return (-1 != IndexOf(name));
338 public int IndexOf(Constraint constraint)
341 foreach (Constraint c in this) {
353 int IndexOf(string constraintName)
355 //LAMESPEC: Spec doesn't say case insensitive
356 //it should to be consistant with the other
357 //case insensitive comparisons in this class
360 foreach (Constraint con in List)
362 if (String.Compare (constraintName, con.ConstraintName, !Table.CaseSensitive, Table.Locale) == 0)
369 return -1; //not found
372 public void Remove(Constraint constraint) {
373 //LAMESPEC: spec doesn't document the ArgumentException the
374 //will be thrown if the CanRemove rule is violated
376 //LAMESPEC: spec says an exception will be thrown
377 //if the element is not in the collection. The implementation
378 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
379 //element doesn't exist
380 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
383 if (null == constraint) throw new ArgumentNullException();
385 if (!constraint.CanRemoveFromCollection(this, true))
388 constraint.RemoveFromConstraintCollectionCleanup(this);
389 List.Remove(constraint);
390 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
393 public void Remove(string name)
395 //if doesn't exist fail quietly
396 int index = IndexOf(name);
397 if (-1 == index) return;
402 public void RemoveAt(int index)
407 protected override ArrayList List {
417 void OnCollectionChanged( CollectionChangeEventArgs ccevent)
419 if (null != CollectionChanged)
421 CollectionChanged(this, ccevent);