Browse Source

Move Language to FilterSettings.

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
c4c3356f2b
  1. 2
      ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs
  2. 2
      ILSpy/CSharpLanguage.cs
  3. 12
      ILSpy/FilterSettings.cs
  4. 2
      ILSpy/ILLanguage.cs
  5. 5
      ILSpy/ILSpy.csproj
  6. 11
      ILSpy/Language.cs
  7. 4
      ILSpy/MainWindow.xaml
  8. 74
      ILSpy/MainWindow.xaml.cs
  9. 3
      ILSpy/Mono.Cecil.Rocks/MethodBodyRocks.cs
  10. 6
      ILSpy/TextView/DecompilerTextView.cs
  11. 2
      ILSpy/TextView/DecompilerTextView.xaml
  12. 2
      ILSpy/TreeNodes/EventTreeNode.cs
  13. 2
      ILSpy/TreeNodes/FieldTreeNode.cs
  14. 71
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  15. 4
      ILSpy/TreeNodes/MethodTreeNode.cs
  16. 2
      ILSpy/TreeNodes/PropertyTreeNode.cs
  17. 112
      ILSpy/TreeTraversal.cs

2
ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs

@ -22,7 +22,7 @@ using System.Linq;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler namespace ICSharpCode.Decompiler.FlowAnalysis
{ {
/// <summary> /// <summary>
/// Additional info about opcodes. /// Additional info about opcodes.

2
ILSpy/Decompiler/CSharpLanguage.cs → ILSpy/CSharpLanguage.cs

@ -20,7 +20,7 @@ using System;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.ILSpy.Decompiler namespace ICSharpCode.ILSpy
{ {
/// <summary> /// <summary>
/// Decompiler logic for C#. /// Decompiler logic for C#.

12
ILSpy/FilterSettings.cs

@ -62,6 +62,18 @@ namespace ICSharpCode.ILSpy
} }
} }
Language language = Languages.AllLanguages[0];
public Language Language {
get { return language; }
set {
if (language != value) {
language = value;
OnPropertyChanged("Language");
}
}
}
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) protected virtual void OnPropertyChanged(string propertyName)

2
ILSpy/Disassembler/ILLanguage.cs → ILSpy/ILLanguage.cs

