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 cancel);
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 int index = IndexOf(name);
41 if (-1 == index) return null;
46 public virtual Constraint this[int index] {
48 return (Constraint)List[index];
52 private void _handleBeforeConstraintNameChange(object sender, string newName)
55 if (newName == null || newName == "")
56 throw new ArgumentException("ConstraintName cannot be set to null or empty " +
57 " after it has been added to a ConstraintCollection.");
59 if (_isDuplicateConstraintName(newName,(Constraint)sender))
60 throw new DuplicateNameException("Constraint name already exists.");
63 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison)
65 string cmpr = constraintName.ToUpper();
66 foreach (Constraint cst in List)
68 //Case insensitive comparision
69 if ( cmpr.CompareTo(cst.ConstraintName.ToUpper()) == 0 &&
70 cst != excludeFromComparison)
79 //finds an open name slot of ConstraintXX
80 //where XX is a number
81 private string _createNewConstraintName()
83 bool loopAgain = false;
89 foreach (Constraint cst in List)
92 if (cst.ConstraintName.ToUpper().CompareTo("CONSTRAINT" +
93 index.ToString()) == 0 )
101 return "Constraint" + index.ToString();
106 // Overloaded Add method (5 of them)
107 // to add Constraint object to the collection
109 public void Add(Constraint constraint)
112 if (null == constraint) throw new ArgumentNullException("Can not add null.");
114 //check constraint membership
115 //can't already exist in this collection or any other
116 if (this == constraint.ConstraintCollection)
117 throw new ArgumentException("Constraint already belongs to this collection.");
118 if (null != constraint.ConstraintCollection)
119 throw new ArgumentException("Constraint already belongs to another collection.");
121 //check for duplicate
122 if (_isDuplicateConstraintName(constraint.ConstraintName,null) )
123 throw new DuplicateNameException("Constraint name already exists.");
125 //if name is null or empty give it a name
126 if (constraint.ConstraintName == null ||
127 constraint.ConstraintName == "" )
129 constraint.ConstraintName = _createNewConstraintName();
132 //Add event handler for ConstraintName change
133 constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
134 _handleBeforeConstraintNameChange);
136 constraint.ConstraintCollection = this;
137 List.Add(constraint);
139 OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
144 public virtual Constraint Add(string name, DataColumn column, bool primaryKey)
147 UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
153 public virtual Constraint Add(string name, DataColumn primaryKeyColumn,
154 DataColumn foreignKeyColumn)
156 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn,
163 public virtual Constraint Add(string name, DataColumn[] columns, bool primaryKey)
165 UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
171 public virtual Constraint Add(string name, DataColumn[] primaryKeyColumns,
172 DataColumn[] foreignKeyColumns)
174 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns,
182 public void AddRange(Constraint[] constraints) {
184 throw new NotImplementedException ();
187 public bool CanRemove(Constraint constraint)
190 //Rule A UniqueConstraint can't be removed if there is
191 //a foreign key relationship to that column
194 //LAMESPEC: MSFT implementation throws and exception here
195 //spec says nothing about this
196 if (null == constraint) throw new ArgumentNullException("Constraint can't be null.");
198 //if we are not a unique constraint then it's ok to remove
199 UniqueConstraint uc = constraint as UniqueConstraint;
200 if (null == uc) return true;
202 //LAMESPEC: spec says return false (which makes sense) and throw exception for False case (?).
203 //discover if there is a related ForeignKey
204 return _canRemoveConstraint(constraint);
212 //CanRemove? See Lamespec below.
214 //the Constraints have a reference to us
215 //and we listen to name change events
216 //we should remove these before clearing
217 foreach (Constraint con in List)
219 con.ConstraintCollection = null;
220 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
221 _handleBeforeConstraintNameChange);
224 //LAMESPEC: MSFT implementation allows this
225 //even when a ForeignKeyConstraint exist for a UniqueConstraint
226 //thus violating the CanRemove logic
227 List.Clear(); //Will violate CanRemove rule
228 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this) );
231 public bool Contains(string name)
233 return (-1 != IndexOf(name));
236 public int IndexOf(Constraint constraint)
238 return List.IndexOf(constraint);
241 public virtual int IndexOf(string constraintName)
243 //LAMESPEC: Spec doesn't say case insensitive
244 //it should to be consistant with the other
245 //case insensitive comparisons in this class
248 foreach (Constraint con in List)
250 if (constraintName.ToUpper().CompareTo( con.ConstraintName.ToUpper() ) == 0)
257 return -1; //not found
260 public void Remove(Constraint constraint) {
261 //LAMESPEC: spec doesn't document the ArgumentException the
262 //will be thrown if the CanRemove rule is violated
264 //LAMESPEC: spec says an exception will be thrown
265 //if the element is not in the collection. The implementation
266 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
267 //element doesn't exist
268 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
271 if (null == constraint) throw new ArgumentNullException();
273 List.Remove(constraint);
274 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
277 public void Remove(string name)
279 //if doesn't exist fail quietly
280 int index = IndexOf(name);
281 if (-1 == index) return;
286 public void RemoveAt(int index)
288 if (index < 0 || index + 1 > List.Count)
289 throw new IndexOutOfRangeException("Index out of range, index = "
290 + index.ToString() + ".");
292 List.RemoveAt(index);
293 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
297 protected override ArrayList List {
303 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent)
305 if (null != CollectionChanged)
307 CollectionChanged(this, ccevent);
311 private bool _canRemoveConstraint(Constraint constraint )
314 if (null != ValidateRemoveConstraint)
316 ValidateRemoveConstraint(this, constraint, ref cancel);