//---------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Diagnostics;
namespace System.Data.Query.InternalTrees
{
#region RuleProcessor
///
/// The RuleProcessor helps apply a set of rules to a query tree
///
internal class RuleProcessor
{
#region private state
///
/// A lookup table for rules.
/// The lookup table is an array indexed by OpType and each entry has a list of rules.
///
private Dictionary m_processedNodeMap;
#endregion
#region constructors
///
/// Initializes a new RuleProcessor
///
internal RuleProcessor()
{
// Build up the accelerator tables
m_processedNodeMap = new Dictionary();
}
#endregion
#region private methods
private static bool ApplyRulesToNode(RuleProcessingContext context, ReadOnlyCollection> rules, Node currentNode, out Node newNode)
{
newNode = currentNode;
// Apply any pre-rule delegates
context.PreProcess(currentNode);
foreach (Rule r in rules[(int)currentNode.Op.OpType])
{
if (!r.Match(currentNode))
{
continue;
}
// Did the rule modify the subtree?
if (r.Apply(context, currentNode, out newNode))
{
// The node has changed; don't try to apply any more rules
context.PostProcess(newNode, r);
return true;
}
else
{
Debug.Assert(newNode == currentNode, "Liar! This rule should have returned 'true'");
}
}
context.PostProcess(currentNode, null);
return false;
}
///
/// Apply rules to the current subtree in a bottom-up fashion.
///
/// Current rule processing context
/// The look-up table with the rules to be applied
/// Current subtree
/// Parent node
/// Index of this child within the parent
/// the result of the transformation
private Node ApplyRulesToSubtree(RuleProcessingContext context,
ReadOnlyCollection> rules,
Node subTreeRoot, Node parent, int childIndexInParent)
{
int loopCount = 0;
Dictionary localProcessedMap = new Dictionary();
SubTreeId subTreeId;
while (true)
{
// Am I looping forever
Debug.Assert(loopCount < 12, "endless loops?");
loopCount++;
//
// We may need to update state regardless of whether this subTree has
// changed after it has been processed last. For example, it may be
// affected by transformation in its siblings due to external references.
//
context.PreProcessSubTree(subTreeRoot);
subTreeId = new SubTreeId(context, subTreeRoot, parent, childIndexInParent);
// Have I seen this subtree already? Just return, if so
if (m_processedNodeMap.ContainsKey(subTreeId))
{
break;
}
// Avoid endless loops here - avoid cycles of 2 or more
if (localProcessedMap.ContainsKey(subTreeId))
{
// mark this subtree as processed
m_processedNodeMap[subTreeId] = subTreeId;
break;
}
// Keep track of this one
localProcessedMap[subTreeId] = subTreeId;
// Walk my children
for (int i = 0; i < subTreeRoot.Children.Count; i++)
{
subTreeRoot.Children[i] = ApplyRulesToSubtree(context, rules, subTreeRoot.Children[i], subTreeRoot, i);
}
// Apply rules to myself. If no transformations were performed,
// then mark this subtree as processed, and break out
Node newSubTreeRoot;
if (!ApplyRulesToNode(context, rules, subTreeRoot, out newSubTreeRoot))
{
Debug.Assert(subTreeRoot == newSubTreeRoot);
// mark this subtree as processed
m_processedNodeMap[subTreeId] = subTreeId;
break;
}
context.PostProcessSubTree(subTreeRoot);
subTreeRoot = newSubTreeRoot;
}
context.PostProcessSubTree(subTreeRoot);
return subTreeRoot;
}
#endregion
#region public methods
///
/// Apply a set of rules to the subtree
///
/// Rule processing context
/// current subtree
/// transformed subtree
internal Node ApplyRulesToSubtree(RuleProcessingContext context, ReadOnlyCollection> rules, Node subTreeRoot)
{
return ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0);
}
#endregion
}
#endregion
#region SubTreeId
internal class SubTreeId
{
#region private state
public Node m_subTreeRoot;
private int m_hashCode;
private Node m_parent;
private int m_parentHashCode;
private int m_childIndex;
#endregion
#region constructors
internal SubTreeId(RuleProcessingContext context, Node node, Node parent, int childIndex)
{
m_subTreeRoot = node;
m_parent = parent;
m_childIndex = childIndex;
m_hashCode = context.GetHashCode(node);
m_parentHashCode = parent == null ? 0 : context.GetHashCode(parent);
}
#endregion
#region public surface
public override int GetHashCode()
{
return m_hashCode;
}
public override bool Equals(object obj)
{
SubTreeId other = obj as SubTreeId;
return ((other != null) && (m_hashCode == other.m_hashCode) &&
((other.m_subTreeRoot == this.m_subTreeRoot) ||
((other.m_parent == this.m_parent) && (other.m_childIndex == this.m_childIndex))));
}
#endregion
}
#endregion
#region RuleProcessingContext
///
/// Delegate that describes the processing
///
/// RuleProcessing context
/// Node to process
internal delegate void OpDelegate(RuleProcessingContext context, Node node);
///
/// A RuleProcessingContext encapsulates information needed by various rules to process
/// the query tree.
///
internal abstract class RuleProcessingContext
{
#region public surface
internal Command Command
{
get { return m_command; }
}
///
/// Callback function to be applied to a node before any rules are applied
///
/// the node
internal virtual void PreProcess(Node node)
{
}
///
/// Callback function to be applied to the subtree rooted at the given
/// node before any rules are applied
///
/// the node that is the root of the subtree
internal virtual void PreProcessSubTree(Node node)
{
}
///
/// Callback function to be applied on a node after a rule has been applied
/// that has modified the node
///
/// current node
/// the rule that modified the node
internal virtual void PostProcess(Node node, Rule rule)
{
}
///
/// Callback function to be applied to the subtree rooted at the given
/// node after any rules are applied
///
/// the node that is the root of the subtree
internal virtual void PostProcessSubTree(Node node)
{
}
///
/// Get the hashcode for this node - to ensure that we don't loop forever
///
/// current node
/// int hashcode
internal virtual int GetHashCode(Node node)
{
return node.GetHashCode();
}
#endregion
#region constructors
internal RuleProcessingContext(Command command)
{
m_command = command;
}
#endregion
#region private state
private Command m_command;
#endregion
}
#endregion
}