2004-03-31 Juraj Skripsky <juraj@hotfeet.ch>
[mono.git] / mcs / class / System.Data / System.Data / MergeManager.cs
1 using System;\r
2 \r
3 namespace System.Data\r
4 {\r
5         /// <summary>\r
6         /// Summary description for MergeManager.\r
7         /// </summary>\r
8         internal class MergeManager\r
9         {\r
10                 internal static void Merge(DataSet targetSet, DataSet sourceSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
11                 {\r
12                         if(targetSet == null)\r
13                                 throw new ArgumentNullException("targetSet");\r
14                         if(sourceSet == null)\r
15                                 throw new ArgumentNullException("sourceSet");\r
16 \r
17                         foreach (DataTable t in sourceSet.Tables)\r
18                                 MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);\r
19 \r
20                 }\r
21 \r
22                 internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
23                 {\r
24                         if(targetSet == null)\r
25                                 throw new ArgumentNullException("targetSet");\r
26                         if(sourceTable == null)\r
27                                 throw new ArgumentNullException("sourceTable");\r
28 \r
29                         \r
30                         if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))\r
31                                 return;\r
32                         DataTable targetTable = targetSet.Tables[sourceTable.TableName];\r
33                         if (targetTable != null)\r
34                         {\r
35                                 checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same\r
36                                 fillData(targetTable, sourceTable, preserveChanges);\r
37                         }\r
38                         \r
39                 }\r
40 \r
41                 internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
42                 {\r
43                         if(targetSet == null)\r
44                                 throw new ArgumentNullException("targetSet");\r
45                         if(sourceRows == null)\r
46                                 throw new ArgumentNullException("sourceRows");\r
47 \r
48                         for (int i = 0; i < sourceRows.Length; i++)\r
49                         {\r
50                                 DataRow row = sourceRows[i];\r
51                                 DataTable sourceTable = row.Table;\r
52                                 if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction))\r
53                                         return;\r
54                                 DataTable targetTable = targetSet.Tables[row.Table.TableName];\r
55                                 if (targetTable != null)\r
56                                 {\r
57                                         checkColumnTypes(targetTable, row.Table);\r
58                                         MergeRow(targetTable, row, preserveChanges);\r
59                                 }\r
60                         }\r
61                 }\r
62 \r
63                 // merge a row into a target table.\r
64                 private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)\r
65                 {\r
66                         DataColumnCollection columns = row.Table.Columns;\r
67                         DataColumn[] primaryKeys = targetTable.PrimaryKey;\r
68                         DataRow targetRow = null;\r
69                         DataRowVersion version = DataRowVersion.Default;\r
70                         if (row.RowState == DataRowState.Deleted)\r
71                                 version = DataRowVersion.Original;\r
72 \r
73                         if (primaryKeys != null && primaryKeys.Length > 0) // if there are any primary key.\r
74                         {\r
75                                 // initiate an array that has the values of the primary keys.\r
76                                 object[] keyValues = new object[primaryKeys.Length];\r
77                                 \r
78                                 for (int j = 0; j < keyValues.Length; j++)\r
79                                 {\r
80                                         keyValues[j] = row[primaryKeys[j].ColumnName, version];\r
81                                 }\r
82                         \r
83                                 // find the row in the target table.\r
84                                 targetRow = targetTable.Rows.Find(keyValues);\r
85                         }\r
86                         // row doesn't exist in target table, or there are no primary keys.\r
87                         // create new row and copy values from source row to the new row.\r
88                         if (targetRow == null)\r
89                         { \r
90                                 targetRow = targetTable.NewRow();\r
91                                 row.CopyValuesToRow(targetRow);\r
92                                 targetTable.Rows.Add(targetRow);\r
93                         }\r
94                         // row exists in target table, and presere changes is false - \r
95                         // change the values of the target row to the values of the source row.\r
96                         else if (!preserveChanges)\r
97                         {\r
98                                 row.CopyValuesToRow(targetRow);\r
99                         }\r
100 \r
101                 }\r
102                         \r
103                 \r
104                 \r
105                 // adjust the table schema according to the missingschemaaction param.\r
106                 // return false if adjusting fails.\r
107                 private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction)\r
108                 {\r
109                         string tableName = sourceTable.TableName;\r
110                         \r
111                         // if the source table not exists in the target dataset\r
112                         // we act according to the missingschemaaction param.\r
113                         int tmp = targetSet.Tables.IndexOf(tableName);\r
114                         // we need to check if it is equals names\r
115                         if (tmp != -1 && !targetSet.Tables[tmp].TableName.Equals(tableName))\r
116                                 tmp = -1;\r
117                         if (tmp == -1)\r
118                         {\r
119                                 if (missingSchemaAction == MissingSchemaAction.Ignore)\r
120                                         return true;\r
121                                 if (missingSchemaAction == MissingSchemaAction.Error)\r
122                                         throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");\r
123                                 targetSet.Tables.Add((DataTable)sourceTable.Clone());\r
124                         }\r
125                         \r
126                         DataTable table = targetSet.Tables[tableName];\r
127                         \r
128                         if (!CheckPrimaryKeys(table, sourceTable))\r
129                                 return false;\r
130                         \r
131                         for (int i = 0; i < sourceTable.Columns.Count; i++)\r
132                         {\r
133                                 DataColumn col = sourceTable.Columns[i];\r
134                                 // if a column from the source table doesn't exists in the target table\r
135                                 // we act according to the missingschemaaction param.\r
136                                 if(!table.Columns.Contains(col.ColumnName))\r
137                                 {\r
138                                         if (missingSchemaAction == MissingSchemaAction.Ignore)\r
139                                                 continue;\r
140                                         if (missingSchemaAction == MissingSchemaAction.Error)\r
141                                                 throw new ArgumentException(("Column '" + col.ColumnName + "' does not belong to table Items."));\r
142                                         \r
143                                         table.Columns.Add(new DataColumn(col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));\r
144                                 }\r
145                         }\r
146 \r
147                         return true;\r
148                 }\r
149                 \r
150                 // find if there is a valid matching between the targetTable PrimaryKey and the\r
151                 // sourceTable primatyKey.\r
152                 // return true if there is a match, else return false and raise a MergeFailedEvent.\r
153                 private static bool CheckPrimaryKeys(DataTable targetTable, DataTable sourceTable)\r
154                 {\r
155                         // if the length of one of the tables primarykey if 0 - there is nothing to check.\r
156                         if (targetTable.PrimaryKey.Length != 0 && sourceTable.PrimaryKey.Length != 0)\r
157                         {\r
158                                 // if the length of primarykey is not equal - merge fails\r
159                                 if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length)\r
160                                 {\r
161                                         string message = "<target>.PrimaryKey and <source>.PrimaryKey have different Length.";\r
162                                         MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);\r
163                                         targetTable.DataSet.OnMergeFailed(e);\r
164                                         return false;\r
165                                 }\r
166                                 else\r
167                                 {\r
168                                         // we have to see that each primary column in the target table\r
169                                         // has a column with the same name in the sourcetable primarykey columns. \r
170                                         bool foundMatch;\r
171                                         DataColumn[] targetDataColumns = targetTable.PrimaryKey;\r
172                                         DataColumn[] srcDataColumns = sourceTable.PrimaryKey;\r
173 \r
174                                         // loop on all primary key columns in the targetTable.\r
175                                         for (int i = 0; i < targetDataColumns.Length; i++)\r
176                                         {\r
177                                                 foundMatch = false;\r
178                                                 DataColumn col = targetDataColumns[i];\r
179 \r
180                                                 // find if there is a column with the same name in the \r
181                                                 // sourceTable primary key columns.\r
182                                                 for (int j = 0; j < srcDataColumns.Length; j++)\r
183                                                 {\r
184                                                         if (srcDataColumns[j].ColumnName == col.ColumnName)\r
185                                                         {\r
186                                                                 foundMatch = true;\r
187                                                                 break;\r
188                                                         }\r
189                                                 }\r
190                                                 if (!foundMatch)\r
191                                                 {\r
192                                                         string message = "Mismatch columns in the PrimaryKey : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";\r
193                                                         MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);\r
194                                                         targetTable.DataSet.OnMergeFailed(e);\r
195                                                         return false;\r
196                                                 }\r
197                                                 \r
198                                         }\r
199                                 }\r
200                         }\r
201 \r
202                         return true;\r
203                 }\r
204                 \r
205                 // fill the data from the source table to the target table\r
206                 private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)\r
207                 {\r
208                         for (int i = 0; i < sourceTable.Rows.Count; i++)\r
209                         {\r
210                                 DataRow row = sourceTable.Rows[i];\r
211                                 MergeRow(targetTable, row, preserveChanges);\r
212                         }\r
213                 }\r
214                 \r
215                 // check tha column from 2 tables that has the same name also has the same datatype.\r
216                 private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)\r
217                 {\r
218                         for (int i = 0; i < sourceTable.Columns.Count; i++)\r
219                         {\r
220                                 DataColumn fromCol = sourceTable.Columns[i];\r
221                                 DataColumn toCol = targetTable.Columns[fromCol.ColumnName];\r
222                                 if((toCol != null) && (toCol.DataType != fromCol.DataType))\r
223                                         throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");\r
224                         }\r
225                 }\r
226         }\r
227 }\r