1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 
7 namespace System.Data
8 {
9     internal readonly struct DataKey
10     {
11         private const int maxColumns = 32;
12 
13         private readonly DataColumn[] _columns;
14 
DataKeySystem.Data.DataKey15         internal DataKey(DataColumn[] columns, bool copyColumns)
16         {
17             if (columns == null)
18             {
19                 throw ExceptionBuilder.ArgumentNull(nameof(columns));
20             }
21 
22             if (columns.Length == 0)
23             {
24                 throw ExceptionBuilder.KeyNoColumns();
25             }
26 
27             if (columns.Length > maxColumns)
28             {
29                 throw ExceptionBuilder.KeyTooManyColumns(maxColumns);
30             }
31 
32             for (int i = 0; i < columns.Length; i++)
33             {
34                 if (columns[i] == null)
35                 {
36                     throw ExceptionBuilder.ArgumentNull("column");
37                 }
38             }
39 
40             for (int i = 0; i < columns.Length; i++)
41             {
42                 for (int j = 0; j < i; j++)
43                 {
44                     if (columns[i] == columns[j])
45                     {
46                         throw ExceptionBuilder.KeyDuplicateColumns(columns[i].ColumnName);
47                     }
48                 }
49             }
50 
51             if (copyColumns)
52             {
53                 // Need to make a copy of all columns
54                 _columns = new DataColumn[columns.Length];
55                 for (int i = 0; i < columns.Length; i++)
56                 {
57                     _columns[i] = columns[i];
58                 }
59             }
60             else
61             {
62                 // take ownership of the array passed in
63                 _columns = columns;
64             }
65             CheckState();
66         }
67 
68         internal DataColumn[] ColumnsReference => _columns;
69         internal bool HasValue => null != _columns;
70         internal DataTable Table => _columns[0].Table;
CheckStateSystem.Data.DataKey71         internal void CheckState()
72         {
73             DataTable table = _columns[0].Table;
74 
75             if (table == null)
76             {
77                 throw ExceptionBuilder.ColumnNotInAnyTable();
78             }
79 
80             for (int i = 1; i < _columns.Length; i++)
81             {
82                 if (_columns[i].Table == null)
83                 {
84                     throw ExceptionBuilder.ColumnNotInAnyTable();
85                 }
86                 if (_columns[i].Table != table)
87                 {
88                     throw ExceptionBuilder.KeyTableMismatch();
89                 }
90             }
91         }
92 
93         //check to see if this.columns && key2's columns are equal regardless of order
94         internal bool ColumnsEqual(DataKey key) => ColumnsEqual(_columns, key._columns);
95 
96         //check to see if columns1 && columns2 are equal regardless of order
ColumnsEqualSystem.Data.DataKey97         internal static bool ColumnsEqual(DataColumn[] column1, DataColumn[] column2)
98         {
99             if (column1 == column2)
100             {
101                 return true;
102             }
103             else if (column1 == null || column2 == null)
104             {
105                 return false;
106             }
107             else if (column1.Length != column2.Length)
108             {
109                 return false;
110             }
111             else
112             {
113                 int i, j;
114                 for (i = 0; i < column1.Length; i++)
115                 {
116                     bool check = false;
117                     for (j = 0; j < column2.Length; j++)
118                     {
119                         if (column1[i].Equals(column2[j]))
120                         {
121                             check = true;
122                             break;
123                         }
124                     }
125                     if (!check)
126                     {
127                         return false;
128                     }
129                 }
130             }
131             return true;
132         }
133 
ContainsColumnSystem.Data.DataKey134         internal bool ContainsColumn(DataColumn column)
135         {
136             for (int i = 0; i < _columns.Length; i++)
137             {
138                 if (column == _columns[i])
139                 {
140                     return true;
141                 }
142             }
143             return false;
144         }
145 
GetHashCodeSystem.Data.DataKey146         public override int GetHashCode()
147         {
148             Debug.Assert(false, "don't put DataKey into a Hashtable");
149             return base.GetHashCode();
150         }
151 
EqualsSystem.Data.DataKey152         public override bool Equals(object value)
153         {
154             Debug.Assert(false, "need to directly call Equals(DataKey)");
155             return Equals((DataKey)value);
156         }
157 
EqualsSystem.Data.DataKey158         internal bool Equals(DataKey value)
159         {
160             //check to see if this.columns && key2's columns are equal...
161             DataColumn[] column1 = _columns;
162             DataColumn[] column2 = value._columns;
163 
164             if (column1 == column2)
165             {
166                 return true;
167             }
168             else if (column1 == null || column2 == null)
169             {
170                 return false;
171             }
172             else if (column1.Length != column2.Length)
173             {
174                 return false;
175             }
176             else
177             {
178                 for (int i = 0; i < column1.Length; i++)
179                 {
180                     if (!column1[i].Equals(column2[i]))
181                     {
182                         return false;
183                     }
184                 }
185                 return true;
186             }
187         }
188 
GetColumnNamesSystem.Data.DataKey189         internal string[] GetColumnNames()
190         {
191             string[] values = new string[_columns.Length];
192             for (int i = 0; i < _columns.Length; ++i)
193             {
194                 values[i] = _columns[i].ColumnName;
195             }
196             return values;
197         }
198 
GetIndexDescSystem.Data.DataKey199         internal IndexField[] GetIndexDesc()
200         {
201             IndexField[] indexDesc = new IndexField[_columns.Length];
202             for (int i = 0; i < _columns.Length; i++)
203             {
204                 indexDesc[i] = new IndexField(_columns[i], false);
205             }
206             return indexDesc;
207         }
208 
GetKeyValuesSystem.Data.DataKey209         internal object[] GetKeyValues(int record)
210         {
211             object[] values = new object[_columns.Length];
212             for (int i = 0; i < _columns.Length; i++)
213             {
214                 values[i] = _columns[i][record];
215             }
216             return values;
217         }
218 
GetSortIndexSystem.Data.DataKey219         internal Index GetSortIndex() => GetSortIndex(DataViewRowState.CurrentRows);
220 
GetSortIndexSystem.Data.DataKey221         internal Index GetSortIndex(DataViewRowState recordStates)
222         {
223             IndexField[] indexDesc = GetIndexDesc();
224             return _columns[0].Table.GetIndex(indexDesc, recordStates, null);
225         }
226 
RecordsEqualSystem.Data.DataKey227         internal bool RecordsEqual(int record1, int record2)
228         {
229             for (int i = 0; i < _columns.Length; i++)
230             {
231                 if (_columns[i].Compare(record1, record2) != 0)
232                 {
233                     return false;
234                 }
235             }
236             return true;
237         }
238 
ToArraySystem.Data.DataKey239         internal DataColumn[] ToArray()
240         {
241             DataColumn[] values = new DataColumn[_columns.Length];
242             for (int i = 0; i < _columns.Length; ++i)
243             {
244                 values[i] = _columns[i];
245             }
246             return values;
247         }
248     }
249 }
250