2002-08-12 Franklin Wise <gracenote@earthlink.net>
[mono.git] / mcs / class / System.Data / System.Data / ConstraintCollection.cs
1 //
2 // System.Data.ConstraintCollection.cs
3 //
4 // Author:
5 //   Franklin Wise <gracenote@earthlink.net>
6 //   Daniel Morgan
7 //   
8 // (C) Ximian, Inc. 2002
9 // (C) 2002 Franklin Wise
10 // (C) 2002 Daniel Morgan
11 //
12
13 using System;
14 using System.Collections;
15 using System.ComponentModel;
16
17 namespace System.Data
18 {
19         [Serializable]
20         internal delegate void DelegateValidateRemoveConstraint(ConstraintCollection sender,
21                         Constraint constraintToRemove, ref bool cancel);
22         
23         /// <summary>
24         /// hold collection of constraints for data table
25         /// </summary>
26         [Serializable]
27         public class ConstraintCollection : InternalDataCollectionBase 
28         {
29                 private bool beginInit = false;
30                 
31                 public event CollectionChangeEventHandler CollectionChanged;
32                 internal event DelegateValidateRemoveConstraint ValidateRemoveConstraint;
33                 
34                 //Don't allow public instantiation
35                 //Will be instantianted from DataTable
36                 internal ConstraintCollection(){} 
37
38                 public virtual Constraint this[string name] {
39                         get {
40                                 int index = IndexOf(name);
41                                 if (-1 == index) return null;
42                                 return this[index];
43                         }
44                 }
45                 
46                 public virtual Constraint this[int index] {
47                         get {
48                                 return (Constraint)List[index];
49                         }
50                 }
51
52                 private void _handleBeforeConstraintNameChange(object sender, string newName)
53                 {
54                         //null or empty
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.");
58
59                         if (_isDuplicateConstraintName(newName,(Constraint)sender))
60                                 throw new DuplicateNameException("Constraint name already exists.");
61                 }
62
63                 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison) 
64                 {       
65                         string cmpr = constraintName.ToUpper();
66                         foreach (Constraint cst in List) 
67                         {
68                                 //Case insensitive comparision
69                                 if (  cmpr.CompareTo(cst.ConstraintName.ToUpper()) == 0  &&
70                                         cst != excludeFromComparison) 
71                                 {
72                                         return true;
73                                 }
74                         }
75
76                         return false;
77                 }
78                 
79                 //finds an open name slot of ConstraintXX
80                 //where XX is a number
81                 private string _createNewConstraintName() 
82                 {
83                         bool loopAgain = false;
84                         int index = 1;
85
86                         do
87                         {       
88                                 loopAgain = false;
89                                 foreach (Constraint cst in List) 
90                                 {
91                                         //Case insensitive
92                                         if (cst.ConstraintName.ToUpper().CompareTo("CONSTRAINT" + 
93                                                 index.ToString()) == 0 ) 
94                                         {
95                                                 loopAgain = true;
96                                                 index++;
97                                         }
98                                 }
99                         } while (loopAgain);
100
101                         return "Constraint" + index.ToString();         
102                         
103                 }
104                 
105                 
106                 // Overloaded Add method (5 of them)
107                 // to add Constraint object to the collection
108
109                 public void Add(Constraint constraint) 
110                 {               
111                         //not null
112                         if (null == constraint) throw new ArgumentNullException("Can not add null.");
113                         
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.");
120                         
121                         //check for duplicate
122                         if (_isDuplicateConstraintName(constraint.ConstraintName,null)  )
123                                 throw new DuplicateNameException("Constraint name already exists.");
124                 
125                         //if name is null or empty give it a name
126                         if (constraint.ConstraintName == null || 
127                                 constraint.ConstraintName == "" ) 
128                         { 
129                                 constraint.ConstraintName = _createNewConstraintName();
130                         }
131
132                         //Add event handler for ConstraintName change
133                         constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
134                                 _handleBeforeConstraintNameChange);
135                         
136                         constraint.ConstraintCollection = this;
137                         List.Add(constraint);
138
139                         OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
140                 }
141
142         
143
144                 public virtual Constraint Add(string name, DataColumn column, bool primaryKey) 
145                 {
146
147                         UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
148                         Add(uc);
149                          
150                         return uc;
151                 }
152
153                 public virtual Constraint Add(string name, DataColumn primaryKeyColumn,
154                         DataColumn foreignKeyColumn) 
155                 {
156                         ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn, 
157                                         foreignKeyColumn);
158                         Add(fc);
159
160                         return fc;
161                 }
162
163                 public virtual Constraint Add(string name, DataColumn[] columns, bool primaryKey) 
164                 {
165                         UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
166                         Add(uc);
167
168                         return uc;
169                 }
170
171                 public virtual Constraint Add(string name, DataColumn[] primaryKeyColumns,
172                         DataColumn[] foreignKeyColumns) 
173                 {
174                         ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns, 
175                                         foreignKeyColumns);
176                         Add(fc);
177
178                         return fc;
179                 }
180
181                 [MonoTODO]
182                 public void AddRange(Constraint[] constraints) {
183
184                         throw new NotImplementedException ();
185                 }
186
187                 public bool CanRemove(Constraint constraint) 
188                 {
189
190                         //Rule A UniqueConstraint can't be removed if there is
191                         //a foreign key relationship to that column
192
193                         //not null 
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.");
197                         
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;
201
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);
205                         
206                 }
207
208                 [MonoTODO]
209                 public void Clear() 
210                 {
211                         
212                         //CanRemove? See Lamespec below.
213
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)
218                         {
219                                 con.ConstraintCollection = null;
220                                 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
221                                 _handleBeforeConstraintNameChange);
222                         }
223
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) );
229                 }
230
231                 public bool Contains(string name) 
232                 {
233                         return (-1 != IndexOf(name));
234                 }
235
236                 public int IndexOf(Constraint constraint) 
237                 {
238                         return List.IndexOf(constraint);
239                 }
240
241                 public virtual int IndexOf(string constraintName) 
242                 {
243                         //LAMESPEC: Spec doesn't say case insensitive
244                         //it should to be consistant with the other 
245                         //case insensitive comparisons in this class
246
247                         int index = 0;
248                         foreach (Constraint con in List)
249                         {
250                                 if (constraintName.ToUpper().CompareTo( con.ConstraintName.ToUpper() ) == 0)
251                                 {
252                                         return index;
253                                 }
254
255                                 index++;
256                         }
257                         return -1; //not found
258                 }
259
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
263                         
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
269
270                         //not null
271                         if (null == constraint) throw new ArgumentNullException();
272
273                         List.Remove(constraint);
274                         OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
275                 }
276
277                 public void Remove(string name) 
278                 {
279                         //if doesn't exist fail quietly
280                         int index = IndexOf(name);
281                         if (-1 == index) return;
282
283                         Remove(this[index]);
284                 }
285
286                 public void RemoveAt(int index) 
287                 {
288                         if (index < 0 || index + 1 > List.Count)
289                                 throw new IndexOutOfRangeException("Index out of range, index = " 
290                                                 + index.ToString() + ".");
291         
292                         List.RemoveAt(index);
293                         OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
294                 }
295
296
297                 protected override ArrayList List {
298                         get{
299                                 return base.List;
300                         }
301                 }
302
303                 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent) 
304                 {
305                         if (null != CollectionChanged)
306                         {
307                                 CollectionChanged(this, ccevent);
308                         }
309                 }
310
311                 private bool _canRemoveConstraint(Constraint constraint )
312                 {
313                         bool cancel = false;
314                         if (null != ValidateRemoveConstraint)
315                         {
316                                 ValidateRemoveConstraint(this, constraint, ref cancel);
317                         }
318                         return !cancel;
319                 }
320         }
321 }