Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.DataSetExtensions / System / Data / DataRowComparer.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DataRowComparer.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">spather</owner>
7 //------------------------------------------------------------------------------
8 using System;
9 using System.Data;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Data.DataSetExtensions;
14
15 namespace System.Data
16 {
17     /// <summary>
18     /// This class implements IEqualityComparer using value based semantics
19     /// when comparing DataRows.
20     /// </summary>
21     public static class DataRowComparer
22     {
23         /// <summary>
24         /// Gets the singleton instance of the data row comparer.
25         /// </summary>
26         public static DataRowComparer<DataRow> Default { get { return DataRowComparer<DataRow>.Default; } }
27
28         internal static bool AreEqual(object a, object b)
29         {
30             if (Object.ReferenceEquals(a, b))
31             {   // same reference or (null, null) or (DBNull.Value, DBNull.Value)
32                 return true;
33             }
34             if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
35                 Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
36             {   // (null, non-null) or (null, DBNull.Value) or vice versa
37                 return false;
38             }
39             return (a.Equals(b) || (a.GetType().IsArray && CompareArray((Array)a, b as Array)));
40         }
41
42         private static bool AreElementEqual(object a, object b)
43         {
44             if (Object.ReferenceEquals(a, b))
45             {   // same reference or (null, null) or (DBNull.Value, DBNull.Value)
46                 return true;
47             }
48             if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
49                 Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
50             {   // (null, non-null) or (null, DBNull.Value) or vice versa
51                 return false;
52             }
53             return a.Equals(b);
54         }
55
56         private static bool CompareArray(Array a, Array b)
57         {
58             if ((null == b) ||
59                 (1 != a.Rank) ||
60                 (1 != b.Rank) ||
61                 (a.Length != b.Length))
62             {   // automatically consider array's with Rank>1 not-equal
63                 return false;
64             }
65
66             int index1 = a.GetLowerBound(0);
67             int index2 = b.GetLowerBound(0);
68             if (a.GetType() == b.GetType() && (0 == index1) && (0 == index2))
69             {
70                 switch (Type.GetTypeCode(a.GetType().GetElementType()))
71                 {
72                     case TypeCode.Byte:
73                         return DataRowComparer.CompareEquatableArray<Byte>((Byte[])a, (Byte[])b);
74                     case TypeCode.Int16:
75                         return DataRowComparer.CompareEquatableArray<Int16>((Int16[])a, (Int16[])b);
76                     case TypeCode.Int32:
77                         return DataRowComparer.CompareEquatableArray<Int32>((Int32[])a, (Int32[])b);
78                     case TypeCode.Int64:
79                         return DataRowComparer.CompareEquatableArray<Int64>((Int64[])a, (Int64[])b);
80                     case TypeCode.String:
81                         return DataRowComparer.CompareEquatableArray<String>((String[])a, (String[])b);
82                 }
83             }
84
85             //Compare every element. But don't recurse if we have Array of array.
86             int length = index1 + a.Length;
87             for (; index1 < length; ++index1, ++index2)
88             {
89                 if (!AreElementEqual(a.GetValue(index1), b.GetValue(index2)))
90                 {
91                     return false;
92                 }
93             }
94             return true;
95         }
96
97         private static bool CompareEquatableArray<TElem>(TElem[] a, TElem[] b) where TElem : IEquatable<TElem>
98         {
99             if (Object.ReferenceEquals(a, b))
100             {
101                 return true;
102             }
103             if (Object.ReferenceEquals(a, null) ||
104                 Object.ReferenceEquals(b, null))
105             {
106                 return false;
107             }
108             if (a.Length != b.Length)
109             {
110                 return false;
111             }
112
113             for (int i = 0; i < a.Length; ++i)
114             {
115                 if (!a[i].Equals(b[i]))
116                 {
117                     return false;
118                 }
119             }
120             return true;
121         }
122     }
123
124     /// <summary>
125     /// This class implements IEqualityComparer using value based semantics
126     /// when comparing DataRows.
127     /// </summary>
128     public sealed class DataRowComparer<TRow> : IEqualityComparer<TRow> where TRow : DataRow
129     {
130         /// <summary>
131         /// Private constructor to prevent initialization outside of Default singleton instance.
132         /// </summary>
133         private DataRowComparer() { }
134
135         private static DataRowComparer<TRow> _instance = new DataRowComparer<TRow>();
136
137         /// <summary>
138         /// Gets the singleton instance of the data row comparer.
139         /// </summary>
140         public static DataRowComparer<TRow> Default { get { return _instance; } }
141
142         /// <summary>
143         /// This method compares to DataRows by doing a column by column value based
144         /// comparision.
145         /// </summary>
146         /// <param name="leftRow">
147         ///   The first input DataRow
148         /// </param>
149         /// <param name="rightRow">
150         ///   The second input DataRow
151         /// </param>
152         /// <returns>
153         ///   True if rows are equal, false if not.
154         /// </returns>
155         public bool Equals(TRow leftRow, TRow rightRow)
156         {
157             if (Object.ReferenceEquals(leftRow, rightRow))
158             {
159                 return true;
160             }
161             if (Object.ReferenceEquals(leftRow, null) ||
162                 Object.ReferenceEquals(rightRow, null))
163             {
164                 return false;
165             }
166
167             if (leftRow.RowState == DataRowState.Deleted || rightRow.RowState == DataRowState.Deleted)
168             {
169                 throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
170             }
171
172             int count = leftRow.Table.Columns.Count;
173             if (count != rightRow.Table.Columns.Count)
174             {
175                 return false;
176             }
177
178             for (int i = 0; i < count; ++i)
179             {
180                 if (!DataRowComparer.AreEqual(leftRow[i], rightRow[i]))
181                 {
182                     return false;
183                 }
184             }
185             return true;
186         }
187
188         /// <summary>
189         /// This mtheod retrieves a hash code for the source row.
190         /// </summary>
191         /// <param name="row">
192         ///   The source DataRow
193         /// </param>
194         /// <returns>
195         ///   HashCode for row based on values in the row.
196         /// </returns>
197         public int GetHashCode(TRow row)
198         {
199             DataSetUtil.CheckArgumentNull(row, "row");
200
201             if (row.RowState == DataRowState.Deleted)
202             {
203                 throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
204             }
205
206             int hash = 0;
207             Debug.Assert(row.Table != null);
208             if (row.Table.Columns.Count > 0)
209             {
210                 // if the row has at least one column, then use the first column value
211                 object value = row[0];
212
213                 Type valueType = value.GetType();
214                 if (valueType.IsArray)
215                 {
216                     Array array = value as Array;
217
218                     if (array.Rank > 1)
219                     {
220                         hash = value.GetHashCode();
221                     }
222                     else if (array.Length > 0)
223                     {
224                         hash = array.GetValue(array.GetLowerBound(0)).GetHashCode();
225                     }
226                 }
227                 else
228                 {
229                     System.ValueType vt = value as System.ValueType;
230
231                     // have to unbox value types.
232                     if (vt != null)
233                     {
234                         hash = vt.GetHashCode();
235                     }
236                     else
237                     {
238                         hash = value.GetHashCode();
239                     }
240                 }
241             }
242             // if table has no columns, the hash code is 0
243             return hash;
244         }
245     }
246 }