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")]
26 [EditorAttribute("Microsoft.VSDesigner.Data.Design.ConstraintsCollectionEditor, "+Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+Consts.AssemblySystem_Drawing )]
28 public class ConstraintCollection : InternalDataCollectionBase
30 //private bool beginInit = false;
32 public event CollectionChangeEventHandler CollectionChanged;
33 internal event DelegateValidateRemoveConstraint ValidateRemoveConstraint;
34 private DataTable table;
35 //Don't allow public instantiation
36 //Will be instantianted from DataTable
37 internal ConstraintCollection(DataTable table){
41 internal DataTable Table{
47 public virtual Constraint this[string name] {
49 //If the name is not found we just return null
50 int index = IndexOf(name); //case insensitive
51 if (-1 == index) return null;
56 public virtual Constraint this[int index] {
58 if (index < 0 || index >= List.Count)
59 throw new IndexOutOfRangeException();
60 return (Constraint)List[index];
64 private void _handleBeforeConstraintNameChange(object sender, string newName)
67 if (newName == null || newName == "")
68 throw new ArgumentException("ConstraintName cannot be set to null or empty " +
69 " after it has been added to a ConstraintCollection.");
71 if (_isDuplicateConstraintName(newName,(Constraint)sender))
72 throw new DuplicateNameException("Constraint name already exists.");
75 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison)
77 string cmpr = constraintName.ToUpper();
78 foreach (Constraint cst in List)
80 //Case insensitive comparision
81 if ( cmpr.CompareTo(cst.ConstraintName.ToUpper()) == 0 &&
82 cst != excludeFromComparison)
91 //finds an open name slot of ConstraintXX
92 //where XX is a number
93 private string _createNewConstraintName()
95 bool loopAgain = false;
101 foreach (Constraint cst in List)
104 if (cst.ConstraintName.ToUpper().CompareTo("CONSTRAINT" +
105 index.ToString()) == 0 )
113 return "Constraint" + index.ToString();
118 // Overloaded Add method (5 of them)
119 // to add Constraint object to the collection
121 public void Add(Constraint constraint)
124 if (null == constraint) throw new ArgumentNullException("Can not add null.");
126 //check constraint membership
127 //can't already exist in this collection or any other
128 if (this == constraint.ConstraintCollection)
129 throw new ArgumentException("Constraint already belongs to this collection.");
130 if (null != constraint.ConstraintCollection)
131 throw new ArgumentException("Constraint already belongs to another collection.");
133 //check for duplicate name
134 if (_isDuplicateConstraintName(constraint.ConstraintName,null) )
135 throw new DuplicateNameException("Constraint name already exists.");
137 //Allow constraint to run validation rules and setup
138 constraint.AddToConstraintCollectionSetup(this); //may throw if it can't setup
140 //Run Constraint to check existing data in table
141 // this is redundant, since AddToConstraintCollectionSetup
142 // calls AssertConstraint right before this call
143 //constraint.AssertConstraint();
145 //if name is null or empty give it a name
146 if (constraint.ConstraintName == null ||
147 constraint.ConstraintName == "" )
149 constraint.ConstraintName = _createNewConstraintName();
152 //Add event handler for ConstraintName change
153 constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
154 _handleBeforeConstraintNameChange);
156 constraint.ConstraintCollection = this;
157 List.Add(constraint);
159 if (constraint is UniqueConstraint)
160 ((UniqueConstraint)constraint).UpdatePrimaryKey();
162 OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
167 public virtual Constraint Add(string name, DataColumn column, bool primaryKey)
170 UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
176 public virtual Constraint Add(string name, DataColumn primaryKeyColumn,
177 DataColumn foreignKeyColumn)
179 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn,
186 public virtual Constraint Add(string name, DataColumn[] columns, bool primaryKey)
188 UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
194 public virtual Constraint Add(string name, DataColumn[] primaryKeyColumns,
195 DataColumn[] foreignKeyColumns)
197 ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns,
204 public void AddRange(Constraint[] constraints) {
206 //FIXME: When AddRange() occurs after BeginInit,
207 //it does not add any elements to the collection until EndInit is called.
209 if ( (constraints == null) || (constraints.Length == 0))
210 throw new ArgumentNullException ("Cannot add null");
212 foreach (Constraint constraint in constraints)
218 public bool CanRemove(Constraint constraint)
221 //Rule A UniqueConstraint can't be removed if there is
222 //a foreign key relationship to that column
225 //LAMESPEC: MSFT implementation throws and exception here
226 //spec says nothing about this
227 if (null == constraint) throw new ArgumentNullException("Constraint can't be null.");
229 //LAMESPEC: spec says return false (which makes sense) and throw exception for False case (?).
230 //TODO: I may want to change how this is done
231 //maybe put a CanRemove on the Constraint class
232 //and have the Constraint fire this event
234 //discover if there is a related ForeignKey
235 string failReason ="";
236 return _canRemoveConstraint(constraint, ref failReason);
243 //CanRemove? See Lamespec below.
245 //the Constraints have a reference to us
246 //and we listen to name change events
247 //we should remove these before clearing
248 foreach (Constraint con in List)
250 con.ConstraintCollection = null;
251 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
252 _handleBeforeConstraintNameChange);
255 //LAMESPEC: MSFT implementation allows this
256 //even when a ForeignKeyConstraint exist for a UniqueConstraint
257 //thus violating the CanRemove logic
258 //CanRemove will throws Exception incase of the above
259 List.Clear(); //Will violate CanRemove rule
260 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this) );
263 public bool Contains(string name)
265 return (-1 != IndexOf(name));
268 public int IndexOf(Constraint constraint)
270 return List.IndexOf(constraint);
273 public virtual int IndexOf(string constraintName)
275 //LAMESPEC: Spec doesn't say case insensitive
276 //it should to be consistant with the other
277 //case insensitive comparisons in this class
280 foreach (Constraint con in List)
282 if (constraintName.ToUpper().CompareTo( con.ConstraintName.ToUpper() ) == 0)
289 return -1; //not found
292 public void Remove(Constraint constraint) {
293 //LAMESPEC: spec doesn't document the ArgumentException the
294 //will be thrown if the CanRemove rule is violated
296 //LAMESPEC: spec says an exception will be thrown
297 //if the element is not in the collection. The implementation
298 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
299 //element doesn't exist
300 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
303 if (null == constraint) throw new ArgumentNullException();
305 string failReason = "";
306 if (! _canRemoveConstraint(constraint, ref failReason) )
308 if (failReason != null || failReason != "")
309 throw new ArgumentException(failReason);
311 throw new ArgumentException("Can't remove constraint.");
314 constraint.RemoveFromConstraintCollectionCleanup(this);
315 List.Remove(constraint);
316 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
319 public void Remove(string name)
321 //if doesn't exist fail quietly
322 int index = IndexOf(name);
323 if (-1 == index) return;
328 public void RemoveAt(int index)
333 protected override ArrayList List {
339 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent)
341 if (null != CollectionChanged)
343 CollectionChanged(this, ccevent);
347 private bool _canRemoveConstraint(Constraint constraint, ref string failReason )
351 if (null != ValidateRemoveConstraint)
353 ValidateRemoveConstraint(this, constraint, ref cancel, ref tmp);
359 internal ICollection UniqueConstraints
363 return GetConstraintsCollection(typeof(UniqueConstraint));
367 internal ICollection ForeignKeyConstraints
371 return GetConstraintsCollection(typeof(ForeignKeyConstraint));
375 private ICollection GetConstraintsCollection (Type constraintType)
377 ArrayList cCollection = new ArrayList();
378 foreach (Constraint c in List)
380 if (c.GetType() == constraintType)