* DataSetReadXmlTest.cs: Added NotWorking test for bug #80048.
[mono.git] / mcs / class / System.Data / Mono.Data.SqlExpressions / ColumnReference.cs
index 488793285ee4fa434dd2f6ca79905842a6d98cac..ff11cf78215b00d43c5cac0af934c14129bfbb70 100644 (file)
@@ -33,6 +33,7 @@
 using System;
 using System.Collections;
 using System.Data;
+using System.ComponentModel;
 
 namespace Mono.Data.SqlExpressions {
        internal enum ReferencedTable {
@@ -44,6 +45,8 @@ namespace Mono.Data.SqlExpressions {
        internal class ColumnReference : BaseExpression {
                ReferencedTable refTable;
                string relationName, columnName;
+               DataColumn _cachedColumn;
+               DataRelation _cachedRelation;
 
                public ColumnReference (string columnName) : this (ReferencedTable.Self, null, columnName) {}
 
@@ -54,34 +57,95 @@ namespace Mono.Data.SqlExpressions {
                        this.columnName = columnName;
                }
 
+               public override bool Equals(object obj)
+               {
+                       if (!base.Equals (obj))
+                               return false;
+
+                       if (!(obj is ColumnReference))
+                               return false;
+
+                       ColumnReference other = (ColumnReference) obj;
+                       if (other.refTable != refTable)
+                               return false;
+
+                       if (other.columnName != columnName)
+                               return false;           
+
+                       if (other.relationName != relationName)
+                               return false;
+
+                       return true;
+               }
+
+               public override int GetHashCode()
+               {
+                       int hashCode = base.GetHashCode ();
+                       hashCode ^= refTable.GetHashCode ();
+                       hashCode ^= columnName.GetHashCode ();
+                       hashCode ^= relationName.GetHashCode ();
+                       return hashCode;
+               }
+
                public ReferencedTable ReferencedTable {
                        get { return refTable; }
                }
 
-               protected DataRelation GetRelation (DataRow row)
+               private DataRelation GetRelation (DataRow row)
                {
-                       DataRelationCollection relations;
-                       if (relationName != null) {
-                               relations = row.Table.DataSet.Relations;
-                               return relations[relations.IndexOf(relationName)];
+                       if (_cachedRelation == null) {
+                               DataTable table = row.Table;
+                               DataRelationCollection relations;
+                               if (relationName != null) {
+                                       relations = table.DataSet.Relations;
+                                       _cachedRelation = relations [relations.IndexOf (relationName)];
+                               }
+                               else {
+                                       if (refTable == ReferencedTable.Parent)
+                                               relations = table.ParentRelations;
+                                       else
+                                               relations = table.ChildRelations;
+                                               
+                                       if (relations.Count > 1)
+                                               throw new EvaluateException (String.Format (
+                                                       "The table [{0}] is involved in more than one relation." +
+                                                       "You must explicitly mention a relation name.",
+                                                       table.TableName));
+                                       else
+                                               _cachedRelation = relations [0];
+                               }
+                               _cachedRelation.DataSet.Relations.CollectionChanged += new CollectionChangeEventHandler (OnRelationRemoved);
                        }
+                       return _cachedRelation;
+               }
 
-                       if (refTable == ReferencedTable.Parent)
-                               relations = row.Table.ParentRelations;
-                       else
-                               relations = row.Table.ChildRelations;
-                               
-                       if (relations.Count > 1)
-                               throw new EvaluateException (String.Format (
-                                       "The table [{0}] is involved in more than one relation." +
-                                       "You must explicitly mention a relation name.",
-                                       row.Table.TableName));
-                       else
-                               return relations[0];
+               private DataColumn GetColumn (DataRow row)
+               {
+                       if (_cachedColumn == null) {
+                               DataTable table = row.Table;
+                               switch (refTable) {
+                                       case ReferencedTable.Parent:
+                                               table = GetRelation (row).ParentTable;
+                                               break;
+                                       case ReferencedTable.Child:
+                                               table = GetRelation (row).ChildTable;
+                                               break;
+                               }
+                               _cachedColumn = table.Columns [columnName];
+                               if (_cachedColumn == null)
+                                       throw new EvaluateException (String.Format ("Cannot find column [{0}].", columnName));
+
+                               _cachedColumn.PropertyChanged += new PropertyChangedEventHandler (OnColumnPropertyChanged);
+                               _cachedColumn.Table.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnRemoved);
+                       }
+                       return _cachedColumn;
                }
 
                public DataRow GetReferencedRow (DataRow row)
                {
+                       // Verify the column reference is valid 
+                       GetColumn (row);
+
                        switch (refTable) {
                        case ReferencedTable.Self:
                        default:
@@ -97,6 +161,9 @@ namespace Mono.Data.SqlExpressions {
                
                public DataRow[] GetReferencedRows (DataRow row)
                {
+                       // Verify the column reference is valid 
+                       GetColumn (row);
+
                        switch (refTable) {
                        case ReferencedTable.Self:
                        default:
@@ -116,7 +183,7 @@ namespace Mono.Data.SqlExpressions {
                {
                        object[] values = new object [rows.Length];
                        for (int i = 0; i < rows.Length; i++)
-                               values [i] = Unify (rows [i][columnName]);
+                               values [i] = Unify (rows [i][GetColumn (rows [i])]);
                                
                        return values;
                }
@@ -146,7 +213,7 @@ namespace Mono.Data.SqlExpressions {
                        object val;
                        try {
                                referencedRow._inExpressionEvaluation = true;
-                               val = referencedRow [columnName];
+                               val = referencedRow [GetColumn (row)];
                                referencedRow._inExpressionEvaluation = false;
                        } catch (IndexOutOfRangeException) {
                                throw new EvaluateException (String.Format ("Cannot find column [{0}].", columnName));
@@ -154,9 +221,84 @@ namespace Mono.Data.SqlExpressions {
                        return Unify (val);
                }
 
+               public override bool EvalBoolean (DataRow row)
+               {
+                       DataColumn col = GetColumn (row);
+                       if (col.DataType != typeof (bool))
+                               throw new EvaluateException ("Not a Boolean Expression");
+
+                       object result  = Eval (row);
+                       if (result == null || result == DBNull.Value)
+                               return false;
+                       else
+                               return (bool)result;
+               }
+
                override public bool DependsOn(DataColumn other)
                {
                        return refTable == ReferencedTable.Self && columnName == other.ColumnName;
                }
+
+               private void DropCached (DataColumnCollection columnCollection, DataRelationCollection relationCollection)
+               {
+                       if (_cachedColumn != null) {
+                               // unregister column listener
+                               _cachedColumn.PropertyChanged -= new PropertyChangedEventHandler (OnColumnPropertyChanged);
+
+                               // unregister column collection listener
+                               if (columnCollection != null)   
+                                       columnCollection.CollectionChanged -= new CollectionChangeEventHandler (OnColumnRemoved);
+                               else if (_cachedColumn.Table != null)
+                                       _cachedColumn.Table.Columns.CollectionChanged -= new CollectionChangeEventHandler (OnColumnRemoved);
+                               
+                               _cachedColumn = null;
+                       }
+
+                       if (_cachedRelation != null) {
+                               // unregister relation collection listener
+                               if (relationCollection != null)                         
+                                       relationCollection.CollectionChanged -= new CollectionChangeEventHandler (OnRelationRemoved);
+                               else if (_cachedRelation.DataSet != null)
+                                       _cachedRelation.DataSet.Relations.CollectionChanged -= new CollectionChangeEventHandler (OnRelationRemoved);
+
+                               _cachedRelation = null;
+                       }                       
+               }
+
+               private void OnColumnPropertyChanged (object sender, PropertyChangedEventArgs args)
+               {
+                       if (!(sender is DataColumn))
+                               return;
+                       
+                       DataColumn dc = (DataColumn) sender;
+                       if ((dc == _cachedColumn) && args.PropertyName == "ColumnName")
+                               DropCached (null, null);
+               }
+
+               private void OnColumnRemoved (object sender, CollectionChangeEventArgs args)
+               {
+                       if (!(args.Element is DataColumnCollection))
+                               return;
+
+                       if (args.Action != CollectionChangeAction.Remove)
+                               return;
+
+                       DataColumnCollection columnCollection = (DataColumnCollection) args.Element;
+                       if (_cachedColumn != null && columnCollection != null && (columnCollection.IndexOf (_cachedColumn)) == -1)
+                               DropCached (columnCollection, null);
+               }
+
+               private void OnRelationRemoved (object sender, CollectionChangeEventArgs args)
+               {
+                       if (!(args.Element is DataRelationCollection))
+                               return;
+
+                       if (args.Action != CollectionChangeAction.Remove)
+                               return;                 
+
+                       DataRelationCollection relationCollection = (DataRelationCollection) args.Element;
+                       if (_cachedRelation != null && relationCollection != null && (relationCollection.IndexOf (_cachedRelation)) == -1)
+                               DropCached (null, relationCollection);
+               }
        }
 }