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 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
37 using System
.Collections
;
38 using System
.ComponentModel
;
40 namespace System
.Data
{
43 internal delegate void DelegateValidateRemoveConstraint(ConstraintCollection sender
, Constraint constraintToRemove
, ref bool fail
,ref string failReason
);
46 /// hold collection of constraints for data table
48 [DefaultEvent ("CollectionChanged")]
49 [EditorAttribute("Microsoft.VSDesigner.Data.Design.ConstraintsCollectionEditor, "+Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+Consts
.AssemblySystem_Drawing
)]
51 public class ConstraintCollection
: InternalDataCollectionBase
53 //private bool beginInit = false;
55 public event CollectionChangeEventHandler CollectionChanged
;
56 internal event DelegateValidateRemoveConstraint ValidateRemoveConstraint
;
57 private DataTable table
;
59 // Call this to set the "table" property of the UniqueConstraint class
60 // intialized with UniqueConstraint( string, string[], bool );
61 // And also validate that the named columns exist in the "table"
62 private delegate void PostAddRange( DataTable table
);
64 // Keep reference to most recent constraints passed to AddRange()
65 // so that they can be added when EndInit() is called.
66 private Constraint
[] _mostRecentConstraints
;
68 //Don't allow public instantiation
69 //Will be instantianted from DataTable
70 internal ConstraintCollection(DataTable table
){
74 internal DataTable Table
{
80 public virtual Constraint
this[string name
] {
82 //If the name is not found we just return null
83 int index
= IndexOf(name
); //case insensitive
84 if (-1 == index
) return null;
89 public virtual Constraint
this[int index
] {
91 if (index
< 0 || index
>= List
.Count
)
92 throw new IndexOutOfRangeException();
93 return (Constraint
)List
[index
];
97 private void _handleBeforeConstraintNameChange(object sender
, string newName
)
100 if (newName
== null || newName
== "")
101 throw new ArgumentException("ConstraintName cannot be set to null or empty " +
102 " after it has been added to a ConstraintCollection.");
104 if (_isDuplicateConstraintName(newName
,(Constraint
)sender
))
105 throw new DuplicateNameException("Constraint name already exists.");
108 private bool _isDuplicateConstraintName(string constraintName
, Constraint excludeFromComparison
)
110 foreach (Constraint cst
in List
) {
111 if (String
.Compare (constraintName
, cst
.ConstraintName
, !Table
.CaseSensitive
) == 0 && cst
!= excludeFromComparison
)
118 //finds an open name slot of ConstraintXX
119 //where XX is a number
120 private string _createNewConstraintName()
122 bool loopAgain
= false;
128 foreach (Constraint cst
in List
)
131 if (String
.Compare (cst
.ConstraintName
,
132 "Constraint" + index
,
133 !Table
.CaseSensitive
,
144 return "Constraint" + index
.ToString();
149 // Overloaded Add method (5 of them)
150 // to add Constraint object to the collection
152 public void Add(Constraint constraint
)
155 if (null == constraint
) throw new ArgumentNullException("Can not add null.");
157 //check constraint membership
158 //can't already exist in this collection or any other
159 if (this == constraint
.ConstraintCollection
)
160 throw new ArgumentException("Constraint already belongs to this collection.");
161 if (null != constraint
.ConstraintCollection
)
162 throw new ArgumentException("Constraint already belongs to another collection.");
164 //check for duplicate name
166 if (_isDuplicateConstraintName(constraint
.ConstraintName
,null) )
167 throw new DuplicateNameException("Constraint name already exists.");
170 // Check whether Constraint is UniqueConstraint and initailized with the special
171 // constructor - UniqueConstraint( string, string[], bool );
172 // If yes, It must be added via AddRange() only
173 // Environment.StackTrace can help us
174 // FIXME: Is a different mechanism to do this?
175 if (constraint
is UniqueConstraint
){
176 if ((constraint
as UniqueConstraint
).DataColsNotValidated
== true){
177 if ( Environment
.StackTrace
.IndexOf( "AddRange" ) == -1 ){
178 throw new ArgumentException(" Some DataColumns are invalid - They may not belong to the table associated with this Constraint Collection" );
183 if (constraint
is ForeignKeyConstraint
){
184 if ((constraint
as ForeignKeyConstraint
).DataColsNotValidated
== true){
185 if ( Environment
.StackTrace
.IndexOf( "AddRange" ) == -1 ){
186 throw new ArgumentException(" Some DataColumns are invalid - They may not belong to the table associated with this Constraint Collection" );
191 //Allow constraint to run validation rules and setup
192 constraint
.AddToConstraintCollectionSetup(this); //may throw if it can't setup
194 //Run Constraint to check existing data in table
195 // this is redundant, since AddToConstraintCollectionSetup
196 // calls AssertConstraint right before this call
197 //constraint.AssertConstraint();
199 //if name is null or empty give it a name
200 if (constraint
.ConstraintName
== null ||
201 constraint
.ConstraintName
== "" )
203 constraint
.ConstraintName
= _createNewConstraintName();
206 //Add event handler for ConstraintName change
207 constraint
.BeforeConstraintNameChange
+= new DelegateConstraintNameChange(
208 _handleBeforeConstraintNameChange
);
210 constraint
.ConstraintCollection
= this;
211 List
.Add(constraint
);
213 if (constraint
is UniqueConstraint
)
214 ((UniqueConstraint
)constraint
).UpdatePrimaryKey();
216 OnCollectionChanged( new CollectionChangeEventArgs( CollectionChangeAction
.Add
, this) );
221 public virtual Constraint
Add(string name
, DataColumn column
, bool primaryKey
)
224 UniqueConstraint uc
= new UniqueConstraint(name
, column
, primaryKey
);
230 public virtual Constraint
Add(string name
, DataColumn primaryKeyColumn
,
231 DataColumn foreignKeyColumn
)
233 ForeignKeyConstraint fc
= new ForeignKeyConstraint(name
, primaryKeyColumn
,
240 public virtual Constraint
Add(string name
, DataColumn
[] columns
, bool primaryKey
)
242 UniqueConstraint uc
= new UniqueConstraint(name
, columns
, primaryKey
);
248 public virtual Constraint
Add(string name
, DataColumn
[] primaryKeyColumns
,
249 DataColumn
[] foreignKeyColumns
)
251 ForeignKeyConstraint fc
= new ForeignKeyConstraint(name
, primaryKeyColumns
,
258 public void AddRange(Constraint
[] constraints
) {
260 //When AddRange() occurs after BeginInit,
261 //it does not add any elements to the collection until EndInit is called.
262 if (this.table
.fInitInProgress
) {
263 // Keep reference so that they can be added when EndInit() is called.
264 _mostRecentConstraints
= constraints
;
268 // Check whether the constraint is UniqueConstraint
269 // And whether it was initialized with the special ctor
270 // i.e UniqueConstraint( string, string[], bool );
271 for (int i
= 0; i
< constraints
.Length
; i
++){
272 if (constraints
[i
] is UniqueConstraint
){
273 if (( constraints
[i
] as UniqueConstraint
).DataColsNotValidated
== true){
274 PostAddRange _postAddRange
= new PostAddRange ((constraints
[i
] as UniqueConstraint
).PostAddRange
);
275 // UniqueConstraint.PostAddRange() validates whether all named
276 // columns exist in the table associated with this instance of
277 // ConstraintCollection.
278 _postAddRange (this.table
);
282 else if (constraints
[i
] is ForeignKeyConstraint
){
283 if (( constraints
[i
] as ForeignKeyConstraint
).DataColsNotValidated
== true){
284 (constraints
[i
] as ForeignKeyConstraint
).postAddRange (this.table
);
290 if ( (constraints
== null) || (constraints
.Length
== 0))
291 throw new ArgumentNullException ("Cannot add null");
294 foreach (Constraint constraint
in constraints
)
302 // Helper AddRange() - Call this function when EndInit is called
303 internal void PostEndInit()
305 AddRange (_mostRecentConstraints
);
309 public bool CanRemove(Constraint constraint
)
312 //Rule A UniqueConstraint can't be removed if there is
313 //a foreign key relationship to that column
316 //LAMESPEC: MSFT implementation throws and exception here
317 //spec says nothing about this
318 if (null == constraint
) throw new ArgumentNullException("Constraint can't be null.");
320 //LAMESPEC: spec says return false (which makes sense) and throw exception for False case (?).
321 //TODO: I may want to change how this is done
322 //maybe put a CanRemove on the Constraint class
323 //and have the Constraint fire this event
325 //discover if there is a related ForeignKey
326 string failReason
="";
327 return _canRemoveConstraint(constraint
, ref failReason
);
334 //CanRemove? See Lamespec below.
336 //the Constraints have a reference to us
337 //and we listen to name change events
338 //we should remove these before clearing
339 foreach (Constraint con
in List
)
341 con
.ConstraintCollection
= null;
342 con
.BeforeConstraintNameChange
-= new DelegateConstraintNameChange(
343 _handleBeforeConstraintNameChange
);
346 //LAMESPEC: MSFT implementation allows this
347 //even when a ForeignKeyConstraint exist for a UniqueConstraint
348 //thus violating the CanRemove logic
349 //CanRemove will throws Exception incase of the above
350 List
.Clear(); //Will violate CanRemove rule
351 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction
.Refresh
, this) );
354 public bool Contains(string name
)
356 return (-1 != IndexOf(name
));
359 public int IndexOf(Constraint constraint
)
361 return List
.IndexOf(constraint
);
364 public virtual int IndexOf(string constraintName
)
366 //LAMESPEC: Spec doesn't say case insensitive
367 //it should to be consistant with the other
368 //case insensitive comparisons in this class
371 foreach (Constraint con
in List
)
373 if (String
.Compare (constraintName
, con
.ConstraintName
, !Table
.CaseSensitive
, Table
.Locale
) == 0)
380 return -1; //not found
383 public void Remove(Constraint constraint
) {
384 //LAMESPEC: spec doesn't document the ArgumentException the
385 //will be thrown if the CanRemove rule is violated
387 //LAMESPEC: spec says an exception will be thrown
388 //if the element is not in the collection. The implementation
389 //doesn't throw an exception. ArrayList.Remove doesn't throw if the
390 //element doesn't exist
391 //ALSO the overloaded remove in the spec doesn't say it throws any exceptions
394 if (null == constraint
) throw new ArgumentNullException();
396 string failReason
= "";
397 if (! _canRemoveConstraint(constraint
, ref failReason
) )
399 if (failReason
!= null || failReason
!= "")
400 throw new ArgumentException(failReason
);
402 throw new ArgumentException("Can't remove constraint.");
405 constraint
.RemoveFromConstraintCollectionCleanup(this);
406 List
.Remove(constraint
);
407 OnCollectionChanged( new CollectionChangeEventArgs(CollectionChangeAction
.Remove
,this));
410 public void Remove(string name
)
412 //if doesn't exist fail quietly
413 int index
= IndexOf(name
);
414 if (-1 == index
) return;
419 public void RemoveAt(int index
)
424 protected override ArrayList List
{
430 protected virtual void OnCollectionChanged( CollectionChangeEventArgs ccevent
)
432 if (null != CollectionChanged
)
434 CollectionChanged(this, ccevent
);
438 private bool _canRemoveConstraint(Constraint constraint
, ref string failReason
)
442 if (null != ValidateRemoveConstraint
)
444 ValidateRemoveConstraint(this, constraint
, ref cancel
, ref tmp
);
450 internal ICollection UniqueConstraints
454 return GetConstraintsCollection(typeof(UniqueConstraint
));
458 internal ICollection ForeignKeyConstraints
462 return GetConstraintsCollection(typeof(ForeignKeyConstraint
));
466 private ICollection
GetConstraintsCollection (Type constraintType
)
468 ArrayList cCollection
= new ArrayList();
469 foreach (Constraint c
in List
)
471 if (c
.GetType() == constraintType
)