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 using System.Collections;
15 using System.ComponentModel;
20 internal delegate void DelegateValidateRemoveConstraint(ConstraintCollection sender,
21 Constraint constraintToRemove, ref bool fail,ref string failReason);
24 /// hold collection of constraints for data table
27 public class ConstraintCollection : InternalDataCollectionBase
29 private bool beginInit = false;
31 public event CollectionChangeEventHandler CollectionChanged;
32 internal event DelegateValidateRemoveConstraint ValidateRemoveConstraint;
34 //Don't allow public instantiation
35 //Will be instantianted from DataTable
36 internal ConstraintCollection(){}
38 public virtual Constraint this[string name] {
40 //If the name is not found we just return null
41 int index = IndexOf(name); //case insensitive
42 if (-1 == index) return null;
47 public virtual Constraint this[int index] {
49 if (index < 0 || index >= List.Count)
50 throw new IndexOutOfRangeException();
51 return (Constraint)List[index];
55 private void _handleBeforeConstraintNameChange(object sender, string newName)
58 if (newName == null || newName == "")
59 throw new ArgumentException("ConstraintName cannot be set to null or empty " +
60 " after it has been added to a ConstraintCollection.");
62 if (_isDuplicateConstraintName(newName,(Constraint)sender))
63 throw new DuplicateNameException("Constraint name already exists.");
66 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison)
68 string cmpr = constraintName.ToUpper();
69 foreach (Constraint cst in List)
71 //Case insensitive comparision
72 if ( cmpr.CompareTo(cst.ConstraintName.ToUpper()) == 0 &&
73 cst != excludeFromComparison)
82 //finds an open name slot of ConstraintXX
83 //where XX is a number
84 private string _createNewConstraintName()
86 bool loopAgain = false;
92 foreach (Constraint cst in List)
95 if (cst.ConstraintName.ToUpper().CompareTo("CONSTRAINT" +
96 index.ToString()) == 0 )
104 return "Constraint" + index.ToString();
109 // Overloaded Add method (5 of them)
110 // to add Constraint object to the collection
112 public void Add(Constraint constraint)
115 if (null == constraint) throw new ArgumentNullException("Can not add null.");
117 //check constraint membership
118 //can't already exist in this collection or any other
119 if (this == constraint.ConstraintCollection)
120 throw new ArgumentException("Constraint already belongs to this collection.");
121 if (null != constraint.ConstraintCollection)
122 throw new ArgumentException("Constraint already belongs to another collection.");
124 //check for duplicate name
125 if (_isDuplicateConstraintName(constraint.ConstraintName,null) )
126 throw new DuplicateNameException("Constraint name already exists.");
128 //Allow constraint to run validation rules and setup
129 constraint.AddToConstraintCollectionSetup(this); //may throw if it can't setup
131 //Run Constraint to check existing data in table
132 constraint.AssertConstraint();
134 //if name is null or empty give it a name
135 if (constraint.ConstraintName == null ||
136 constraint.ConstraintName == "" )
138 constraint.ConstraintName = _createNewConstraintName();
141 //Add event handler for ConstraintName change
142 constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
143 _handleBeforeConstraintNameChange);
145 constraint.ConstraintCollection = this;
146 List.Add(constraint);
148 OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
153 public virtual Constraint Add(string name, DataColumn column, bool primaryKey)
156 UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
162 public virtual Constraint Add(string name, DataColumn primaryKeyColumn,
163 DataColumn foreignKeyColumn)
165 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn,
172 public virtual Constraint Add(string name, DataColumn[] columns, bool primaryKey)
174 UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
180 public virtual Constraint Add(string name, DataColumn[] primaryKeyColumns,
181 DataColumn[] foreignKeyColumns)
183 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns,
191 public void AddRange(Constraint[] constraints) {
193 throw new NotImplementedException ();
196 public bool CanRemove(Constraint constraint)
199 //Rule A UniqueConstraint can't be removed if there is
200 //a foreign key relationship to that column
203 //LAMESPEC: MSFT implementation throws and exception here
204 //spec says nothing about this
205 if (null == constraint) throw new ArgumentNullException("Constraint can't be null.");
207 //LAMESPEC: spec says return false (which makes sense) and throw exception for False case (?).
208 //TODO: I may want to change how this is done
209 //maybe put a CanRemove on the Constraint class
210 //and have the Constraint fire this event
212 //discover if there is a related ForeignKey
213 string failReason ="";
214 return _canRemoveConstraint(constraint, ref failReason);
222 //CanRemove? See Lamespec below.
224 //the Constraints have a reference to us
225 //and we listen to name change events
226 //we should remove these before clearing
227 foreach (Constraint con in List)
229 con.ConstraintCollection = null;
230 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
231 _handleBeforeConstraintNameChange);
234 //LAMESPEC: MSFT implementation allows this
235 //even when a ForeignKeyConstraint exist for a UniqueConstraint
236 //thus violating the CanRemove logic
237 List.Clear(); //Will violate CanRemove rule
238 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this) );
241 public bool Contains(string name)
243 return (-1 != IndexOf(name));
246 public int IndexOf(Constraint constraint)
248 return List.IndexOf(constraint);
251 public virtual int IndexOf(string constraintName)
253 //LAMESPEC: Spec doesn't say case insensitive
254 //it should to be consistant with the other
255 //case insensitive comparisons in this class
258 foreach (Constraint con in List)
260 if (constraintName.ToUpper().CompareTo( con.ConstraintName.ToUpper() ) == 0)
267 return -1; //not found
270 public void Remove(Constraint constraint) {
271 //LAMESPEC: spec doesn't document the ArgumentException the
272 //will be thrown if the CanRemove rule is violated
274 //LAMESPEC: spec says an exception will be thrown
275 //if the element is not in the collection. The implementation
276 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
277 //element doesn't exist
278 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
281 if (null == constraint) throw new ArgumentNullException();
283 string failReason = "";
284 if (! _canRemoveConstraint(constraint, ref failReason) )
286 if (failReason != null || failReason != "")
287 throw new ArgumentException(failReason);
289 throw new ArgumentException("Can't remove constraint.");
292 constraint.RemoveFromConstraintCollectionCleanup(this);
293 List.Remove(constraint);
294 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
297 public void Remove(string name)
299 //if doesn't exist fail quietly
300 int index = IndexOf(name);
301 if (-1 == index) return;
306 public void RemoveAt(int index)
308 if (index < 0 || index + 1 > List.Count)
309 throw new IndexOutOfRangeException("Index out of range, index = "
310 + index.ToString() + ".");
312 this[index].RemoveFromConstraintCollectionCleanup(this);
313 List.RemoveAt(index);
314 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
318 protected override ArrayList List {
324 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent)
326 if (null != CollectionChanged)
328 CollectionChanged(this, ccevent);
332 private bool _canRemoveConstraint(Constraint constraint, ref string failReason )
336 if (null != ValidateRemoveConstraint)
338 ValidateRemoveConstraint(this, constraint, ref cancel, ref tmp);