@ -24,7 +24,7 @@ using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.ILSpy.Disassembler namespace ICSharpCode.ILSpy
{ {
public class ILLanguage : Language public class ILLanguage : Language
{ {

5
ILSpy/ILSpy.csproj

@ -81,14 +81,14 @@
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="AssemblyList.cs" /> <Compile Include="AssemblyList.cs" />
<Compile Include="CSharpLanguage.cs" />
<Compile Include="CueBannerService.cs" /> <Compile Include="CueBannerService.cs" />
<Compile Include="DecompilationOptions.cs" /> <Compile Include="DecompilationOptions.cs" />
<Compile Include="Decompiler\CSharpLanguage.cs" />
<Compile Include="Disassembler\ILLanguage.cs" />
<Compile Include="ExtensionMethods.cs" /> <Compile Include="ExtensionMethods.cs" />
<Compile Include="FilterSettings.cs" /> <Compile Include="FilterSettings.cs" />
<Compile Include="Fusion.cs" /> <Compile Include="Fusion.cs" />
<Compile Include="GacInterop.cs" /> <Compile Include="GacInterop.cs" />
<Compile Include="ILLanguage.cs" />
<Compile Include="Language.cs" /> <Compile Include="Language.cs" />
<Compile Include="Images\Images.cs" /> <Compile Include="Images\Images.cs" />
<Compile Include="Mono.Cecil.Rocks\MethodBodyRocks.cs" /> <Compile Include="Mono.Cecil.Rocks\MethodBodyRocks.cs" />
@ -122,7 +122,6 @@
<Compile Include="TreeNodes\ReferenceFolderTreeNode.cs" /> <Compile Include="TreeNodes\ReferenceFolderTreeNode.cs" />
<Compile Include="TreeNodes\ResourceListTreeNode.cs" /> <Compile Include="TreeNodes\ResourceListTreeNode.cs" />
<Compile Include="TreeNodes\TypeTreeNode.cs" /> <Compile Include="TreeNodes\TypeTreeNode.cs" />
<Compile Include="TreeTraversal.cs" />
<EmbeddedResource Include="TextView\ILAsm-Mode.xshd" /> <EmbeddedResource Include="TextView\ILAsm-Mode.xshd" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

11
ILSpy/Language.cs

@ -28,8 +28,6 @@ namespace ICSharpCode.ILSpy
/// </summary> /// </summary>
public abstract class Language public abstract class Language
{ {
public static Language Current = new Decompiler.CSharpLanguage();
public abstract string Name { get; } public abstract string Name { get; }
public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting { public virtual ICSharpCode.AvalonEdit.Highlighting.IHighlightingDefinition SyntaxHighlighting {
@ -74,4 +72,13 @@ namespace ICSharpCode.ILSpy
return Name; return Name;
} }
} }
public static class Languages
{
public static readonly Language[] AllLanguages = {
new CSharpLanguage(),
new ILLanguage(false),
new ILLanguage(true)
};
}
} }

4
ILSpy/MainWindow.xaml

@ -64,7 +64,9 @@
<Image Width="16" Height="16" Source="Images/PrivateInternal.png" /> <Image Width="16" Height="16" Source="Images/PrivateInternal.png" />
</CheckBox> </CheckBox>
<Separator /> <Separator />
<ComboBox Name="languageComboBox" DisplayMemberPath="Name" Width="100" SelectionChanged="LanguageComboBox_SelectionChanged" /> <ComboBox Name="languageComboBox" DisplayMemberPath="Name" Width="100"
ItemsSource="{x:Static local:Languages.AllLanguages}"
SelectedItem="{Binding Language}" />
</ToolBar> </ToolBar>
<!-- Main grid separating left pane (treeView) from main pane (textEditor) --> <!-- Main grid separating left pane (treeView) from main pane (textEditor) -->
<Grid> <Grid>

74
ILSpy/MainWindow.xaml.cs

@ -17,24 +17,17 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.ILSpy.Disassembler;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
using Microsoft.Win32; using Microsoft.Win32;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks; using Mono.Cecil.Rocks;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
@ -45,6 +38,7 @@ namespace ICSharpCode.ILSpy
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
AssemblyList assemblyList = new AssemblyList(); AssemblyList assemblyList = new AssemblyList();
AssemblyListTreeNode assemblyListTreeNode;
FilterSettings filterSettings = new FilterSettings(); FilterSettings filterSettings = new FilterSettings();
static readonly System.Reflection.Assembly[] initialAssemblies = { static readonly System.Reflection.Assembly[] initialAssemblies = {
@ -68,19 +62,9 @@ namespace ICSharpCode.ILSpy
InitializeComponent(); InitializeComponent();
decompilerTextView.mainWindow = this; decompilerTextView.mainWindow = this;
languageComboBox.Items.Add(new Decompiler.CSharpLanguage()); assemblyListTreeNode = new AssemblyListTreeNode(assemblyList);
languageComboBox.Items.Add(new Disassembler.ILLanguage(false));
languageComboBox.Items.Add(new Disassembler.ILLanguage(true));
languageComboBox.SelectedItem = languageComboBox.Items[0];
AssemblyListTreeNode assemblyListTreeNode = new AssemblyListTreeNode(assemblyList);
assemblyListTreeNode.FilterSettings = filterSettings.Clone();
filterSettings.PropertyChanged += delegate {
// filterSettings is mutable; but the ILSpyTreeNode filtering assumes that filter settings are immutable.
// Thus, the main window will use one mutable instance (for data-binding), and assign a new clone to the ILSpyTreeNodes whenever the main
// mutable instance changes.
assemblyListTreeNode.FilterSettings = filterSettings.Clone(); assemblyListTreeNode.FilterSettings = filterSettings.Clone();
}; filterSettings.PropertyChanged += new PropertyChangedEventHandler(filterSettings_PropertyChanged);
treeView.Root = assemblyListTreeNode; treeView.Root = assemblyListTreeNode;
assemblyListTreeNode.Select = SelectNode; assemblyListTreeNode.Select = SelectNode;
@ -92,22 +76,21 @@ namespace ICSharpCode.ILSpy
} }
#if DEBUG #if DEBUG
toolBar.Items.Add(new Separator()); AddDebugItemsToToolbar();
Button cfg = new Button() { Content = "CFG" };
cfg.Click += new RoutedEventHandler(cfg_Click);
toolBar.Items.Add(cfg);
Button ssa = new Button() { Content = "SSA" };
ssa.Click += new RoutedEventHandler(ssa_Click);
toolBar.Items.Add(ssa);
Button varGraph = new Button() { Content = "Var" };
varGraph.Click += new RoutedEventHandler(varGraph_Click);
toolBar.Items.Add(varGraph);
#endif #endif
} }
void filterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// filterSettings is mutable; but the ILSpyTreeNode filtering assumes that filter settings are immutable.
// Thus, the main window will use one mutable instance (for data-binding), and assign a new clone to the ILSpyTreeNodes whenever the main
// mutable instance changes.
assemblyListTreeNode.FilterSettings = filterSettings.Clone();
if (e.PropertyName == "Language") {
TreeView_SelectionChanged(null, null);
}
}
internal AssemblyList AssemblyList { internal AssemblyList AssemblyList {
get { return assemblyList; } get { return assemblyList; }
} }
@ -122,6 +105,23 @@ namespace ICSharpCode.ILSpy
#region Debugging CFG #region Debugging CFG
#if DEBUG #if DEBUG
void AddDebugItemsToToolbar()
{
toolBar.Items.Add(new Separator());
Button cfg = new Button() { Content = "CFG" };
cfg.Click += new RoutedEventHandler(cfg_Click);
toolBar.Items.Add(cfg);
Button ssa = new Button() { Content = "SSA" };
ssa.Click += new RoutedEventHandler(ssa_Click);
toolBar.Items.Add(ssa);
Button varGraph = new Button() { Content = "Var" };
varGraph.Click += new RoutedEventHandler(varGraph_Click);
toolBar.Items.Add(varGraph);
}
void cfg_Click(object sender, RoutedEventArgs e) void cfg_Click(object sender, RoutedEventArgs e)
{ {
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode; MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
@ -213,13 +213,7 @@ namespace ICSharpCode.ILSpy
void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
decompilerTextView.Decompile(treeView.SelectedItems.OfType<ILSpyTreeNodeBase>()); decompilerTextView.Decompile(filterSettings.Language, treeView.SelectedItems.OfType<ILSpyTreeNodeBase>());
}
void LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ILSpy.Language.Current = (ILSpy.Language)languageComboBox.SelectedItem;
TreeView_SelectionChanged(null, null);
} }
} }
} }

