mirror of https://github.com/icsharpcode/ILSpy.git
8 changed files with 506 additions and 382 deletions
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public abstract partial class Node |
||||
{ |
||||
public void Optimize() |
||||
{ |
||||
OptimizeLoops(); |
||||
} |
||||
|
||||
public void OptimizeLoops() |
||||
{ |
||||
Reset: |
||||
foreach(Node child in this.Childs) { |
||||
if (child.Predecessors.Count == 1) { |
||||
if (Options.ReduceGraph <= 0) return; |
||||
Node predecessor = child.Predecessors[0]; |
||||
Node mergedNode; |
||||
if (child.Successors.Contains(predecessor)) { |
||||
mergedNode = MergeChilds<Loop>(predecessor, child); |
||||
} else { |
||||
mergedNode = MergeChilds<AcyclicGraph>(predecessor, child); |
||||
} |
||||
mergedNode.FalttenAcyclicChilds(); |
||||
goto Reset; |
||||
} |
||||
} |
||||
// If the result is single acyclic node, eliminate it
|
||||
if (this.Childs.Count == 1 && this.HeadChild is AcyclicGraph) { |
||||
if (Options.ReduceGraph-- <= 0) return; |
||||
Node headChild = this.HeadChild; |
||||
this.Childs.Remove(this.HeadChild); |
||||
headChild.Childs.MoveTo(this); |
||||
} |
||||
} |
||||
|
||||
NodeCollection GetReachableNodes() |
||||
{ |
||||
NodeCollection accumulator = new NodeCollection(); |
||||
AddReachableNode(accumulator, this); |
||||
return accumulator; |
||||
} |
||||
|
||||
static void AddReachableNode(NodeCollection accumulator, Node node) |
||||
{ |
||||
if (!accumulator.Contains(node)) { |
||||
accumulator.Add(node); |
||||
foreach(Node successor in node.Successors) { |
||||
AddReachableNode(accumulator, successor); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void OptimizeIf() |
||||
{ |
||||
Node conditionNode = this.HeadChild; |
||||
// Find conditionNode (the start)
|
||||
while(true) { |
||||
if (conditionNode is BasicBlock && conditionNode.Successors.Count == 2) { |
||||
break; // Found
|
||||
} else if (conditionNode.Successors.Count == 1) { |
||||
conditionNode = conditionNode.Successors[0]; |
||||
continue; // Next
|
||||
} else { |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,227 @@
@@ -0,0 +1,227 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public abstract partial class Node |
||||
{ |
||||
public static int NextNodeID = 1; |
||||
|
||||
int id; |
||||
Node parent; |
||||
NodeCollection childs = new NodeCollection(); |
||||
|
||||
// Structural and linking cache
|
||||
NodeCollection basicBlocks_cache = null; |
||||
NodeCollection predecessors_cache = null; |
||||
NodeCollection successors_cache = null; |
||||
|
||||
public int ID { |
||||
get { return id; } |
||||
} |
||||
|
||||
public Node Parent { |
||||
get { return parent; } |
||||
} |
||||
|
||||
public Node HeadChild { |
||||
get { |
||||
if (this.Childs.Count > 0) { |
||||
return this.Childs[0]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public NodeCollection Childs { |
||||
get { |
||||
return childs; |
||||
} |
||||
} |
||||
|
||||
/// <summary> All basic blocks within the scope of this node (inclusive) </summary>
|
||||
public NodeCollection BasicBlocks { |
||||
get { |
||||
if (basicBlocks_cache == null) { |
||||
NodeCollection basicBlocks = new NodeCollection(); |
||||
|
||||
if (this is BasicBlock) { |
||||
basicBlocks.Add(this); |
||||
} |
||||
foreach(Node child in this.Childs) { |
||||
basicBlocks.AddRange(child.BasicBlocks); |
||||
} |
||||
|
||||
basicBlocks_cache = basicBlocks; |
||||
} |
||||
return basicBlocks_cache; |
||||
} |
||||
} |
||||
|
||||
NodeCollection FloatUpToNeighbours(IEnumerable<BasicBlock> basickBlocks) |
||||
{ |
||||
NodeCollection neighbours = new NodeCollection(); |
||||
if (this.Parent != null) { |
||||
foreach(BasicBlock basicBlock in basickBlocks) { |
||||
// Find neighbour coresponding to the target
|
||||
Node targetNode = basicBlock; |
||||
while(targetNode != null && targetNode.Parent != this.Parent) { |
||||
targetNode = targetNode.Parent; |
||||
} |
||||
// The target is outside the scope of the parent node
|
||||
if (targetNode == null) continue; |
||||
// This child is a loop
|
||||
if (targetNode == this) continue; |
||||
// We found a target in our scope
|
||||
neighbours.Add(targetNode); |
||||
} |
||||
} |
||||
return neighbours; |
||||
} |
||||
|
||||
public NodeCollection Predecessors { |
||||
get { |
||||
if (predecessors_cache == null) { |
||||
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>(); |
||||
foreach(BasicBlock basicBlock in this.BasicBlocks) { |
||||
foreach(BasicBlock basicBlockPredecessor in basicBlock.BasicBlockPredecessors) { |
||||
basicBlockPredecessors.Add(basicBlockPredecessor); |
||||
} |
||||
} |
||||
|
||||
predecessors_cache = FloatUpToNeighbours(basicBlockPredecessors); |
||||
} |
||||
return predecessors_cache; |
||||
} |
||||
} |
||||
|
||||
public NodeCollection Successors { |
||||
get { |
||||
if (successors_cache == null) { |
||||
List<BasicBlock> basicBlockSuccessors = new List<BasicBlock>(); |
||||
foreach(BasicBlock basicBlock in this.BasicBlocks) { |
||||
foreach(BasicBlock basicBlockSuccessor in basicBlock.BasicBlockSuccessors) { |
||||
basicBlockSuccessors.Add(basicBlockSuccessor); |
||||
} |
||||
} |
||||
|
||||
successors_cache = FloatUpToNeighbours(basicBlockSuccessors); |
||||
} |
||||
return successors_cache; |
||||
} |
||||
} |
||||
|
||||
int Index { |
||||
get { |
||||
if (this.Parent == null) throw new Exception("Does not have a parent"); |
||||
return this.Parent.Childs.IndexOf(this); |
||||
} |
||||
} |
||||
|
||||
public Node NextNode { |
||||
get { |
||||
int index = this.Index + 1; |
||||
if (0 <= index && index < this.Parent.Childs.Count) { |
||||
return this.Parent.Childs[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public string Label { |
||||
get { |
||||
return this.GetType().Name + "_" + ID; |
||||
} |
||||
} |
||||
|
||||
public string Description { |
||||
get { |
||||
return ToString(); |
||||
} |
||||
} |
||||
|
||||
protected Node() |
||||
{ |
||||
this.id = NextNodeID++; |
||||
this.Childs.Added += delegate(object sender, NodeEventArgs e) { |
||||
if (e.Node.Parent != null) { |
||||
throw new Exception("Node is already assigned to other parent"); |
||||
} |
||||
e.Node.parent = this; |
||||
NotifyChildsChanged(); |
||||
}; |
||||
this.Childs.Removed += delegate(object sender, NodeEventArgs e) { |
||||
e.Node.parent = null; |
||||
NotifyChildsChanged(); |
||||
}; |
||||
} |
||||
|
||||
void NotifyChildsChanged() |
||||
{ |
||||
this.basicBlocks_cache = null; |
||||
foreach(Node child in this.Childs) { |
||||
child.predecessors_cache = null; |
||||
child.successors_cache = null; |
||||
} |
||||
if (this.Parent != null) { |
||||
this.Parent.NotifyChildsChanged(); |
||||
} |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder(); |
||||
sb.Append(this.GetType().Name); |
||||
sb.Append(" "); |
||||
sb.Append(ID); |
||||
sb.Append(" "); |
||||
|
||||
sb.Append("("); |
||||
|
||||
if (this.Predecessors.Count > 0) { |
||||
sb.Append("Predecessors:"); |
||||
bool isFirst = true; |
||||
foreach(Node predecessor in this.Predecessors) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
sb.Append(","); |
||||
} |
||||
sb.Append(predecessor.ID); |
||||
} |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (this.Successors.Count > 0) { |
||||
sb.Append("Successors:"); |
||||
bool isFirst = true; |
||||
foreach(Node successor in this.Successors) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
sb.Append(","); |
||||
} |
||||
sb.Append(successor.ID); |
||||
} |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (this.Parent != null) { |
||||
sb.Append("Parent:"); |
||||
sb.Append(this.Parent.ID); |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (sb[sb.Length - 1] == '(') { |
||||
sb.Length -= 1; |
||||
} else if (sb[sb.Length - 1] == ' ') { |
||||
sb.Length -= 1; |
||||
sb.Append(")"); |
||||
} |
||||
return sb.ToString(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public abstract partial class Node |
||||
{ |
||||
public void Remove() |
||||
{ |
||||
if (this.Parent != null) { |
||||
this.Parent.Childs.Remove(this); |
||||
} |
||||
} |
||||
|
||||
public void MoveTo(Node newNode) |
||||
{ |
||||
MoveTo(newNode, newNode.Childs.Count); |
||||
} |
||||
|
||||
public void MoveTo(Node newNode, int index) |
||||
{ |
||||
this.Remove(); |
||||
newNode.Childs.Insert(index, this); |
||||
} |
||||
|
||||
Node MergeChilds<T>(params Node[] nodes) where T: Node, new() |
||||
{ |
||||
foreach(Node node in nodes) { |
||||
if (node == null) throw new ArgumentNullException("nodes"); |
||||
if (node.Parent != this) throw new ArgumentException("The node is not my child"); |
||||
} |
||||
if (nodes.Length == 0) throw new ArgumentException("At least one node must be specified"); |
||||
|
||||
Node mergedNode = new T(); |
||||
|
||||
// Add the merged node
|
||||
if (Options.ReduceGraph-- <= 0) return mergedNode; |
||||
int headIndex = this.Childs.IndexOf(nodes[0]); |
||||
this.Childs.Insert(headIndex, mergedNode); |
||||
|
||||
foreach(Node node in nodes) { |
||||
if (Options.ReduceGraph-- <= 0) return mergedNode; |
||||
node.MoveTo(mergedNode); |
||||
} |
||||
|
||||
return mergedNode; |
||||
} |
||||
|
||||
public void FalttenAcyclicChilds() |
||||
{ |
||||
Reset: |
||||
foreach(Node child in this.Childs) { |
||||
if (child is AcyclicGraph) { |
||||
if (Options.ReduceGraph-- <= 0) return; |
||||
child.Childs.MoveTo(this, child.Index); |
||||
child.Remove(); |
||||
goto Reset; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,366 +0,0 @@
@@ -1,366 +0,0 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public class NodeCollection: System.Collections.ObjectModel.Collection<Node> |
||||
{ |
||||
public void AddRange(IEnumerable<Node> items) |
||||
{ |
||||
foreach(Node item in items) { |
||||
this.Add(item); |
||||
} |
||||
} |
||||
|
||||
public void RemoveRange(IEnumerable<Node> items) |
||||
{ |
||||
foreach(Node item in items) { |
||||
this.Remove(item); |
||||
} |
||||
} |
||||
|
||||
public static NodeCollection Intersection(NodeCollection collectionA, NodeCollection collectionB) |
||||
{ |
||||
NodeCollection result = new NodeCollection(); |
||||
foreach(Node a in collectionA) { |
||||
if (collectionB.Contains(a)) { |
||||
result.Add(a); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
protected override void InsertItem(int index, Node item) |
||||
{ |
||||
if (!this.Contains(item)) { |
||||
base.InsertItem(index, item); |
||||
} |
||||
} |
||||
|
||||
public bool ContainsRecursive(Node node) |
||||
{ |
||||
if (this.Contains(node)) { |
||||
return true; |
||||
} |
||||
foreach(Node item in this.Items) { |
||||
if (item.Childs.ContainsRecursive(node)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public Node FindContainer(Node node) |
||||
{ |
||||
foreach(Node item in this.Items) { |
||||
if (item == node || item.Childs.ContainsRecursive(node)) { |
||||
return item; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public abstract class Node |
||||
{ |
||||
public static int NextNodeID = 1; |
||||
|
||||
int id; |
||||
Node parent; |
||||
Node headChild; |
||||
NodeCollection childs = new NodeCollection(); |
||||
NodeCollection predecessors = new NodeCollection(); |
||||
NodeCollection successors = new NodeCollection(); |
||||
|
||||
public int ID { |
||||
get { return id; } |
||||
} |
||||
|
||||
public Node Parent { |
||||
get { return parent; } |
||||
set { parent = value; } |
||||
} |
||||
|
||||
public Node HeadChild { |
||||
get { return headChild; } |
||||
protected set { headChild = value; } |
||||
} |
||||
|
||||
public NodeCollection Childs { |
||||
get { return childs; } |
||||
} |
||||
|
||||
public NodeCollection Predecessors { |
||||
get { return predecessors; } |
||||
} |
||||
|
||||
public NodeCollection Successors { |
||||
get { return successors; } |
||||
} |
||||
|
||||
public Node NextNode { |
||||
get { |
||||
if (this.Parent == null) throw new Exception("Does not have a parent"); |
||||
int myIndex = this.Parent.Childs.IndexOf(this); |
||||
int index = myIndex + 1; |
||||
if (0 <= index && index < this.Parent.Childs.Count) { |
||||
return this.Parent.Childs[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public string Label { |
||||
get { |
||||
return this.GetType().Name + "_" + ID; |
||||
} |
||||
} |
||||
|
||||
public Node(Node parent) |
||||
{ |
||||
this.parent = parent; |
||||
this.id = NextNodeID++; |
||||
} |
||||
|
||||
NodeCollection GetReachableNodes() |
||||
{ |
||||
NodeCollection accumulator = new NodeCollection(); |
||||
AddReachableNode(accumulator, this); |
||||
return accumulator; |
||||
} |
||||
|
||||
static void AddReachableNode(NodeCollection accumulator, Node node) |
||||
{ |
||||
if (!accumulator.Contains(node)) { |
||||
accumulator.Add(node); |
||||
foreach(Node successor in node.Successors) { |
||||
AddReachableNode(successor, accumulator); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void GetBasicBlockSuccessors(NodeCollection accumulator) |
||||
{ |
||||
BasicBlock me = this as BasicBlock; |
||||
if (me != null) { |
||||
if (me.FallThroughBasicBlock != null) { |
||||
accumulator.Add(me.FallThroughBasicBlock); |
||||
} |
||||
if (me.BranchBasicBlock != null) { |
||||
accumulator.Add(me.BranchBasicBlock); |
||||
} |
||||
} else { |
||||
foreach(Node child in this.Childs) { |
||||
child.GetBasicBlockSuccessors(accumulator); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void RebuildNodeLinks() |
||||
{ |
||||
foreach(Node child in this.Childs) { |
||||
NodeCollection successorBasicBlocks = new NodeCollection(); |
||||
child.GetBasicBlockSuccessors(successorBasicBlocks); |
||||
NodeCollection successorNodes = new NodeCollection(); |
||||
foreach(Node successorBasicBlock in successorBasicBlocks) { |
||||
Node container = this.Childs.FindContainer(successorBasicBlock); |
||||
if (container != null) { |
||||
successorNodes.Add(container); |
||||
} |
||||
} |
||||
// Remove self link
|
||||
if (successorNodes.Contains(child)) { |
||||
successorNodes.Remove(child); |
||||
} |
||||
foreach(Node target in successorNodes) { |
||||
child.Successors.Add(target); |
||||
target.Predecessors.Add(child); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void Optimize() |
||||
{ |
||||
for(int i = 0; i < this.Childs.Count;) { |
||||
Node child = childs[i]; |
||||
if (child.Predecessors.Count == 1) { |
||||
if (Options.ReduceGraph-- <= 0) return; |
||||
MergeChilds(child.Predecessors[0], child); |
||||
i = 0; // Restart
|
||||
} else { |
||||
i++; // Next
|
||||
} |
||||
} |
||||
// If it result is single acyclic node, eliminate it
|
||||
if (this.Childs.Count == 1 && this.Childs[0] is AcyclicGraph) { |
||||
this.headChild = this.Childs[0].HeadChild; |
||||
this.childs = this.Childs[0].Childs; |
||||
this.UpdateParentOfChilds(); |
||||
} |
||||
OptimizeIf(); |
||||
} |
||||
|
||||
public void OptimizeIf() |
||||
{ |
||||
Node conditionNode = this.HeadChild; |
||||
// Find conditionNode (the start)
|
||||
while(true) { |
||||
if (conditionNode is BasicBlock && conditionNode.Successors.Count == 2) { |
||||
break; // Found
|
||||
} else if (conditionNode.Successors == 1) { |
||||
conditionNode = conditionNode.Successors[0]; |
||||
continue; // Next
|
||||
} else { |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void UpdateParentOfChilds() |
||||
{ |
||||
foreach(Node child in this.Childs) { |
||||
child.Parent = this; |
||||
} |
||||
} |
||||
|
||||
static void MergeChilds(Node head, Node tail) |
||||
{ |
||||
if (head == null) throw new ArgumentNullException("head"); |
||||
if (tail == null) throw new ArgumentNullException("tail"); |
||||
if (head.Parent != tail.Parent) throw new ArgumentException("different parents"); |
||||
|
||||
Node container = head.Parent; |
||||
Node mergedNode; |
||||
// Get type
|
||||
if (tail.Successors.Contains(head)) { |
||||
mergedNode = new Loop(container); |
||||
} else { |
||||
mergedNode = new AcyclicGraph(container); |
||||
} |
||||
|
||||
// Add head
|
||||
if (head is BasicBlock) { |
||||
mergedNode.HeadChild = head; |
||||
mergedNode.Childs.Add(head); |
||||
} else if (head is AcyclicGraph) { |
||||
mergedNode.HeadChild = ((AcyclicGraph)head).HeadChild; |
||||
mergedNode.Childs.AddRange(((AcyclicGraph)head).Childs); |
||||
} else if (head is Loop) { |
||||
mergedNode.HeadChild = head; |
||||
mergedNode.Childs.Add(head); |
||||
} else { |
||||
throw new Exception("Invalid head type"); |
||||
} |
||||
|
||||
// Add tail
|
||||
if (tail is BasicBlock) { |
||||
mergedNode.Childs.Add(tail); |
||||
} else if (tail is AcyclicGraph) { |
||||
mergedNode.Childs.AddRange(((AcyclicGraph)tail).Childs); |
||||
} else if (tail is Loop) { |
||||
mergedNode.Childs.Add(tail); |
||||
} else { |
||||
throw new Exception("Invalid tail type"); |
||||
} |
||||
|
||||
mergedNode.UpdateParentOfChilds(); |
||||
|
||||
// Remove links between the head and tail
|
||||
if (head.Successors.Contains(tail)) { |
||||
head.Successors.Remove(tail); |
||||
tail.Predecessors.Remove(head); |
||||
} |
||||
if (tail.Successors.Contains(head)) { |
||||
tail.Successors.Remove(head); |
||||
head.Predecessors.Remove(tail); |
||||
} |
||||
|
||||
Relink(head, mergedNode); |
||||
Relink(tail, mergedNode); |
||||
|
||||
mergedNode.RebuildNodeLinks(); |
||||
|
||||
// Remove the old nodes and add the merged node - replace head with the merged node
|
||||
container.Childs.Remove(tail); |
||||
int headIndex = container.Childs.IndexOf(head); |
||||
container.Childs.Remove(head); |
||||
container.Childs.Insert(headIndex, mergedNode); |
||||
} |
||||
|
||||
static void Relink(Node node, Node target) |
||||
{ |
||||
// Relink all neighbours to the target node
|
||||
foreach(Node predecessor in node.Predecessors) { |
||||
predecessor.Successors.Remove(node); |
||||
predecessor.Successors.Add(target); |
||||
} |
||||
foreach(Node successor in node.Successors) { |
||||
successor.Predecessors.Remove(node); |
||||
successor.Predecessors.Add(target); |
||||
} |
||||
|
||||
// Move our pointers to the target node
|
||||
target.Predecessors.AddRange(node.Predecessors); |
||||
target.Successors.AddRange(node.Successors); |
||||
node.Predecessors.Clear(); |
||||
node.Successors.Clear(); |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder(); |
||||
sb.Append(this.GetType().Name); |
||||
sb.Append(" "); |
||||
sb.Append(ID); |
||||
sb.Append(" "); |
||||
|
||||
sb.Append("("); |
||||
|
||||
if (this.Predecessors.Count > 0) { |
||||
sb.Append("Predecessors:"); |
||||
bool isFirst = true; |
||||
foreach(Node predecessor in this.Predecessors) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
sb.Append(","); |
||||
} |
||||
sb.Append(predecessor.ID); |
||||
} |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (this.Successors.Count > 0) { |
||||
sb.Append("Successors:"); |
||||
bool isFirst = true; |
||||
foreach(Node successor in this.Successors) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
sb.Append(","); |
||||
} |
||||
sb.Append(successor.ID); |
||||
} |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (this.Parent != null) { |
||||
sb.Append("Parent:"); |
||||
sb.Append(this.Parent.ID); |
||||
} |
||||
sb.Append(" "); |
||||
|
||||
if (this.HeadChild != null) { |
||||
sb.Append("Head:"); |
||||
sb.Append(this.HeadChild.ID); |
||||
} |
||||
sb.Append(" "); |
||||
|
||||
sb.Length = sb.Length - 1; |
||||
sb.Append(")"); |
||||
return sb.ToString(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public class NodeEventArgs: EventArgs |
||||
{ |
||||
Node node; |
||||
|
||||
public Node Node { |
||||
get { return node; } |
||||
} |
||||
|
||||
public NodeEventArgs(Node node) |
||||
{ |
||||
this.node = node; |
||||
} |
||||
} |
||||
|
||||
public class NodeCollection: System.Collections.ObjectModel.Collection<Node> |
||||
{ |
||||
public event EventHandler<NodeEventArgs> Added; |
||||
public event EventHandler<NodeEventArgs> Removed; |
||||
|
||||
protected virtual void OnAdded(Node node) |
||||
{ |
||||
if (Added != null) { |
||||
Added(this, new NodeEventArgs(node)); |
||||
} |
||||
} |
||||
|
||||
protected virtual void OnRemoved(Node node) |
||||
{ |
||||
if (Removed != null) { |
||||
Removed(this, new NodeEventArgs(node)); |
||||
} |
||||
} |
||||
|
||||
protected override void ClearItems() |
||||
{ |
||||
while(this.Count > 0) { |
||||
this.RemoveAt(this.Count - 1); |
||||
} |
||||
} |
||||
|
||||
protected override void InsertItem(int index, Node item) |
||||
{ |
||||
if (!this.Contains(item)) { |
||||
base.InsertItem(index, item); |
||||
} |
||||
OnAdded(item); |
||||
} |
||||
|
||||
protected override void RemoveItem(int index) |
||||
{ |
||||
Node node = this[index]; |
||||
base.RemoveItem(index); |
||||
OnRemoved(node); |
||||
} |
||||
|
||||
protected override void SetItem(int index, Node item) |
||||
{ |
||||
this.RemoveAt(index); |
||||
this.Insert(index, item); |
||||
} |
||||
|
||||
|
||||
public void AddRange(IEnumerable<Node> items) |
||||
{ |
||||
foreach(Node item in items) { |
||||
this.Add(item); |
||||
} |
||||
} |
||||
|
||||
public void RemoveRange(IEnumerable<Node> items) |
||||
{ |
||||
foreach(Node item in items) { |
||||
this.Remove(item); |
||||
} |
||||
} |
||||
|
||||
public void MoveTo(Node newNode) |
||||
{ |
||||
while(this.Count > 0) { |
||||
this[0].MoveTo(newNode); |
||||
} |
||||
} |
||||
|
||||
public void MoveTo(Node newNode, int index) |
||||
{ |
||||
while(this.Count > 0) { |
||||
this[0].MoveTo(newNode, index); |
||||
index++; |
||||
} |
||||
} |
||||
|
||||
public static NodeCollection Intersection(NodeCollection collectionA, NodeCollection collectionB) |
||||
{ |
||||
NodeCollection result = new NodeCollection(); |
||||
foreach(Node a in collectionA) { |
||||
if (collectionB.Contains(a)) { |
||||
result.Add(a); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("{0} Count = {1}", typeof(NodeCollection).Name, this.Count); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue