[runtime] Overwrite stacktrace for exception on re-throw. Fixes #1856.
[mono.git] / mcs / class / System.Data / Mono.Data.SqlExpressions / ColumnReference.cs
1 //
2 // ColumnReference.cs
3 //
4 // Author:
5 //   Juraj Skripsky (juraj@hotfeet.ch)
6 //
7 // (C) 2004 HotFeet GmbH (http://www.hotfeet.ch)
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Data;
36 using System.ComponentModel;
37
38 namespace Mono.Data.SqlExpressions {
39         internal enum ReferencedTable {
40                 Self,
41                 Parent,
42                 Child
43         }
44         
45         internal class ColumnReference : BaseExpression {
46                 ReferencedTable refTable;
47                 string relationName, columnName;
48                 DataColumn _cachedColumn;
49                 DataRelation _cachedRelation;
50
51                 public ColumnReference (string columnName) : this (ReferencedTable.Self, null, columnName) {}
52
53                 public ColumnReference (ReferencedTable refTable, string relationName, string columnName)
54                 {
55                         this.refTable = refTable;
56                         this.relationName = relationName;
57                         this.columnName = columnName;
58                 }
59
60                 public override bool Equals(object obj)
61                 {
62                         if (!base.Equals (obj))
63                                 return false;
64
65                         if (!(obj is ColumnReference))
66                                 return false;
67
68                         ColumnReference other = (ColumnReference) obj;
69                         if (other.refTable != refTable)
70                                 return false;
71
72                         if (other.columnName != columnName)
73                                 return false;           
74
75                         if (other.relationName != relationName)
76                                 return false;
77
78                         return true;
79                 }
80
81                 public override int GetHashCode()
82                 {
83                         int hashCode = base.GetHashCode ();
84                         hashCode ^= refTable.GetHashCode ();
85                         hashCode ^= columnName.GetHashCode ();
86                         hashCode ^= relationName.GetHashCode ();
87                         return hashCode;
88                 }
89
90                 public ReferencedTable ReferencedTable {
91                         get { return refTable; }
92                 }
93
94                 private DataRelation GetRelation (DataRow row)
95                 {
96                         if (_cachedRelation == null) {
97                                 DataTable table = row.Table;
98                                 DataRelationCollection relations;
99                                 if (relationName != null) {
100                                         relations = table.DataSet.Relations;
101                                         _cachedRelation = relations [relations.IndexOf (relationName)];
102                                 }
103                                 else {
104                                         if (refTable == ReferencedTable.Parent)
105                                                 relations = table.ParentRelations;
106                                         else
107                                                 relations = table.ChildRelations;
108                                                 
109                                         if (relations.Count > 1)
110                                                 throw new EvaluateException (String.Format (
111                                                         "The table [{0}] is involved in more than one relation." +
112                                                         "You must explicitly mention a relation name.",
113                                                         table.TableName));
114                                         else
115                                                 _cachedRelation = relations [0];
116                                 }
117                                 _cachedRelation.DataSet.Relations.CollectionChanged += new CollectionChangeEventHandler (OnRelationRemoved);
118                         }
119                         return _cachedRelation;
120                 }
121
122                 private DataColumn GetColumn (DataRow row)
123                 {
124                         if (_cachedColumn == null) {
125                                 DataTable table = row.Table;
126                                 switch (refTable) {
127                                         case ReferencedTable.Parent:
128                                                 table = GetRelation (row).ParentTable;
129                                                 break;
130                                         case ReferencedTable.Child:
131                                                 table = GetRelation (row).ChildTable;
132                                                 break;
133                                 }
134                                 _cachedColumn = table.Columns [columnName];
135                                 if (_cachedColumn == null)
136                                         throw new EvaluateException (String.Format ("Cannot find column [{0}].", columnName));
137
138                                 _cachedColumn.PropertyChanged += new PropertyChangedEventHandler (OnColumnPropertyChanged);
139                                 _cachedColumn.Table.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnRemoved);
140                         }
141                         return _cachedColumn;
142                 }
143
144                 public DataRow GetReferencedRow (DataRow row)
145                 {
146                         // Verify the column reference is valid 
147                         GetColumn (row);
148
149                         switch (refTable) {
150                         case ReferencedTable.Self:
151                         default:
152                                 return row;
153
154                         case ReferencedTable.Parent:
155                                 return row.GetParentRow (GetRelation (row));
156
157                         case ReferencedTable.Child:
158                                 return row.GetChildRows (GetRelation (row)) [0];
159                         }
160                 }
161                 
162                 public DataRow[] GetReferencedRows (DataRow row)
163                 {
164                         // Verify the column reference is valid 
165                         GetColumn (row);
166
167                         switch (refTable) {
168                         case ReferencedTable.Self:
169                         default:
170                                 DataRow[] rows = row.Table.NewRowArray(row.Table.Rows.Count);
171                                 row.Table.Rows.CopyTo (rows, 0);
172                                 return rows;
173                                 
174                         case ReferencedTable.Parent:
175                                 return row.GetParentRows (GetRelation (row));
176
177                         case ReferencedTable.Child:
178                                 return row.GetChildRows (GetRelation (row));
179                         }
180                 }
181                 
182                 public object[] GetValues (DataRow[] rows)
183                 {
184                         object[] values = new object [rows.Length];
185                         for (int i = 0; i < rows.Length; i++)
186                                 values [i] = Unify (rows [i][GetColumn (rows [i])]);
187                                 
188                         return values;
189                 }
190
191                 private object Unify (object val) {
192                         if (Numeric.IsNumeric (val))
193                                 return Numeric.Unify ((IConvertible)val);
194                                 
195                         if (val == null || val == DBNull.Value)
196                                 return null;
197                                 
198                         if (val is bool || val is string || val is DateTime || val is Guid || val is char)
199                                 return val;
200                         
201                         if (val is Enum)
202                                 return (int)val;
203                         
204                         throw new EvaluateException (String.Format ("Cannot handle data type found in column '{0}'.", columnName));                     
205                 }
206
207                 public override object Eval (DataRow row)
208                 {
209                         DataRow referencedRow = GetReferencedRow (row);
210                         if (referencedRow == null)
211                                 return null;
212                                 
213                         object val;
214                         try {
215                                 referencedRow._inExpressionEvaluation = true;
216                                 val = referencedRow [GetColumn (row)];
217                                 referencedRow._inExpressionEvaluation = false;
218                         } catch (IndexOutOfRangeException) {
219                                 throw new EvaluateException (String.Format ("Cannot find column [{0}].", columnName));
220                         }
221                         return Unify (val);
222                 }
223
224                 public override bool EvalBoolean (DataRow row)
225                 {
226                         DataColumn col = GetColumn (row);
227                         if (col.DataType != typeof (bool))
228                                 throw new EvaluateException ("Not a Boolean Expression");
229
230                         object result  = Eval (row);
231                         if (result == null || result == DBNull.Value)
232                                 return false;
233                         else
234                                 return (bool)result;
235                 }
236
237                 override public bool DependsOn(DataColumn other)
238                 {
239                         return refTable == ReferencedTable.Self && columnName == other.ColumnName;
240                 }
241
242                 private void DropCached (DataColumnCollection columnCollection, DataRelationCollection relationCollection)
243                 {
244                         if (_cachedColumn != null) {
245                                 // unregister column listener
246                                 _cachedColumn.PropertyChanged -= new PropertyChangedEventHandler (OnColumnPropertyChanged);
247
248                                 // unregister column collection listener
249                                 if (columnCollection != null)   
250                                         columnCollection.CollectionChanged -= new CollectionChangeEventHandler (OnColumnRemoved);
251                                 else if (_cachedColumn.Table != null)
252                                         _cachedColumn.Table.Columns.CollectionChanged -= new CollectionChangeEventHandler (OnColumnRemoved);
253                                 
254                                 _cachedColumn = null;
255                         }
256
257                         if (_cachedRelation != null) {
258                                 // unregister relation collection listener
259                                 if (relationCollection != null)                         
260                                         relationCollection.CollectionChanged -= new CollectionChangeEventHandler (OnRelationRemoved);
261                                 else if (_cachedRelation.DataSet != null)
262                                         _cachedRelation.DataSet.Relations.CollectionChanged -= new CollectionChangeEventHandler (OnRelationRemoved);
263
264                                 _cachedRelation = null;
265                         }                       
266                 }
267
268                 private void OnColumnPropertyChanged (object sender, PropertyChangedEventArgs args)
269                 {
270                         if (!(sender is DataColumn))
271                                 return;
272                         
273                         DataColumn dc = (DataColumn) sender;
274                         if ((dc == _cachedColumn) && args.PropertyName == "ColumnName")
275                                 DropCached (null, null);
276                 }
277
278                 private void OnColumnRemoved (object sender, CollectionChangeEventArgs args)
279                 {
280                         if (!(args.Element is DataColumnCollection))
281                                 return;
282
283                         if (args.Action != CollectionChangeAction.Remove)
284                                 return;
285
286                         DataColumnCollection columnCollection = (DataColumnCollection) args.Element;
287                         if (_cachedColumn != null && columnCollection != null && (columnCollection.IndexOf (_cachedColumn)) == -1)
288                                 DropCached (columnCollection, null);
289                 }
290
291                 private void OnRelationRemoved (object sender, CollectionChangeEventArgs args)
292                 {
293                         if (!(args.Element is DataRelationCollection))
294                                 return;
295
296                         if (args.Action != CollectionChangeAction.Remove)
297                                 return;                 
298
299                         DataRelationCollection relationCollection = (DataRelationCollection) args.Element;
300                         if (_cachedRelation != null && relationCollection != null && (relationCollection.IndexOf (_cachedRelation)) == -1)
301                                 DropCached (null, relationCollection);
302                 }
303         }
304 }