Merge remote-tracking branch 'raof/aot-cpu-safety'
[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 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Collections;
38 using System.ComponentModel;
39
40 namespace System.Data {
41         [Editor]
42         [Serializable]
43         internal delegate void DelegateValidateRemoveConstraint (ConstraintCollection sender, Constraint constraintToRemove, ref bool fail,ref string failReason);
44
45         /// <summary>
46         /// hold collection of constraints for data table
47         /// </summary>
48         [DefaultEvent ("CollectionChanged")]
49         [Editor ("Microsoft.VSDesigner.Data.Design.ConstraintsCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
50                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
51         public partial class ConstraintCollection : InternalDataCollectionBase {
52                 public event CollectionChangeEventHandler CollectionChanged;
53                 private DataTable table;
54
55                 // Keep reference to most recent constraints passed to AddRange()
56                 // so that they can be added when EndInit() is called.
57                 private Constraint [] _mostRecentConstraints;
58
59                 //Don't allow public instantiation
60                 //Will be instantianted from DataTable
61                 internal ConstraintCollection(DataTable table)
62                 {
63                         this.table = table;
64                 }
65
66                 internal DataTable Table {
67                         get { return this.table; }
68                 }
69
70                 public
71 #if !NET_2_0
72                 virtual
73 #endif
74                 Constraint this [string name] {
75                         get {
76                                 int index = IndexOf (name);
77                                 return -1 == index ? null : (Constraint) List [index];
78                         }
79                 }
80
81                 public
82 #if !NET_2_0
83                 virtual
84 #endif
85                 Constraint this [int index] {
86                         get {
87                                 if (index < 0 || index >= List.Count)
88                                         throw new IndexOutOfRangeException ();
89                                 return (Constraint) List [index];
90                         }
91                 }
92
93                 private void _handleBeforeConstraintNameChange (object sender, string newName)
94                 {
95                         if (newName == null || newName == "")
96                                 throw new ArgumentException (
97                                         "ConstraintName cannot be set to null or empty after adding it to a ConstraintCollection.");
98
99                         if (_isDuplicateConstraintName (newName, (Constraint) sender))
100                                 throw new DuplicateNameException ("Constraint name already exists.");
101                 }
102
103                 private bool _isDuplicateConstraintName (string constraintName, Constraint excludeFromComparison)
104                 {
105                         foreach (Constraint cst in List) {
106                                 if (cst == excludeFromComparison)
107                                         continue;
108                                 if (String.Compare (constraintName, cst.ConstraintName, false, Table.Locale) == 0)
109                                         return true;
110                         }
111
112                         return false;
113                 }
114
115                 //finds an open name slot of ConstraintXX
116                 //where XX is a number
117                 private string _createNewConstraintName ()
118                 {
119                         // FIXME: Do constraint id's need to be reused?  This loop is horrendously slow.
120                         for (int i = 1; ; ++i) {
121                                 string new_name = "Constraint" + i;
122                                 if (IndexOf (new_name) == -1)
123                                         return new_name;
124                         }
125                 }
126
127
128                 // Overloaded Add method (5 of them)
129                 // to add Constraint object to the collection
130
131                 public void Add (Constraint constraint)
132                 {
133                         //not null
134                         if (null == constraint)
135                                 throw new ArgumentNullException ("Can not add null.");
136
137                         if (constraint.InitInProgress)
138                                 throw new ArgumentException ("Hmm .. Failed to Add to collection");
139
140                         //check constraint membership
141                         //can't already exist in this collection or any other
142                         if (this == constraint.ConstraintCollection)
143                                 throw new ArgumentException ("Constraint already belongs to this collection.");
144                         if (null != constraint.ConstraintCollection)
145                                 throw new ArgumentException ("Constraint already belongs to another collection.");
146
147                         //check if a constraint already exists for the datacolums
148                         foreach (Constraint c in this) {
149                                 if (c.Equals (constraint))
150                                         throw new DataException (
151                                                 "Constraint matches contraint named '" + c.ConstraintName + "' already in collection");
152                         }
153
154                         //check for duplicate name
155                         if (_isDuplicateConstraintName (constraint.ConstraintName, null))
156                                 throw new DuplicateNameException ("Constraint name already exists.");
157
158                         //Allow constraint to run validation rules and setup
159                         constraint.AddToConstraintCollectionSetup (this); //may throw if it can't setup
160
161                         //if name is null or empty give it a name
162                         if (constraint.ConstraintName == null || constraint.ConstraintName == "")
163                                 constraint.ConstraintName = _createNewConstraintName ();
164
165                         //Add event handler for ConstraintName change
166                         constraint.BeforeConstraintNameChange += new DelegateConstraintNameChange (_handleBeforeConstraintNameChange);
167
168                         constraint.ConstraintCollection = this;
169                         List.Add (constraint);
170
171                         if (constraint is UniqueConstraint && ((UniqueConstraint) constraint).IsPrimaryKey)
172                                 table.PrimaryKey = ((UniqueConstraint) constraint).Columns;
173
174                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, this));
175                 }
176
177                 public
178 #if !NET_2_0
179                 virtual
180 #endif
181                 Constraint Add (string name, DataColumn column, bool primaryKey)
182                 {
183                         UniqueConstraint uc = new UniqueConstraint (name, column, primaryKey);
184                         Add (uc);
185                         return uc;
186                 }
187
188                 public
189 #if !NET_2_0
190                 virtual
191 #endif
192                 Constraint Add (string name, DataColumn primaryKeyColumn, DataColumn foreignKeyColumn)
193                 {
194                         ForeignKeyConstraint fc = new ForeignKeyConstraint (name, primaryKeyColumn, foreignKeyColumn);
195                         Add (fc);
196                         return fc;
197                 }
198
199                 public
200 #if !NET_2_0
201                 virtual
202 #endif
203                 Constraint Add (string name, DataColumn[] columns, bool primaryKey)
204                 {
205                         UniqueConstraint uc = new UniqueConstraint (name, columns, primaryKey);
206                         Add (uc);
207                         return uc;
208                 }
209
210                 public
211 #if !NET_2_0
212                 virtual
213 #endif
214                 Constraint Add (string name, DataColumn[] primaryKeyColumns, DataColumn[] foreignKeyColumns)
215                 {
216                         ForeignKeyConstraint fc = new ForeignKeyConstraint (name, primaryKeyColumns, foreignKeyColumns);
217                         Add (fc);
218                         return fc;
219                 }
220
221                 public void AddRange (Constraint[] constraints)
222                 {
223                         //When AddRange() occurs after BeginInit,
224                         //it does not add any elements to the collection until EndInit is called.
225                         if (Table.InitInProgress) {
226                                 // Keep reference so that they can be added when EndInit() is called.
227                                 _mostRecentConstraints = constraints;
228                                 return;
229                         }
230
231                         if (constraints == null)
232                                 return;
233
234                         for (int i = 0; i < constraints.Length; ++i) {
235                                 if (constraints [i] != null)
236                                         Add (constraints [i]);
237                         }
238                 }
239
240                 // Helper AddRange() - Call this function when EndInit is called
241                 // keeps track of the Constraints most recently added and adds them
242                 // to the collection
243                 internal void PostAddRange ()
244                 {
245                         if (_mostRecentConstraints == null)
246                                 return;
247
248                         // Check whether the constraint is Initialized
249                         // If not, initialize before adding to collection
250                         for (int i = 0; i < _mostRecentConstraints.Length; i++) {
251                                 Constraint c = _mostRecentConstraints [i];
252                                 if (c == null)
253                                         continue;
254                                 if (c.InitInProgress)
255                                         c.FinishInit (Table);
256                                 Add (c);
257                         }
258                         _mostRecentConstraints = null;
259                 }
260
261                 public bool CanRemove (Constraint constraint)
262                 {
263                         return constraint.CanRemoveFromCollection (this, false);
264                 }
265
266                 public void Clear ()
267                 {
268                         // Clear should also remove PrimaryKey
269                         Table.PrimaryKey = null;
270
271                         //CanRemove? See Lamespec below.
272                         //the Constraints have a reference to us
273                         //and we listen to name change events
274                         //we should remove these before clearing
275                         foreach (Constraint con in List) {
276                                 con.ConstraintCollection = null;
277                                 con.BeforeConstraintNameChange -= new DelegateConstraintNameChange (_handleBeforeConstraintNameChange);
278                         }
279
280                         //LAMESPEC: MSFT implementation allows this
281                         //even when a ForeignKeyConstraint exist for a UniqueConstraint
282                         //thus violating the CanRemove logic
283                         //CanRemove will throws Exception incase of the above
284                         List.Clear (); //Will violate CanRemove rule
285                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, this));
286                 }
287
288                 public bool Contains (string name)
289                 {
290                         return -1 != IndexOf (name);
291                 }
292
293                 public int IndexOf (Constraint constraint)
294                 {
295                         int index = 0;
296                         foreach (Constraint c in this) {
297                                 if (c == constraint)
298                                         return index;
299                                 index++;
300                         }
301                         return -1;
302                 }
303
304                 public
305 #if !NET_2_0
306                 virtual
307 #endif
308                 int IndexOf (string constraintName)
309                 {
310                         //LAMESPEC: Spec doesn't say case insensitive
311                         //it should to be consistant with the other
312                         //case insensitive comparisons in this class
313
314                         int index = 0;
315                         foreach (Constraint con in List) {
316                                 if (String.Compare (constraintName, con.ConstraintName, !Table.CaseSensitive, Table.Locale) == 0)
317                                         return index;
318                                 index++;
319                         }
320                         return -1; //not found
321                 }
322
323                 public void Remove (Constraint constraint)
324                 {
325                         //LAMESPEC: spec doesn't document the ArgumentException the
326                         //will be thrown if the CanRemove rule is violated
327
328                         //LAMESPEC: spec says an exception will be thrown
329                         //if the element is not in the collection. The implementation
330                         //doesn't throw an exception. ArrayList.Remove doesn't throw if the
331                         //element doesn't exist
332                         //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
333
334                         //not null
335                         if (null == constraint)
336                                 throw new ArgumentNullException();
337
338                         if (!constraint.CanRemoveFromCollection (this, true))
339                                 return;
340
341                         constraint.RemoveFromConstraintCollectionCleanup (this);
342                         constraint.ConstraintCollection = null;
343                         List.Remove (constraint);
344                         OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, this));
345                 }
346
347                 public void Remove (string name)
348                 {
349                         int index = IndexOf (name);
350                         if (-1 == index)
351                                 throw new ArgumentException ("Constraint '" + name + "' does not belong to this DataTable.");
352
353                         Remove (this [index]);
354                 }
355
356                 public void RemoveAt(int index)
357                 {
358                         Remove (this [index]);
359                 }
360
361                 protected override ArrayList List {
362                         get { return base.List; }
363                 }
364
365
366 #if !NET_2_0
367                 protected virtual
368 #else
369                 internal
370 #endif
371                 void OnCollectionChanged (CollectionChangeEventArgs ccevent)
372                 {
373                         if (null != CollectionChanged)
374                                 CollectionChanged(this, ccevent);
375                 }
376         }
377
378 #if NET_2_0
379         sealed partial class ConstraintCollection {
380                 public void CopyTo (Constraint [] array, int index)
381                 {
382                         base.CopyTo (array, index);
383                 }
384         }
385 #else
386         [Serializable]
387         partial class ConstraintCollection {
388         }
389 #endif
390 }