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;
17 namespace System.Data {
20 internal delegate void DelegateValidateRemoveConstraint(ConstraintCollection sender, Constraint constraintToRemove, ref bool fail,ref string failReason);
23 /// hold collection of constraints for data table
25 [DefaultEvent ("CollectionChanged")]
27 public class ConstraintCollection : InternalDataCollectionBase
29 //private bool beginInit = false;
31 public event CollectionChangeEventHandler CollectionChanged;
32 internal event DelegateValidateRemoveConstraint ValidateRemoveConstraint;
33 private DataTable table;
34 //Don't allow public instantiation
35 //Will be instantianted from DataTable
36 internal ConstraintCollection(DataTable table){
40 internal DataTable Table{
46 public virtual Constraint this[string name] {
48 //If the name is not found we just return null
49 int index = IndexOf(name); //case insensitive
50 if (-1 == index) return null;
55 public virtual Constraint this[int index] {
57 if (index < 0 || index >= List.Count)
58 throw new IndexOutOfRangeException();
59 return (Constraint)List[index];
63 private void _handleBeforeConstraintNameChange(object sender, string newName)
66 if (newName == null || newName == "")
67 throw new ArgumentException("ConstraintName cannot be set to null or empty " +
68 " after it has been added to a ConstraintCollection.");
70 if (_isDuplicateConstraintName(newName,(Constraint)sender))
71 throw new DuplicateNameException("Constraint name already exists.");
74 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison)
76 string cmpr = constraintName.ToUpper();
77 foreach (Constraint cst in List)
79 //Case insensitive comparision
80 if ( cmpr.CompareTo(cst.ConstraintName.ToUpper()) == 0 &&
81 cst != excludeFromComparison)
90 //finds an open name slot of ConstraintXX
91 //where XX is a number
92 private string _createNewConstraintName()
94 bool loopAgain = false;
100 foreach (Constraint cst in List)
103 if (cst.ConstraintName.ToUpper().CompareTo("CONSTRAINT" +
104 index.ToString()) == 0 )
112 return "Constraint" + index.ToString();
117 // Overloaded Add method (5 of them)
118 // to add Constraint object to the collection
120 public void Add(Constraint constraint)
123 if (null == constraint) throw new ArgumentNullException("Can not add null.");
125 //check constraint membership
126 //can't already exist in this collection or any other
127 if (this == constraint.ConstraintCollection)
128 throw new ArgumentException("Constraint already belongs to this collection.");
129 if (null != constraint.ConstraintCollection)
130 throw new ArgumentException("Constraint already belongs to another collection.");
132 //check for duplicate name
133 if (_isDuplicateConstraintName(constraint.ConstraintName,null) )
134 throw new DuplicateNameException("Constraint name already exists.");
136 //Allow constraint to run validation rules and setup
137 constraint.AddToConstraintCollectionSetup(this); //may throw if it can't setup
139 //Run Constraint to check existing data in table
140 // this is redundant, since AddToConstraintCollectionSetup
141 // calls AssertConstraint right before this call
142 //constraint.AssertConstraint();
144 //if name is null or empty give it a name
145 if (constraint.ConstraintName == null ||
146 constraint.ConstraintName == "" )
148 constraint.ConstraintName = _createNewConstraintName();
151 //Add event handler for ConstraintName change
152 constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
153 _handleBeforeConstraintNameChange);
155 constraint.ConstraintCollection = this;
156 List.Add(constraint);
158 if (constraint is UniqueConstraint)
159 ((UniqueConstraint)constraint).UpdatePrimaryKey();
161 OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
166 public virtual Constraint Add(string name, DataColumn column, bool primaryKey)
169 UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
175 public virtual Constraint Add(string name, DataColumn primaryKeyColumn,
176 DataColumn foreignKeyColumn)
178 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn,
185 public virtual Constraint Add(string name, DataColumn[] columns, bool primaryKey)
187 UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
193 public virtual Constraint Add(string name, DataColumn[] primaryKeyColumns,
194 DataColumn[] foreignKeyColumns)
196 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns,
204 public void AddRange(Constraint[] constraints) {
206 throw new NotImplementedException ();
209 public bool CanRemove(Constraint constraint)
212 //Rule A UniqueConstraint can't be removed if there is
213 //a foreign key relationship to that column
216 //LAMESPEC: MSFT implementation throws and exception here
217 //spec says nothing about this
218 if (null == constraint) throw new ArgumentNullException("Constraint can't be null.");
220 //LAMESPEC: spec says return false (which makes sense) and throw exception for False case (?).
221 //TODO: I may want to change how this is done
222 //maybe put a CanRemove on the Constraint class
223 //and have the Constraint fire this event
225 //discover if there is a related ForeignKey
226 string failReason ="";
227 return _canRemoveConstraint(constraint, ref failReason);
235 //CanRemove? See Lamespec below.
237 //the Constraints have a reference to us
238 //and we listen to name change events
239 //we should remove these before clearing
240 foreach (Constraint con in List)
242 con.ConstraintCollection = null;
243 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
244 _handleBeforeConstraintNameChange);
247 //LAMESPEC: MSFT implementation allows this
248 //even when a ForeignKeyConstraint exist for a UniqueConstraint
249 //thus violating the CanRemove logic
250 List.Clear(); //Will violate CanRemove rule
251 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this) );
254 public bool Contains(string name)
256 return (-1 != IndexOf(name));
259 public int IndexOf(Constraint constraint)
261 return List.IndexOf(constraint);
264 public virtual int IndexOf(string constraintName)
266 //LAMESPEC: Spec doesn't say case insensitive
267 //it should to be consistant with the other
268 //case insensitive comparisons in this class
271 foreach (Constraint con in List)
273 if (constraintName.ToUpper().CompareTo( con.ConstraintName.ToUpper() ) == 0)
280 return -1; //not found
283 public void Remove(Constraint constraint) {
284 //LAMESPEC: spec doesn't document the ArgumentException the
285 //will be thrown if the CanRemove rule is violated
287 //LAMESPEC: spec says an exception will be thrown
288 //if the element is not in the collection. The implementation
289 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
290 //element doesn't exist
291 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
294 if (null == constraint) throw new ArgumentNullException();
296 string failReason = "";
297 if (! _canRemoveConstraint(constraint, ref failReason) )
299 if (failReason != null || failReason != "")
300 throw new ArgumentException(failReason);
302 throw new ArgumentException("Can't remove constraint.");
305 constraint.RemoveFromConstraintCollectionCleanup(this);
306 List.Remove(constraint);
307 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
310 public void Remove(string name)
312 //if doesn't exist fail quietly
313 int index = IndexOf(name);
314 if (-1 == index) return;
319 public void RemoveAt(int index)
324 protected override ArrayList List {
330 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent)
332 if (null != CollectionChanged)
334 CollectionChanged(this, ccevent);
338 private bool _canRemoveConstraint(Constraint constraint, ref string failReason )
342 if (null != ValidateRemoveConstraint)
344 ValidateRemoveConstraint(this, constraint, ref cancel, ref tmp);
350 internal ICollection UniqueConstraints
354 return GetConstraintsCollection(typeof(UniqueConstraint));
358 internal ICollection ForeignKeyConstraints
362 return GetConstraintsCollection(typeof(ForeignKeyConstraint));
366 private ICollection GetConstraintsCollection (Type constraintType)
368 ArrayList cCollection = new ArrayList();
369 foreach (Constraint c in List)
371 if (c.GetType() == constraintType)