5 // Juraj Skripsky (juraj@hotfeet.ch)
7 // (C) 2004 HotFeet GmbH (http://www.hotfeet.ch)
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
36 using System.ComponentModel;
38 namespace Mono.Data.SqlExpressions {
39 internal enum ReferencedTable {
45 internal class ColumnReference : BaseExpression {
46 ReferencedTable refTable;
47 string relationName, columnName;
48 DataColumn _cachedColumn;
49 DataRelation _cachedRelation;
51 public ColumnReference (string columnName) : this (ReferencedTable.Self, null, columnName) {}
53 public ColumnReference (ReferencedTable refTable, string relationName, string columnName)
55 this.refTable = refTable;
56 this.relationName = relationName;
57 this.columnName = columnName;
60 public override bool Equals(object obj)
62 if (!base.Equals (obj))
65 if (!(obj is ColumnReference))
68 ColumnReference other = (ColumnReference) obj;
69 if (other.refTable != refTable)
72 if (other.columnName != columnName)
75 if (other.relationName != relationName)
81 public override int GetHashCode()
83 int hashCode = base.GetHashCode ();
84 hashCode ^= refTable.GetHashCode ();
85 hashCode ^= columnName.GetHashCode ();
86 hashCode ^= relationName.GetHashCode ();
90 public ReferencedTable ReferencedTable {
91 get { return refTable; }
94 private DataRelation GetRelation (DataRow row)
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)];
104 if (refTable == ReferencedTable.Parent)
105 relations = table.ParentRelations;
107 relations = table.ChildRelations;
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.",
115 _cachedRelation = relations [0];
117 _cachedRelation.DataSet.Relations.CollectionChanged += new CollectionChangeEventHandler (OnRelationRemoved);
119 return _cachedRelation;
122 private DataColumn GetColumn (DataRow row)
124 if (_cachedColumn == null) {
125 DataTable table = row.Table;
127 case ReferencedTable.Parent:
128 table = GetRelation (row).ParentTable;
130 case ReferencedTable.Child:
131 table = GetRelation (row).ChildTable;
134 _cachedColumn = table.Columns [columnName];
135 if (_cachedColumn == null)
136 throw new EvaluateException (String.Format ("Cannot find column [{0}].", columnName));
138 _cachedColumn.PropertyChanged += new PropertyChangedEventHandler (OnColumnPropertyChanged);
139 _cachedColumn.Table.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnRemoved);
141 return _cachedColumn;
144 public DataRow GetReferencedRow (DataRow row)
146 // Verify the column reference is valid
150 case ReferencedTable.Self:
154 case ReferencedTable.Parent:
155 return row.GetParentRow (GetRelation (row));
157 case ReferencedTable.Child:
158 return row.GetChildRows (GetRelation (row)) [0];
162 public DataRow[] GetReferencedRows (DataRow row)
164 // Verify the column reference is valid
168 case ReferencedTable.Self:
170 DataRow[] rows = row.Table.NewRowArray(row.Table.Rows.Count);
171 row.Table.Rows.CopyTo (rows, 0);
174 case ReferencedTable.Parent:
175 return row.GetParentRows (GetRelation (row));
177 case ReferencedTable.Child:
178 return row.GetChildRows (GetRelation (row));
182 public object[] GetValues (DataRow[] rows)
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])]);
191 private object Unify (object val) {
192 if (Numeric.IsNumeric (val))
193 return Numeric.Unify ((IConvertible)val);
195 if (val == null || val == DBNull.Value)
198 if (val is bool || val is string || val is DateTime || val is Guid || val is char)
204 throw new EvaluateException (String.Format ("Cannot handle data type found in column '{0}'.", columnName));
207 public override object Eval (DataRow row)
209 DataRow referencedRow = GetReferencedRow (row);
210 if (referencedRow == null)
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));
224 public override bool EvalBoolean (DataRow row)
226 DataColumn col = GetColumn (row);
227 if (col.DataType != typeof (bool))
228 throw new EvaluateException ("Not a Boolean Expression");
230 object result = Eval (row);
231 if (result == null || result == DBNull.Value)
237 override public bool DependsOn(DataColumn other)
239 return refTable == ReferencedTable.Self && columnName == other.ColumnName;
242 private void DropCached (DataColumnCollection columnCollection, DataRelationCollection relationCollection)
244 if (_cachedColumn != null) {
245 // unregister column listener
246 _cachedColumn.PropertyChanged -= new PropertyChangedEventHandler (OnColumnPropertyChanged);
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);
254 _cachedColumn = null;
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);
264 _cachedRelation = null;
268 private void OnColumnPropertyChanged (object sender, PropertyChangedEventArgs args)
270 if (!(sender is DataColumn))
273 DataColumn dc = (DataColumn) sender;
274 if ((dc == _cachedColumn) && args.PropertyName == "ColumnName")
275 DropCached (null, null);
278 private void OnColumnRemoved (object sender, CollectionChangeEventArgs args)
280 if (!(args.Element is DataColumnCollection))
283 if (args.Action != CollectionChangeAction.Remove)
286 DataColumnCollection columnCollection = (DataColumnCollection) args.Element;
287 if (_cachedColumn != null && columnCollection != null && (columnCollection.IndexOf (_cachedColumn)) == -1)
288 DropCached (columnCollection, null);
291 private void OnRelationRemoved (object sender, CollectionChangeEventArgs args)
293 if (!(args.Element is DataRelationCollection))
296 if (args.Action != CollectionChangeAction.Remove)
299 DataRelationCollection relationCollection = (DataRelationCollection) args.Element;
300 if (_cachedRelation != null && relationCollection != null && (relationCollection.IndexOf (_cachedRelation)) == -1)
301 DropCached (null, relationCollection);