3
ILSpy/Mono.Cecil.Rocks/MethodBodyRocks.cs

@ -32,9 +32,6 @@ using Mono.Cecil.Cil;
namespace Mono.Cecil.Rocks { namespace Mono.Cecil.Rocks {
#if INSIDE_ROCKS
public
#endif
static class MethodBodyRocks { static class MethodBodyRocks {
public static ParameterDefinition GetParameter (this MethodBody self, int index) public static ParameterDefinition GetParameter (this MethodBody self, int index)

6
ILSpy/TextView/DecompilerTextView.cs

@ -66,7 +66,7 @@ namespace ICSharpCode.ILSpy.TextView
foldingManager = FoldingManager.Install(textEditor.TextArea); foldingManager = FoldingManager.Install(textEditor.TextArea);
} }
public void Decompile(IEnumerable<ILSpyTreeNodeBase> treeNodes) public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes)
{ {
if (waitAdorner.Visibility != Visibility.Visible) { if (waitAdorner.Visibility != Visibility.Visible) {
waitAdorner.Visibility = Visibility.Visible; waitAdorner.Visibility = Visibility.Visible;
@ -82,7 +82,7 @@ namespace ICSharpCode.ILSpy.TextView
DecompilationOptions options = new DecompilationOptions(); DecompilationOptions options = new DecompilationOptions();
options.CancellationToken = myCancellationTokenSource.Token; options.CancellationToken = myCancellationTokenSource.Token;
var task = RunDecompiler(ILSpy.Language.Current, treeNodes.ToArray(), options); var task = RunDecompiler(language, treeNodes.ToArray(), options);
Action continuation = delegate { Action continuation = delegate {
try { try {
if (currentCancellationTokenSource == myCancellationTokenSource) { if (currentCancellationTokenSource == myCancellationTokenSource) {
@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy.TextView
SmartTextOutput textOutput = task.Result; SmartTextOutput textOutput = task.Result;
referenceElementGenerator.References = textOutput.References; referenceElementGenerator.References = textOutput.References;
definitionLookup = textOutput.DefinitionLookup; definitionLookup = textOutput.DefinitionLookup;
textEditor.SyntaxHighlighting = ILSpy.Language.Current.SyntaxHighlighting; textEditor.SyntaxHighlighting = language.SyntaxHighlighting;
textEditor.Text = textOutput.ToString(); textEditor.Text = textOutput.ToString();
foldingManager.UpdateFoldings(textOutput.Foldings.OrderBy(f => f.StartOffset), -1); foldingManager.UpdateFoldings(textOutput.Foldings.OrderBy(f => f.StartOffset), -1);
} catch (AggregateException ex) { } catch (AggregateException ex) {

2
ILSpy/TextView/DecompilerTextView.xaml

@ -13,7 +13,7 @@
IsReadOnly="True" IsReadOnly="True"
Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.InfoTextBrushKey}}" /> Foreground="{DynamicResource {x:Static SystemColors.InfoTextBrushKey}}" />
<Border Name="waitAdorner" Background="#C0FFFFFF"> <Border Name="waitAdorner" Background="#C0FFFFFF" Visibility="Collapsed">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock FontSize="14pt">Decompiling...</TextBlock> <TextBlock FontSize="14pt">Decompiling...</TextBlock>
<ProgressBar IsIndeterminate="True" Height="16" Margin="0, 4" /> <ProgressBar IsIndeterminate="True" Height="16" Margin="0, 4" />

2
ILSpy/TreeNodes/EventTreeNode.cs

@ -42,7 +42,7 @@ namespace ICSharpCode.ILSpy
} }
public override object Text { public override object Text {
get { return ev.Name + " : " + Language.Current.TypeToString(ev.EventType); } get { return ev.Name + " : " + this.Language.TypeToString(ev.EventType); }
} }
public override object Icon { public override object Icon {

2
ILSpy/TreeNodes/FieldTreeNode.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy
} }
public override object Text { public override object Text {
get { return field.Name + " : " + Language.Current.TypeToString(field.FieldType); } get { return field.Name + " : " + this.Language.TypeToString(field.FieldType); }
} }
public override object Icon { public override object Icon {

71
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -40,6 +40,10 @@ namespace ICSharpCode.ILSpy
} }
} }
public Language Language {
get { return filterSettings.Language; }
}
public SharpTreeNodeCollection VisibleChildren { public SharpTreeNodeCollection VisibleChildren {
get { return base.Children; } get { return base.Children; }
} }
@ -93,16 +97,20 @@ namespace ICSharpCode.ILSpy
{ {
if (children == null) if (children == null)
throw new ArgumentNullException("children"); throw new ArgumentNullException("children");
this.Children = children; this.allChildren = children;
children.CollectionChanged += children_CollectionChanged; children.CollectionChanged += allChildren_CollectionChanged;
} }
void children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) void allChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
var visibleChildren = this.VisibleChildren;
switch (e.Action) { switch (e.Action) {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
if (e.NewItems.Count == 1 && e.NewStartingIndex == this.Children.Count - 1) { if (e.NewItems.Count == 1 && e.NewStartingIndex == allChildren.Count - 1) {
FilterChild((T)e.NewItems[0]); T newChild = (T)e.NewItems[0];
if (FilterChild(newChild))
visibleChildren.Add(newChild);
break; break;
} else { } else {
goto default; goto default;
@ -115,13 +123,16 @@ namespace ICSharpCode.ILSpy
void ResetChildren() void ResetChildren()
{ {
base.Children.Clear(); var visibleChildren = this.VisibleChildren;
foreach (T child in this.Children) {
FilterChild(child); visibleChildren.Clear();
foreach (T child in allChildren) {
if (FilterChild(child))
visibleChildren.Add(child);
} }
} }
void FilterChild(T child) bool FilterChild(T child)
{ {
FilterResult r; FilterResult r;
if (this.FilterSettings == null) if (this.FilterSettings == null)
@ -130,24 +141,18 @@ namespace ICSharpCode.ILSpy
r = child.Filter(this.FilterSettings); r = child.Filter(this.FilterSettings);
switch (r) { switch (r) {
case FilterResult.Hidden: case FilterResult.Hidden:
// don't add to base.Children return false;
break;
case FilterResult.Match: case FilterResult.Match:
child.FilterSettings = StripSearchTerm(this.FilterSettings); child.FilterSettings = StripSearchTerm(this.FilterSettings);
base.Children.Add(child); return true;
break;
case FilterResult.Recurse: case FilterResult.Recurse:
child.FilterSettings = this.FilterSettings; child.FilterSettings = this.FilterSettings;
child.EnsureLazyChildren(); child.EnsureLazyChildren();
if (child.VisibleChildren.Count > 0) return child.VisibleChildren.Count > 0;
base.Children.Add(child);
break;
case FilterResult.MatchAndRecurse: case FilterResult.MatchAndRecurse:
child.FilterSettings = StripSearchTerm(this.FilterSettings); child.FilterSettings = StripSearchTerm(this.FilterSettings);
child.EnsureLazyChildren(); child.EnsureLazyChildren();
if (child.VisibleChildren.Count > 0) return child.VisibleChildren.Count > 0;
base.Children.Add(child);
break;
default: default:
throw new InvalidEnumArgumentException(); throw new InvalidEnumArgumentException();
} }
@ -166,10 +171,34 @@ namespace ICSharpCode.ILSpy
protected override void OnFilterSettingsChanged() protected override void OnFilterSettingsChanged()
{ {
ResetChildren(); var visibleChildren = this.VisibleChildren;
var allChildren = this.Children;
int j = 0;
for (int i = 0; i < allChildren.Count; i++) {
T child = allChildren[i];
if (j < visibleChildren.Count && visibleChildren[j] == child) {
// it was visible before
if (FilterChild(child)) {
j++; // keep it visible
} else {
visibleChildren.RemoveAt(j); // hide it
}
} else {
// it wasn't visible before
if (FilterChild(child)) {
// make it visible
visibleChildren.Insert(j++, child);
}
}
}
RaisePropertyChanged("Text");
} }
public new readonly ObservableCollection<T> Children; readonly ObservableCollection<T> allChildren;
public new ObservableCollection<T> Children {
get { return allChildren; }
}
} }
class ILSpyTreeNode : ILSpyTreeNode<ILSpyTreeNodeBase> {} class ILSpyTreeNode : ILSpyTreeNode<ILSpyTreeNodeBase> {}

4
ILSpy/TreeNodes/MethodTreeNode.cs

@ -48,10 +48,10 @@ namespace ICSharpCode.ILSpy
b.Append('('); b.Append('(');
for (int i = 0; i < method.Parameters.Count; i++) { for (int i = 0; i < method.Parameters.Count; i++) {
if (i > 0) b.Append(", "); if (i > 0) b.Append(", ");
b.Append(Language.Current.TypeToString(method.Parameters[i].ParameterType)); b.Append(this.Language.TypeToString(method.Parameters[i].ParameterType));
} }
b.Append(") : "); b.Append(") : ");
b.Append(Language.Current.TypeToString(method.ReturnType)); b.Append(this.Language.TypeToString(method.ReturnType));
return b.ToString(); return b.ToString();
} }
} }

2
ILSpy/TreeNodes/PropertyTreeNode.cs

@ -44,7 +44,7 @@ namespace ICSharpCode.ILSpy
} }
public override object Text { public override object Text {
get { return property.Name + " : " + Language.Current.TypeToString(property.PropertyType); } get { return property.Name + " : " + this.Language.TypeToString(property.PropertyType); }
} }
public override object Icon { public override object Icon {

112
ILSpy/TreeTraversal.cs

@ -1,112 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Static helper methods for traversing trees.
/// </summary>
public static class TreeTraversal
{
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in pre-order.
/// </summary>
/// <param name="root">The root element of the tree.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
public static IEnumerable<T> PreOrder<T>(T root, Func<T, IEnumerable<T>> recursion)
{
return PreOrder(new T[] { root }, recursion);
}
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in pre-order.
/// </summary>
/// <param name="input">The root elements of the forest.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
public static IEnumerable<T> PreOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
{
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>();
try {
stack.Push(input.GetEnumerator());
while (stack.Count > 0) {
while (stack.Peek().MoveNext()) {
T element = stack.Peek().Current;
yield return element;
IEnumerable<T> children = recursion(element);
if (children != null) {
stack.Push(children.GetEnumerator());
}
}
stack.Pop().Dispose();
}
} finally {
while (stack.Count > 0) {
stack.Pop().Dispose();
}
}
}
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in post-order.
/// </summary>
/// <param name="root">The root element of the tree.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in post-order.</returns>
public static IEnumerable<T> PostOrder<T>(T root, Func<T, IEnumerable<T>> recursion)
{
return PostOrder(new T[] { root }, recursion);
}
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in post-order.
/// </summary>
/// <param name="input">The root elements of the forest.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in post-order.</returns>
public static IEnumerable<T> PostOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
{
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>();
try {
stack.Push(input.GetEnumerator());
while (stack.Count > 0) {
while (stack.Peek().MoveNext()) {
T element = stack.Peek().Current;
IEnumerable<T> children = recursion(element);
if (children != null) {
stack.Push(children.GetEnumerator());
} else {
yield return element;
}
}
stack.Pop().Dispose();
if (stack.Count > 0)
yield return stack.Peek().Current;
}
} finally {
while (stack.Count > 0) {
stack.Pop().Dispose();
}
}
}
}
}
Loading…
Cancel
Save