[genmdesc] Fixed generator to allow instructions lengths equal to 0.
[mono.git] / mcs / class / System.Data / System.Data / DataRelation.cs
1 //
2 // System.Data.DataRelation.cs
3 //
4 // Author:
5 //   Daniel Morgan <danmorg@sc.rr.com>
6 //   Alan Tam Siu Lung <Tam@SiuLung.com>
7 //   Tim Coleman <tim@timcoleman.com>
8 //
9 // (C) 2002 Daniel Morgan
10 // (C) 2002 Ximian, Inc.
11 // Copyright (C) Tim Coleman, 2003
12 //
13
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 //
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 //
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.ComponentModel;
39 using System.Runtime.Serialization;
40
41 namespace System.Data
42 {
43         /// <summary>
44         /// DataRelation is used for a parent/child relationship
45         /// between two DataTable objects
46         /// </summary>
47         [Editor ("Microsoft.VSDesigner.Data.Design.DataRelationEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
48                  "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
49         [DefaultProperty ("RelationName")]
50         [TypeConverterAttribute (typeof (RelationshipConverter))]
51         public class DataRelation {
52                 private DataSet dataSet;
53                 private string relationName;
54                 private UniqueConstraint parentKeyConstraint;
55                 private ForeignKeyConstraint childKeyConstraint;
56                 private DataColumn [] parentColumns;
57                 private DataColumn [] childColumns;
58                 private bool nested;
59                 internal bool createConstraints = true;
60                 private bool initFinished;
61                 private PropertyCollection extendedProperties;
62                 private PropertyChangedEventHandler onPropertyChangingDelegate;
63
64                 string _relationName;
65                 string _parentTableName;
66                 string _childTableName;
67                 string [] _parentColumnNames;
68                 string [] _childColumnNames;
69                 bool _nested;
70                 bool initInProgress = false;
71                 string _parentTableNameSpace = String.Empty;
72                 string _childTableNameSpace = String.Empty;
73
74                 #region Constructors
75
76                 public DataRelation (string relationName, DataColumn parentColumn, DataColumn childColumn)
77                         : this (relationName, parentColumn, childColumn, true)
78                 {
79                 }
80
81                 public DataRelation (string relationName, DataColumn [] parentColumns, DataColumn [] childColumns)
82                         : this (relationName, parentColumns, childColumns, true)
83                 {
84                 }
85
86                 public DataRelation (string relationName, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
87                         : this (relationName, new DataColumn[] { parentColumn }, new DataColumn [] { childColumn }, createConstraints)
88                 {
89                 }
90
91                 public DataRelation (string relationName, DataColumn [] parentColumns, DataColumn [] childColumns, bool createConstraints)
92                 {
93                         this.extendedProperties = new PropertyCollection ();
94                         this.relationName = relationName == null ? string.Empty : relationName;
95                         if (parentColumns == null)
96                                 throw new ArgumentNullException ("parentColumns");
97                         this.parentColumns = parentColumns;
98                         if (childColumns == null)
99                                 throw new ArgumentNullException ("childColumns");
100                         this.childColumns = childColumns;
101                         this.createConstraints = createConstraints;
102                         if (parentColumns.Length != childColumns.Length)
103                                 throw new ArgumentException ("ParentColumns and ChildColumns should be the same length");
104                         DataTable parentTable = parentColumns [0].Table;
105                         DataTable childTable = childColumns [0].Table;
106                         if (parentTable.DataSet != childTable.DataSet)
107                                 throw new InvalidConstraintException ();
108                         foreach (DataColumn column in parentColumns)
109                                 if (column.Table != parentTable)
110                                         throw new InvalidConstraintException ();
111                         foreach (DataColumn column in childColumns)
112                                 if (column.Table != childTable)
113                                         throw new InvalidConstraintException ();
114
115                         for (int i = 0; i < ChildColumns.Length; i++)
116                                 if (!parentColumns [i].DataTypeMatches (childColumns [i]))
117                                         throw new InvalidConstraintException (
118                                                 "Parent Columns and Child Columns don't have matching column types");
119                 }
120
121                 [Browsable (false)]
122                 public DataRelation (string relationName, string parentTableName, string childTableName, string [] parentColumnNames, string[] childColumnNames, bool nested)
123                 {
124                         _relationName = relationName;
125                         _parentTableName = parentTableName;
126                         _childTableName = childTableName;
127                         _parentColumnNames = parentColumnNames;
128                         _childColumnNames = childColumnNames;
129                         _nested = nested;
130                         InitInProgress = true;
131                 }
132
133                 [Browsable (false)]
134                 public DataRelation (string relationName, string parentTableName,
135                                      string parentTableNamespace, string childTableName,
136                                      string childTableNamespace, string[] parentColumnNames,
137                                      string[] childColumnNames, bool nested)
138                 {
139                         _relationName = relationName;
140                         _parentTableName = parentTableName;
141                         _parentTableNameSpace = parentTableNamespace;
142                         _childTableName = childTableName;
143                         _childTableNameSpace = childTableNamespace;
144                         _parentColumnNames = parentColumnNames;
145                         _childColumnNames = childColumnNames;
146                         _nested = nested;
147                         InitInProgress = true;
148                 }
149
150                 internal bool InitInProgress {
151                         get { return initInProgress; }
152                         set { initInProgress = value; }
153                 }
154
155                 internal void FinishInit (DataSet ds)
156                 {
157                         if (!ds.Tables.Contains (_parentTableName) || !ds.Tables.Contains (_childTableName))
158                                 throw new InvalidOperationException ();
159
160                         if (_parentColumnNames.Length != _childColumnNames.Length)
161                                 throw new InvalidOperationException ();
162
163                         DataTable parent = ds.Tables [_parentTableName];
164                         DataTable child = ds.Tables [_childTableName];
165
166                         parentColumns = new DataColumn [_parentColumnNames.Length];
167                         childColumns = new DataColumn [_childColumnNames.Length];
168
169                         for (int i = 0; i < _parentColumnNames.Length; ++i) {
170                                 if (!parent.Columns.Contains (_parentColumnNames [i]))
171                                         throw new InvalidOperationException ();
172                                 parentColumns [i] = parent.Columns [_parentColumnNames [i]];
173                                 if (!child.Columns.Contains (_childColumnNames [i]))
174                                         throw new InvalidOperationException ();
175                                 childColumns [i] = child.Columns [_childColumnNames [i]];
176                         }
177
178                         this.RelationName = _relationName;
179                         this.Nested = _nested;
180                         this.initFinished = true;
181                         this.extendedProperties = new PropertyCollection ();
182                         InitInProgress = false;
183                         if (_parentTableNameSpace != String.Empty)
184                                 parent.Namespace = _parentTableNameSpace;
185                         if (_childTableNameSpace != String.Empty)
186                                 child.Namespace = _childTableNameSpace;
187                 }
188
189                 #endregion // Constructors
190
191                 #region Properties
192
193                 [DataCategory ("Data")]
194                 public virtual DataColumn [] ChildColumns {
195                         get { return childColumns; }
196                 }
197
198                 public virtual ForeignKeyConstraint ChildKeyConstraint {
199                         get { return childKeyConstraint; }
200                 }
201
202                 internal void SetChildKeyConstraint (ForeignKeyConstraint foreignKeyConstraint)
203                 {
204                         childKeyConstraint = foreignKeyConstraint;
205                 }
206
207                 public virtual DataTable ChildTable {
208                         get { return childColumns [0].Table; }
209                 }
210
211                 [Browsable (false)]
212                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
213                 public virtual DataSet DataSet {
214                         get { return childColumns [0].Table.DataSet; }
215                 }
216
217                 [Browsable (false)]
218                 [DataCategory ("Data")]
219                 public PropertyCollection ExtendedProperties {
220                         get {
221                                 if (extendedProperties == null)
222                                         extendedProperties = new PropertyCollection();
223                                 return extendedProperties;
224                         }
225                 }
226
227                 [DataCategory ("Data")]
228                 [DefaultValue (false)]
229                 public virtual bool Nested {
230                         get { return nested; }
231                         set { nested = value; }
232                 }
233
234                 [DataCategory ("Data")]
235                 public virtual DataColumn[] ParentColumns {
236                         get { return parentColumns; }
237                 }
238
239                 public virtual UniqueConstraint ParentKeyConstraint {
240                         get { return parentKeyConstraint; }
241                 }
242
243                 internal void SetParentKeyConstraint (UniqueConstraint uniqueConstraint)
244                 {
245                         parentKeyConstraint = uniqueConstraint;
246                 }
247
248                 internal void SetDataSet (DataSet ds)
249                 {
250                         dataSet = ds;
251                 }
252
253                 public virtual DataTable ParentTable {
254                         get { return parentColumns [0].Table; }
255                 }
256
257                 [DataCategory ("Data")]
258                 [DefaultValue ("")]
259                 public virtual string RelationName {
260                         get { return relationName; }
261                         set { relationName = value; }
262                 }
263
264                 #endregion // Properties
265
266                 #region Methods
267
268                 protected void CheckStateForProperty ()
269                 {
270                         // TODO: check consistency of constraints
271                         DataTable parentTable = parentColumns [0].Table;
272                         DataTable childTable = childColumns [0].Table;
273                         if (parentTable.DataSet != childTable.DataSet)
274                                 throw new DataException ();
275                         bool allColumnsEqual = false;
276                         for (int colCnt = 0; colCnt < parentColumns.Length; ++colCnt) {
277                                 if (!parentColumns [colCnt].DataType.Equals (childColumns [colCnt].DataType))
278                                         throw new DataException ();
279                                 if (parentColumns [colCnt] != childColumns [colCnt])
280                                         allColumnsEqual = false;
281                         }
282                         if (allColumnsEqual)
283                                 throw new DataException ();
284                 }
285
286                 protected internal void OnPropertyChanging (PropertyChangedEventArgs pcevent)
287                 {
288                         if (onPropertyChangingDelegate != null)
289                                 onPropertyChangingDelegate (this, pcevent);
290                 }
291
292                 protected internal void RaisePropertyChanging (string name)
293                 {
294                         OnPropertyChanging(new PropertyChangedEventArgs(name));
295                 }
296
297                 public override string ToString ()
298                 {
299                         return relationName;
300                 }
301
302                 internal void UpdateConstraints ()
303                 {
304                         if (initFinished || ! createConstraints)
305                                 return;
306
307                         ForeignKeyConstraint    foreignKeyConstraint    = null;
308                         UniqueConstraint        uniqueConstraint        = null;
309
310                         foreignKeyConstraint    = FindForeignKey (ChildTable.Constraints);
311                         uniqueConstraint        = FindUniqueConstraint (ParentTable.Constraints);
312
313                         // if we did not find the unique constraint in the parent table.
314                         // we generate new uniqueConstraint and add it to the parent table.
315                         if (uniqueConstraint == null) {
316                                 uniqueConstraint = new UniqueConstraint (ParentColumns, false);
317                                 ParentTable.Constraints.Add (uniqueConstraint);
318                         }
319
320                         // if we did not find the foreign key constraint in the parent table.
321                         // we generate new foreignKeyConstraint and add it to the parent table.
322                         if (foreignKeyConstraint == null) {
323                                 foreignKeyConstraint = new ForeignKeyConstraint (RelationName,
324                                                                                  ParentColumns,
325                                                                                  ChildColumns);
326                                 ChildTable.Constraints.Add (foreignKeyConstraint);
327                         }
328
329                         SetParentKeyConstraint (uniqueConstraint);
330                         SetChildKeyConstraint (foreignKeyConstraint);
331                 }
332
333                 private static bool CompareDataColumns (DataColumn [] dc1, DataColumn [] dc2)
334                 {
335                         if (dc1.Length != dc2.Length)
336                                 return false;
337
338                         for (int columnCnt = 0; columnCnt < dc1.Length; ++columnCnt){
339                                 if (dc1 [columnCnt] != dc2 [columnCnt])
340                                         return false;
341                         }
342                         return true;
343                 }
344
345                 private ForeignKeyConstraint FindForeignKey (ConstraintCollection cl)
346                 {
347                         ForeignKeyConstraint fkc = null;
348                         foreach (Constraint o in cl) {
349                                 if (! (o is ForeignKeyConstraint))
350                                         continue;
351                                 fkc = (ForeignKeyConstraint) o;
352                                 /* Check ChildColumns & ParentColumns */
353                                 if (CompareDataColumns (ChildColumns, fkc.Columns) &&
354                                     CompareDataColumns (ParentColumns, fkc.RelatedColumns))
355                                         return fkc;
356                         }
357                         return null;
358                 }
359
360                 private UniqueConstraint FindUniqueConstraint (ConstraintCollection cl)
361                 {
362                         UniqueConstraint uc = null;
363                         // find if the unique constraint already exists in the parent table.
364                         foreach (Constraint o in cl){
365                                 if (! (o is UniqueConstraint))
366                                         continue;
367                                 uc = (UniqueConstraint) o;
368                                 //Check in ParentColumns
369                                 if (CompareDataColumns (ParentColumns, uc.Columns))
370                                         return uc;
371                         }
372                         return null;
373                 }
374
375                 /// <summary>
376                 ///     Check whether the given column is part of this relation.
377                 /// <summary>
378                 /// <returns>
379                 ///     true if the column is part of this relation, otherwise false.
380                 /// </returns>
381                 internal bool Contains (DataColumn column)
382                 {
383                         foreach (DataColumn col in ParentColumns)
384                                 if (col == column)
385                                         return true;
386
387                         foreach (DataColumn col in ChildColumns)
388                                 if (col == column)
389                                         return true;
390                         return false;
391                 }
392
393                 #endregion // Methods
394         }
395 }