*** empty log message ***
[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 fail,ref string failReason);
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                                 //If the name is not found we just return null
41                                 int index = IndexOf(name); //case insensitive
42                                 if (-1 == index) return null;
43                                 return this[index];
44                         }
45                 }
46                 
47                 public virtual Constraint this[int index] {
48                         get {
49                                 if (index < 0 || index >= List.Count)
50                                         throw new IndexOutOfRangeException();
51                                 return (Constraint)List[index];
52                         }
53                 }
54
55                 private void _handleBeforeConstraintNameChange(object sender, string newName)
56                 {
57                         //null or empty
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.");
61
62                         if (_isDuplicateConstraintName(newName,(Constraint)sender))
63                                 throw new DuplicateNameException("Constraint name already exists.");
64                 }
65
66                 private bool _isDuplicateConstraintName(string constraintName, Constraint excludeFromComparison) 
67                 {       
68                         string cmpr = constraintName.ToUpper();
69                         foreach (Constraint cst in List) 
70                         {
71                                 //Case insensitive comparision
72                                 if (  cmpr.CompareTo(cst.ConstraintName.ToUpper()) == 0  &&
73                                         cst != excludeFromComparison) 
74                                 {
75                                         return true;
76                                 }
77                         }
78
79                         return false;
80                 }
81                 
82                 //finds an open name slot of ConstraintXX
83                 //where XX is a number
84                 private string _createNewConstraintName() 
85                 {
86                         bool loopAgain = false;
87                         int index = 1;
88
89                         do
90                         {       
91                                 loopAgain = false;
92                                 foreach (Constraint cst in List) 
93                                 {
94                                         //Case insensitive
95                                         if (cst.ConstraintName.ToUpper().CompareTo("CONSTRAINT" + 
96                                                 index.ToString()) == 0 ) 
97                                         {
98                                                 loopAgain = true;
99                                                 index++;
100                                         }
101                                 }
102                         } while (loopAgain);
103
104                         return "Constraint" + index.ToString();         
105                         
106                 }
107                 
108                 
109                 // Overloaded Add method (5 of them)
110                 // to add Constraint object to the collection
111
112                 public void Add(Constraint constraint) 
113                 {               
114                         //not null
115                         if (null == constraint) throw new ArgumentNullException("Can not add null.");
116                         
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.");
123                         
124                         //check for duplicate name
125                         if (_isDuplicateConstraintName(constraint.ConstraintName,null)  )
126                                 throw new DuplicateNameException("Constraint name already exists.");
127                 
128                         //Allow constraint to run validation rules and setup 
129                         constraint.AddToConstraintCollectionSetup(this); //may throw if it can't setup
130
131                         //Run Constraint to check existing data in table
132                         constraint.AssertConstraint();
133
134                         //if name is null or empty give it a name
135                         if (constraint.ConstraintName == null || 
136                                 constraint.ConstraintName == "" ) 
137                         { 
138                                 constraint.ConstraintName = _createNewConstraintName();
139                         }
140
141                         //Add event handler for ConstraintName change
142                         constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange(
143                                 _handleBeforeConstraintNameChange);
144                         
145                         constraint.ConstraintCollection = this;
146                         List.Add(constraint);
147
148                         OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction.Add, this) );
149                 }
150
151         
152
153                 public virtual Constraint Add(string name, DataColumn column, bool primaryKey) 
154                 {
155
156                         UniqueConstraint uc = new UniqueConstraint(name, column, primaryKey);
157                         Add(uc);
158                          
159                         return uc;
160                 }
161
162                 public virtual Constraint Add(string name, DataColumn primaryKeyColumn,
163                         DataColumn foreignKeyColumn) 
164                 {
165                         ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumn, 
166                                         foreignKeyColumn);
167                         Add(fc);
168
169                         return fc;
170                 }
171
172                 public virtual Constraint Add(string name, DataColumn[] columns, bool primaryKey) 
173                 {
174                         UniqueConstraint uc = new UniqueConstraint(name, columns, primaryKey);
175                         Add(uc);
176
177                         return uc;
178                 }
179
180                 public virtual Constraint Add(string name, DataColumn[] primaryKeyColumns,
181                         DataColumn[] foreignKeyColumns) 
182                 {
183                         ForeignKeyConstraint fc = new ForeignKeyConstraint(name, primaryKeyColumns, 
184                                         foreignKeyColumns);
185                         Add(fc);
186
187                         return fc;
188                 }
189
190                 [MonoTODO]
191                 public void AddRange(Constraint[] constraints) {
192
193                         throw new NotImplementedException ();
194                 }
195
196                 public bool CanRemove(Constraint constraint) 
197                 {
198
199                         //Rule A UniqueConstraint can't be removed if there is
200                         //a foreign key relationship to that column
201
202                         //not null 
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.");
206                         
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
211
212                         //discover if there is a related ForeignKey
213                         string failReason ="";
214                         return _canRemoveConstraint(constraint, ref failReason);
215                         
216                 }
217
218                 [MonoTODO]
219                 public void Clear() 
220                 {
221                         
222                         //CanRemove? See Lamespec below.
223
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)
228                         {
229                                 con.ConstraintCollection = null;
230                                 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange(
231                                 _handleBeforeConstraintNameChange);
232                         }
233
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) );
239                 }
240
241                 public bool Contains(string name) 
242                 {
243                         return (-1 != IndexOf(name));
244                 }
245
246                 public int IndexOf(Constraint constraint) 
247                 {
248                         return List.IndexOf(constraint);
249                 }
250
251                 public virtual int IndexOf(string constraintName) 
252                 {
253                         //LAMESPEC: Spec doesn't say case insensitive
254                         //it should to be consistant with the other 
255                         //case insensitive comparisons in this class
256
257                         int index = 0;
258                         foreach (Constraint con in List)
259                         {
260                                 if (constraintName.ToUpper().CompareTo( con.ConstraintName.ToUpper() ) == 0)
261                                 {
262                                         return index;
263                                 }
264
265                                 index++;
266                         }
267                         return -1; //not found
268                 }
269
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
273                         
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
279
280                         //not null
281                         if (null == constraint) throw new ArgumentNullException();
282
283                         string failReason = "";
284                         if (! _canRemoveConstraint(constraint, ref failReason) )
285                         {
286                                 if (failReason != null || failReason != "")     
287                                         throw new ArgumentException(failReason);
288                                 else
289                                         throw new ArgumentException("Can't remove constraint.");                
290                         }
291                                 
292                         constraint.RemoveFromConstraintCollectionCleanup(this);
293                         List.Remove(constraint);
294                         OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
295                 }
296
297                 public void Remove(string name) 
298                 {
299                         //if doesn't exist fail quietly
300                         int index = IndexOf(name);
301                         if (-1 == index) return;
302
303                         Remove(this[index]);
304                 }
305
306                 public void RemoveAt(int index) 
307                 {
308                         if (index < 0 || index + 1 > List.Count)
309                                 throw new IndexOutOfRangeException("Index out of range, index = " 
310                                                 + index.ToString() + ".");
311         
312                         this[index].RemoveFromConstraintCollectionCleanup(this);
313                         List.RemoveAt(index);
314                         OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction.Remove,this));
315                 }
316
317
318                 protected override ArrayList List {
319                         get{
320                                 return base.List;
321                         }
322                 }
323
324                 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent) 
325                 {
326                         if (null != CollectionChanged)
327                         {
328                                 CollectionChanged(this, ccevent);
329                         }
330                 }
331
332                 private bool _canRemoveConstraint(Constraint constraint, ref string failReason )
333                 {
334                         bool cancel = false;
335                         string tmp = "";
336                         if (null != ValidateRemoveConstraint)
337                         {
338                                 ValidateRemoveConstraint(this, constraint, ref cancel, ref tmp);
339                         }
340                         failReason = tmp;
341                         return !cancel;
342                 }
343         }
344 }