Browse Source
			
			
			
			
				
		git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4578 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
				 12 changed files with 1224 additions and 0 deletions
			
			
		@ -0,0 +1,8 @@
				@@ -0,0 +1,8 @@
					 | 
				
			||||
<Application x:Class="XmlDOM.App" | 
				
			||||
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | 
				
			||||
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | 
				
			||||
	StartupUri="Window1.xaml"> | 
				
			||||
	<Application.Resources> | 
				
			||||
		  | 
				
			||||
	</Application.Resources> | 
				
			||||
</Application> | 
				
			||||
@ -0,0 +1,19 @@
				@@ -0,0 +1,19 @@
					 | 
				
			||||
using System; | 
				
			||||
using System.Windows; | 
				
			||||
using System.Data; | 
				
			||||
using System.Xml; | 
				
			||||
using System.Configuration; | 
				
			||||
 | 
				
			||||
namespace XmlDOM | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// Interaction logic for App.xaml
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public partial class App : Application | 
				
			||||
	{ | 
				
			||||
		public App() | 
				
			||||
		{ | 
				
			||||
			InitializeComponent(); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,31 @@
				@@ -0,0 +1,31 @@
					 | 
				
			||||
#region Using directives
 | 
				
			||||
 | 
				
			||||
using System; | 
				
			||||
using System.Reflection; | 
				
			||||
using System.Runtime.InteropServices; | 
				
			||||
 | 
				
			||||
#endregion
 | 
				
			||||
 | 
				
			||||
// General Information about an assembly is controlled through the following 
 | 
				
			||||
// set of attributes. Change these attribute values to modify the information
 | 
				
			||||
// associated with an assembly.
 | 
				
			||||
[assembly: AssemblyTitle("XmlDOM")] | 
				
			||||
[assembly: AssemblyDescription("")] | 
				
			||||
[assembly: AssemblyConfiguration("")] | 
				
			||||
[assembly: AssemblyCompany("")] | 
				
			||||
[assembly: AssemblyProduct("XmlDOM")] | 
				
			||||
[assembly: AssemblyCopyright("Copyright 2009")] | 
				
			||||
[assembly: AssemblyTrademark("")] | 
				
			||||
[assembly: AssemblyCulture("")] | 
				
			||||
 | 
				
			||||
// This sets the default COM visibility of types in the assembly to invisible.
 | 
				
			||||
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
 | 
				
			||||
[assembly: ComVisible(false)] | 
				
			||||
 | 
				
			||||
// The assembly version has following format :
 | 
				
			||||
//
 | 
				
			||||
// Major.Minor.Build.Revision
 | 
				
			||||
//
 | 
				
			||||
// You can specify all the values or you can use the default the Revision and 
 | 
				
			||||
// Build Numbers by using the '*' as shown below:
 | 
				
			||||
[assembly: AssemblyVersion("1.0.*")] | 
				
			||||
@ -0,0 +1,27 @@
				@@ -0,0 +1,27 @@
					 | 
				
			||||
#region Using directives
 | 
				
			||||
 | 
				
			||||
using System.Resources; | 
				
			||||
using System.Windows; | 
				
			||||
 | 
				
			||||
#endregion
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
//In order to begin building localizable applications, set 
 | 
				
			||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
 | 
				
			||||
//inside a <PropertyGroup>.  For example, if you are using US english
 | 
				
			||||
//in your source files, set the <UICulture> to en-US.  Then uncomment
 | 
				
			||||
//the NeutralResourceLanguage attribute below.  Update the "en-US" in
 | 
				
			||||
//the line below to match the UICulture setting in the project file.
 | 
				
			||||
 | 
				
			||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
[assembly: ThemeInfo( | 
				
			||||
    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
 | 
				
			||||
    //(used if a resource is not found in the page, 
 | 
				
			||||
    // or application resource dictionaries)
 | 
				
			||||
    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
 | 
				
			||||
    //(used if a resource is not found in the page, 
 | 
				
			||||
    // app, or any theme specific resource dictionaries)
 | 
				
			||||
)] | 
				
			||||
@ -0,0 +1,40 @@
				@@ -0,0 +1,40 @@
					 | 
				
			||||
<Window x:Class="XmlDOM.Window1" | 
				
			||||
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | 
				
			||||
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | 
				
			||||
	xmlns:clr="clr-namespace:ICSharpCode.AvalonEdit.XmlParser;assembly=ICSharpCode.AvalonEdit" | 
				
			||||
	xmlns:ic="http://icsharpcode.net/sharpdevelop/avalonedit" | 
				
			||||
	Title="XmlEditor" Height="450" Width="600" | 
				
			||||
	> | 
				
			||||
	<Window.Resources> | 
				
			||||
		<Storyboard x:Key="anim"> | 
				
			||||
	        <ColorAnimation Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)" To="Transparent" Duration="0:0:4"/> | 
				
			||||
	    </Storyboard> | 
				
			||||
		<HierarchicalDataTemplate DataType="{x:Type clr:RawDocument}" ItemsSource="{Binding Helper_Elements}"> | 
				
			||||
			<TextBlock Text="XML Document" Margin="2"/> | 
				
			||||
		</HierarchicalDataTemplate> | 
				
			||||
		<HierarchicalDataTemplate DataType="{x:Type clr:RawElement}" ItemsSource="{Binding Helper_AttributesAndElements}"> | 
				
			||||
			<TextBlock Text="{Binding StartTag.Name}" Margin="2" Initialized="BindElement"/> | 
				
			||||
		</HierarchicalDataTemplate> | 
				
			||||
		<HierarchicalDataTemplate DataType="{x:Type clr:RawAttribute}"> | 
				
			||||
			<StackPanel Orientation="Horizontal" Margin="2"> | 
				
			||||
				<TextBlock Text="{Binding Name}" Foreground="Blue" Initialized="BindObject"/> | 
				
			||||
				<TextBlock Text="=" VerticalAlignment="Center"/> | 
				
			||||
				<TextBlock Text="{Binding Value}" Foreground="Blue" Initialized="BindObject"/> | 
				
			||||
			</StackPanel> | 
				
			||||
		</HierarchicalDataTemplate> | 
				
			||||
		<HierarchicalDataTemplate DataType="{x:Type clr:RawText}" ItemContainerStyle="{x:Null}"> | 
				
			||||
			<Border BorderBrush="LightGray" Height="1" BorderThickness="1"/> | 
				
			||||
		</HierarchicalDataTemplate> | 
				
			||||
	</Window.Resources> | 
				
			||||
	<Grid> | 
				
			||||
		<Grid.ColumnDefinitions> | 
				
			||||
			<ColumnDefinition Width="2*"/> | 
				
			||||
			<ColumnDefinition Width="*"/> | 
				
			||||
		</Grid.ColumnDefinitions> | 
				
			||||
		<ic:TextEditor x:Name="editor"/> | 
				
			||||
		<DockPanel Grid.Column="1"> | 
				
			||||
			<Button DockPanel.Dock="Top" Content="Parse" Click="Button_Click"/> | 
				
			||||
			<TreeView  Name="treeView"/> | 
				
			||||
		</DockPanel> | 
				
			||||
	</Grid> | 
				
			||||
</Window> | 
				
			||||
@ -0,0 +1,82 @@
				@@ -0,0 +1,82 @@
					 | 
				
			||||
// <file>
 | 
				
			||||
//     <copyright see="prj:///doc/copyright.txt"/>
 | 
				
			||||
//     <license see="prj:///doc/license.txt"/>
 | 
				
			||||
//     <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
 | 
				
			||||
//     <version>$Revision$</version>
 | 
				
			||||
// </file>
 | 
				
			||||
 | 
				
			||||
using System; | 
				
			||||
using System.Collections.Generic; | 
				
			||||
using System.Text; | 
				
			||||
using System.Windows; | 
				
			||||
using System.Windows.Controls; | 
				
			||||
using System.Windows.Data; | 
				
			||||
using System.Windows.Documents; | 
				
			||||
using System.Windows.Input; | 
				
			||||
using System.Windows.Media; | 
				
			||||
using System.Windows.Media.Animation; | 
				
			||||
using System.Windows.Threading; | 
				
			||||
 | 
				
			||||
using ICSharpCode.AvalonEdit.XmlParser; | 
				
			||||
 | 
				
			||||
namespace XmlDOM | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// Interaction logic for Window1.xaml
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public partial class Window1 : Window | 
				
			||||
	{ | 
				
			||||
		XmlParser parser; | 
				
			||||
		 | 
				
			||||
		public Window1() | 
				
			||||
		{ | 
				
			||||
			InitializeComponent(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		protected override void OnInitialized(EventArgs e) | 
				
			||||
		{ | 
				
			||||
			parser = new XmlParser(editor.Document); | 
				
			||||
 | 
				
			||||
			DispatcherTimer timer = new DispatcherTimer(); | 
				
			||||
			timer.Interval = TimeSpan.FromSeconds(0.25); | 
				
			||||
			timer.Tick += delegate { Button_Click(null, null); }; | 
				
			||||
			timer.Start(); | 
				
			||||
			 | 
				
			||||
			base.OnInitialized(e); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void Button_Click(object sender, RoutedEventArgs e) | 
				
			||||
		{ | 
				
			||||
			RawDocument doc = parser.Parse(); | 
				
			||||
			if (treeView.Items.Count == 0) { | 
				
			||||
				treeView.Items.Add(doc); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void BindObject(object sender, EventArgs e) | 
				
			||||
		{ | 
				
			||||
			TextBlock textBlock = (TextBlock)sender; | 
				
			||||
			RawObject node = (RawObject)textBlock.DataContext; | 
				
			||||
			node.LocalDataChanged += delegate { | 
				
			||||
				BindingOperations.GetBindingExpression(textBlock, TextBlock.TextProperty).UpdateTarget(); | 
				
			||||
				textBlock.Background = new SolidColorBrush(Colors.LightGreen); | 
				
			||||
				Storyboard sb = ((Storyboard)this.FindResource("anim")); | 
				
			||||
				Storyboard.SetTarget(sb, textBlock); | 
				
			||||
				sb.Begin(); | 
				
			||||
			}; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void BindElement(object sender, EventArgs e) | 
				
			||||
		{ | 
				
			||||
			TextBlock textBlock = (TextBlock)sender; | 
				
			||||
			RawElement node = (RawElement)textBlock.DataContext; | 
				
			||||
			node.StartTag.LocalDataChanged += delegate { | 
				
			||||
				BindingOperations.GetBindingExpression(textBlock, TextBlock.TextProperty).UpdateTarget(); | 
				
			||||
				textBlock.Background = new SolidColorBrush(Colors.LightGreen); | 
				
			||||
				Storyboard sb = ((Storyboard)this.FindResource("anim")); | 
				
			||||
				Storyboard.SetTarget(sb, textBlock); | 
				
			||||
				sb.Begin(); | 
				
			||||
			}; | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,65 @@
				@@ -0,0 +1,65 @@
					 | 
				
			||||
<?xml version="1.0" encoding="utf-8"?> | 
				
			||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> | 
				
			||||
  <PropertyGroup> | 
				
			||||
    <ProjectGuid>{AF8CA20E-58AC-423E-95A8-5AA464938D19}</ProjectGuid> | 
				
			||||
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | 
				
			||||
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | 
				
			||||
    <OutputType>WinExe</OutputType> | 
				
			||||
    <RootNamespace>XmlDOM</RootNamespace> | 
				
			||||
    <AssemblyName>XmlDOM</AssemblyName> | 
				
			||||
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | 
				
			||||
    <AppDesignerFolder>Properties</AppDesignerFolder> | 
				
			||||
  </PropertyGroup> | 
				
			||||
  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> | 
				
			||||
    <OutputPath>bin\Debug\</OutputPath> | 
				
			||||
    <DebugSymbols>True</DebugSymbols> | 
				
			||||
    <DebugType>Full</DebugType> | 
				
			||||
    <Optimize>False</Optimize> | 
				
			||||
    <CheckForOverflowUnderflow>True</CheckForOverflowUnderflow> | 
				
			||||
    <DefineConstants>DEBUG;TRACE</DefineConstants> | 
				
			||||
  </PropertyGroup> | 
				
			||||
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | 
				
			||||
    <OutputPath>bin\Release\</OutputPath> | 
				
			||||
    <DebugSymbols>False</DebugSymbols> | 
				
			||||
    <DebugType>None</DebugType> | 
				
			||||
    <Optimize>True</Optimize> | 
				
			||||
    <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> | 
				
			||||
    <DefineConstants>TRACE</DefineConstants> | 
				
			||||
  </PropertyGroup> | 
				
			||||
  <ItemGroup> | 
				
			||||
    <Reference Include="ICSharpCode.AvalonEdit"> | 
				
			||||
      <HintPath>..\..\bin\ICSharpCode.AvalonEdit.dll</HintPath> | 
				
			||||
    </Reference> | 
				
			||||
    <Reference Include="PresentationCore"> | 
				
			||||
      <RequiredTargetFramework>3.0</RequiredTargetFramework> | 
				
			||||
    </Reference> | 
				
			||||
    <Reference Include="PresentationFramework"> | 
				
			||||
      <RequiredTargetFramework>3.0</RequiredTargetFramework> | 
				
			||||
    </Reference> | 
				
			||||
    <Reference Include="System" /> | 
				
			||||
    <Reference Include="System.Data" /> | 
				
			||||
    <Reference Include="System.Xml" /> | 
				
			||||
    <Reference Include="WindowsBase"> | 
				
			||||
      <RequiredTargetFramework>3.0</RequiredTargetFramework> | 
				
			||||
    </Reference> | 
				
			||||
  </ItemGroup> | 
				
			||||
  <ItemGroup> | 
				
			||||
    <ApplicationDefinition Include="App.xaml" /> | 
				
			||||
  </ItemGroup> | 
				
			||||
  <ItemGroup> | 
				
			||||
    <Compile Include="App.xaml.cs"> | 
				
			||||
      <SubType>Code</SubType> | 
				
			||||
      <DependentUpon>App.xaml</DependentUpon> | 
				
			||||
    </Compile> | 
				
			||||
    <Compile Include="Properties\AssemblyInfo.cs" /> | 
				
			||||
    <Compile Include="Properties\WPFAssemblyInfo.cs" /> | 
				
			||||
    <Compile Include="Window1.xaml.cs"> | 
				
			||||
      <SubType>Code</SubType> | 
				
			||||
      <DependentUpon>Window1.xaml</DependentUpon> | 
				
			||||
    </Compile> | 
				
			||||
  </ItemGroup> | 
				
			||||
  <ItemGroup> | 
				
			||||
    <Page Include="Window1.xaml" /> | 
				
			||||
  </ItemGroup> | 
				
			||||
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> | 
				
			||||
</Project> | 
				
			||||
@ -0,0 +1,18 @@
				@@ -0,0 +1,18 @@
					 | 
				
			||||
 | 
				
			||||
Microsoft Visual Studio Solution File, Format Version 11.00 | 
				
			||||
# Visual Studio 10 | 
				
			||||
# SharpDevelop 4.0.0.4567 | 
				
			||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlDOM", "XmlDOM.csproj", "{AF8CA20E-58AC-423E-95A8-5AA464938D19}" | 
				
			||||
EndProject | 
				
			||||
Global | 
				
			||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution | 
				
			||||
		Debug|Any CPU = Debug|Any CPU | 
				
			||||
		Release|Any CPU = Release|Any CPU | 
				
			||||
	EndGlobalSection | 
				
			||||
	GlobalSection(ProjectConfigurationPlatforms) = postSolution | 
				
			||||
		{AF8CA20E-58AC-423E-95A8-5AA464938D19}.Debug|Any CPU.Build.0 = Debug|Any CPU | 
				
			||||
		{AF8CA20E-58AC-423E-95A8-5AA464938D19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | 
				
			||||
		{AF8CA20E-58AC-423E-95A8-5AA464938D19}.Release|Any CPU.Build.0 = Release|Any CPU | 
				
			||||
		{AF8CA20E-58AC-423E-95A8-5AA464938D19}.Release|Any CPU.ActiveCfg = Release|Any CPU | 
				
			||||
	EndGlobalSection | 
				
			||||
EndGlobal | 
				
			||||
@ -0,0 +1,112 @@
				@@ -0,0 +1,112 @@
					 | 
				
			||||
// <file>
 | 
				
			||||
//     <copyright see="prj:///doc/copyright.txt"/>
 | 
				
			||||
//     <license see="prj:///doc/license.txt"/>
 | 
				
			||||
//     <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
 | 
				
			||||
//     <version>$Revision$</version>
 | 
				
			||||
// </file>
 | 
				
			||||
 | 
				
			||||
using System; | 
				
			||||
using System.Collections.Generic; | 
				
			||||
using System.Collections.ObjectModel; | 
				
			||||
using System.Collections.Specialized; | 
				
			||||
using System.Linq; | 
				
			||||
 | 
				
			||||
// Missing XML comment
 | 
				
			||||
#pragma warning disable 1591
 | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.XmlParser | 
				
			||||
{ | 
				
			||||
	public class FilteredObservableCollection<T>: ObservableCollection<T> | 
				
			||||
	{ | 
				
			||||
		ObservableCollection<T> source; | 
				
			||||
		Predicate<T> condition; | 
				
			||||
		List<int> srcPtrs = new List<int>(); | 
				
			||||
		 | 
				
			||||
		public FilteredObservableCollection(ObservableCollection<T> source, Predicate<T> condition) | 
				
			||||
		{ | 
				
			||||
			this.source = source; | 
				
			||||
			this.condition = condition; | 
				
			||||
			 | 
				
			||||
			for(int i = 0; i < source.Count; i++) { | 
				
			||||
				if (condition(source[i])) { | 
				
			||||
					int index = srcPtrs.Count; | 
				
			||||
					this.InsertItem(index, source[i]); | 
				
			||||
					srcPtrs.Insert(index, i); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			this.source.CollectionChanged += new NotifyCollectionChangedEventHandler(FilteredObservableCollection_CollectionChanged); | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		void FilteredObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | 
				
			||||
		{ | 
				
			||||
			if (e.Action == NotifyCollectionChangedAction.Remove) { | 
				
			||||
				// Remove the item from our collection
 | 
				
			||||
				if (condition((T)e.OldItems[0])) { | 
				
			||||
					int index = srcPtrs.IndexOf(e.OldStartingIndex); | 
				
			||||
					this.RemoveAt(index); | 
				
			||||
					srcPtrs.RemoveAt(index); | 
				
			||||
				} | 
				
			||||
				// Update pointers
 | 
				
			||||
				for(int i = 0; i < srcPtrs.Count; i++) { | 
				
			||||
					if (srcPtrs[i] > e.OldStartingIndex) { | 
				
			||||
						srcPtrs[i]--; | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			if (e.Action == NotifyCollectionChangedAction.Add) { | 
				
			||||
				// Update pointers
 | 
				
			||||
				for(int i = 0; i < srcPtrs.Count; i++) { | 
				
			||||
					if (srcPtrs[i] >= e.NewStartingIndex) { | 
				
			||||
						srcPtrs[i]++; | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
				// Add item to collection
 | 
				
			||||
				if (condition((T)e.NewItems[0])) { | 
				
			||||
					int index = srcPtrs.FindIndex(srcPtr => srcPtr >= e.NewStartingIndex); | 
				
			||||
					if (index == -1) index = srcPtrs.Count; | 
				
			||||
					this.InsertItem(index, (T)e.NewItems[0]); | 
				
			||||
					srcPtrs.Insert(index, e.NewStartingIndex); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public class MergedObservableCollection<T>: ObservableCollection<T> | 
				
			||||
	{ | 
				
			||||
		ObservableCollection<T> a; | 
				
			||||
		ObservableCollection<T> b; | 
				
			||||
		 | 
				
			||||
		public MergedObservableCollection(ObservableCollection<T> a, ObservableCollection<T> b) | 
				
			||||
		{ | 
				
			||||
			this.a = a; | 
				
			||||
			this.b = b; | 
				
			||||
			 | 
				
			||||
			foreach(T item in a) this.Add(item); | 
				
			||||
			foreach(T item in b) this.Add(item); | 
				
			||||
			 | 
				
			||||
			this.a.CollectionChanged += new NotifyCollectionChangedEventHandler(MergedObservableCollection_CollectionAChanged); | 
				
			||||
			this.b.CollectionChanged += new NotifyCollectionChangedEventHandler(MergedObservableCollection_CollectionBChanged); | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		void MergedObservableCollection_CollectionAChanged(object sender, NotifyCollectionChangedEventArgs e) | 
				
			||||
		{ | 
				
			||||
			if (e.Action == NotifyCollectionChangedAction.Remove) { | 
				
			||||
				this.RemoveAt(e.OldStartingIndex); | 
				
			||||
			} | 
				
			||||
			if (e.Action == NotifyCollectionChangedAction.Add) { | 
				
			||||
				this.InsertItem(e.NewStartingIndex, (T)e.NewItems[0]); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void MergedObservableCollection_CollectionBChanged(object sender, NotifyCollectionChangedEventArgs e) | 
				
			||||
		{ | 
				
			||||
			if (e.Action == NotifyCollectionChangedAction.Remove) { | 
				
			||||
				this.RemoveAt(e.OldStartingIndex + a.Count); | 
				
			||||
			} | 
				
			||||
			if (e.Action == NotifyCollectionChangedAction.Add) { | 
				
			||||
				this.InsertItem(e.NewStartingIndex + a.Count, (T)e.NewItems[0]); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,455 @@
				@@ -0,0 +1,455 @@
					 | 
				
			||||
// <file>
 | 
				
			||||
//     <copyright see="prj:///doc/copyright.txt"/>
 | 
				
			||||
//     <license see="prj:///doc/license.txt"/>
 | 
				
			||||
//     <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
 | 
				
			||||
//     <version>$Revision$</version>
 | 
				
			||||
// </file>
 | 
				
			||||
 | 
				
			||||
using System; | 
				
			||||
using System.Collections.Generic; | 
				
			||||
using System.Collections.ObjectModel; | 
				
			||||
using System.Linq; | 
				
			||||
using System.Xml.Linq; | 
				
			||||
 | 
				
			||||
using ICSharpCode.AvalonEdit.Document; | 
				
			||||
 | 
				
			||||
// Missing XML comment
 | 
				
			||||
#pragma warning disable 1591
 | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.XmlParser | 
				
			||||
{ | 
				
			||||
	public class RawObjectEventArgs: EventArgs | 
				
			||||
	{ | 
				
			||||
		public RawObject Object { get; set; } | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// The base class for all XML objects.  The objects store the precise text 
 | 
				
			||||
	/// representation so that generated text will preciesly match original.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public abstract class RawObject: TextSegment | 
				
			||||
	{ | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Unique identifier for the specific call of parsing read function.  
 | 
				
			||||
		/// It is used to uniquely identify all object data (including nested).
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		internal object ReadCallID { get; private set; } | 
				
			||||
		 | 
				
			||||
		public RawObject Parent { get; set; } | 
				
			||||
		 | 
				
			||||
		public RawDocument Document { | 
				
			||||
			get { | 
				
			||||
				if (this.Parent != null) { | 
				
			||||
					return this.Parent.Document; | 
				
			||||
				} else if (this is RawDocument) { | 
				
			||||
					return (RawDocument)this; | 
				
			||||
				} else { | 
				
			||||
					return null; | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary> Occurs when the value of any local properties changes.  Nested changes do not cause the event to occur </summary>
 | 
				
			||||
		public event EventHandler LocalDataChanged; | 
				
			||||
		 | 
				
			||||
		protected void OnLocalDataChanged() | 
				
			||||
		{ | 
				
			||||
			Log("XML DOM: Local data changed for {0}", this); | 
				
			||||
			if (LocalDataChanged != null) { | 
				
			||||
				LocalDataChanged(this, EventArgs.Empty); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public new int EndOffset { | 
				
			||||
			get { | 
				
			||||
				return this.StartOffset + this.Length; | 
				
			||||
			} | 
				
			||||
			set { | 
				
			||||
				this.Length = value - this.StartOffset; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public RawObject() | 
				
			||||
		{ | 
				
			||||
			this.ReadCallID = new object(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public RawObject Clone() | 
				
			||||
		{ | 
				
			||||
			RawObject clone = (RawObject)System.Activator.CreateInstance(this.GetType()); | 
				
			||||
			bool oldVal = LoggingEnabled; | 
				
			||||
			LoggingEnabled = false; | 
				
			||||
			clone.UpdateDataFrom(this); | 
				
			||||
			LoggingEnabled = oldVal; | 
				
			||||
			return clone; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public virtual IEnumerable<RawObject> GetSeftAndAllNestedObjects() | 
				
			||||
		{ | 
				
			||||
			yield return this; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public virtual void UpdateDataFrom(RawObject source) | 
				
			||||
		{ | 
				
			||||
			this.ReadCallID = source.ReadCallID; | 
				
			||||
			this.StartOffset = source.StartOffset; | 
				
			||||
			this.EndOffset = source.EndOffset; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override string ToString() | 
				
			||||
		{ | 
				
			||||
			return string.Format("{0}({1}-{2})", this.GetType().Name.Remove(0, 3), this.StartOffset, this.EndOffset); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public static bool LoggingEnabled = true; | 
				
			||||
		 | 
				
			||||
		public static void Log(string format, params object[] args) | 
				
			||||
		{ | 
				
			||||
			if (LoggingEnabled) { | 
				
			||||
				System.Diagnostics.Debug.WriteLine(format, args); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		internal void OnInserting(RawObject parent, int index) | 
				
			||||
		{ | 
				
			||||
			Log("XML DOM: Inserting {0} at index {1}", this, index); | 
				
			||||
			this.Parent = parent; | 
				
			||||
			if (this.Document != null) { | 
				
			||||
				foreach(RawObject obj in GetSeftAndAllNestedObjects()) { | 
				
			||||
					this.Document.OnObjectAttached(new RawObjectEventArgs() { Object = obj }); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		internal void OnRemoving(RawObject parent, int index) | 
				
			||||
		{ | 
				
			||||
			Log("XML DOM: Removing {0} at index {1}", this, index); | 
				
			||||
			this.Parent = null; | 
				
			||||
			if (this.Document != null) { | 
				
			||||
				foreach(RawObject obj in GetSeftAndAllNestedObjects()) { | 
				
			||||
					this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj }); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public class RawDocument: RawObject | 
				
			||||
	{ | 
				
			||||
		public ObservableCollection<RawObject> Children { get; set; } | 
				
			||||
		 | 
				
			||||
		public event EventHandler<RawObjectEventArgs> ObjectAttached; | 
				
			||||
		public event EventHandler<RawObjectEventArgs> ObjectDettached; | 
				
			||||
		 | 
				
			||||
		public ObservableCollection<RawObject> Helper_Elements { | 
				
			||||
			get { | 
				
			||||
				return new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		internal void OnObjectAttached(RawObjectEventArgs e) | 
				
			||||
		{ | 
				
			||||
			if (ObjectAttached != null) ObjectAttached(this, e); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		internal void OnObjectDettached(RawObjectEventArgs e) | 
				
			||||
		{ | 
				
			||||
			if (ObjectDettached != null) ObjectDettached(this, e); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public RawDocument() | 
				
			||||
		{ | 
				
			||||
			this.Children = new ObservableCollection<RawObject>(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override IEnumerable<RawObject> GetSeftAndAllNestedObjects() | 
				
			||||
		{ | 
				
			||||
			return Enumerable.Union( | 
				
			||||
				new RawObject[] { this }, | 
				
			||||
				this.Children.SelectMany(x => x.GetSeftAndAllNestedObjects()) | 
				
			||||
			); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override void UpdateDataFrom(RawObject source) | 
				
			||||
		{ | 
				
			||||
			if (this.ReadCallID == source.ReadCallID) return; | 
				
			||||
			base.UpdateDataFrom(source); | 
				
			||||
			RawDocument src = (RawDocument)source; | 
				
			||||
			RawUtils.SmartListUpdate(src.Children, this.Children, this); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public XDocument CreateXDocument(bool autoUpdate) | 
				
			||||
		{ | 
				
			||||
			XDocument doc = new XDocument(); | 
				
			||||
			return doc; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override string ToString() | 
				
			||||
		{ | 
				
			||||
			return string.Format("[{0} Chld:{1}]", base.ToString(), this.Children.Count); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public class RawTag: RawObject | 
				
			||||
	{ | 
				
			||||
		public string OpeningBracket { get; set; } // "<" or "</"
 | 
				
			||||
		public string Name { get; set; } | 
				
			||||
		public ObservableCollection<RawObject> Attributes { get; set; } | 
				
			||||
		public string ClosingBracket { get; set; } // ">" or "/>" for well formed
 | 
				
			||||
		 | 
				
			||||
		public RawTag() | 
				
			||||
		{ | 
				
			||||
			this.Attributes = new ObservableCollection<RawObject>(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override IEnumerable<RawObject> GetSeftAndAllNestedObjects() | 
				
			||||
		{ | 
				
			||||
			return Enumerable.Union( | 
				
			||||
				new RawObject[] { this }, | 
				
			||||
				this.Attributes // Have no nested objects
 | 
				
			||||
			); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override void UpdateDataFrom(RawObject source) | 
				
			||||
		{ | 
				
			||||
			if (this.ReadCallID == source.ReadCallID) return; | 
				
			||||
			base.UpdateDataFrom(source); | 
				
			||||
			RawTag src = (RawTag)source; | 
				
			||||
			if (this.OpeningBracket != src.OpeningBracket || | 
				
			||||
				this.Name != src.Name || | 
				
			||||
				this.ClosingBracket != src.ClosingBracket) | 
				
			||||
			{ | 
				
			||||
				this.OpeningBracket = src.OpeningBracket; | 
				
			||||
				this.Name = src.Name; | 
				
			||||
				this.ClosingBracket = src.ClosingBracket; | 
				
			||||
				OnLocalDataChanged(); | 
				
			||||
			} | 
				
			||||
			RawUtils.SmartListUpdate(src.Attributes, this.Attributes, this); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override string ToString() | 
				
			||||
		{ | 
				
			||||
			return string.Format("[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Attributes.Count); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public class RawElement: RawObject | 
				
			||||
	{ | 
				
			||||
		public RawTag StartTag { get; set; } | 
				
			||||
		public ObservableCollection<RawObject> Children { get; set; } | 
				
			||||
		public bool HasEndTag { get; set; } | 
				
			||||
		public RawTag EndTag { get; set; } | 
				
			||||
		 | 
				
			||||
		public ObservableCollection<RawObject> Helper_AttributesAndElements { | 
				
			||||
			get { | 
				
			||||
				return new MergedObservableCollection<RawObject>( | 
				
			||||
					new FilteredObservableCollection<RawObject>(this.StartTag.Attributes, x => x is RawAttribute), | 
				
			||||
					new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement) | 
				
			||||
				); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public RawElement() | 
				
			||||
		{ | 
				
			||||
			this.StartTag = new RawTag() { Parent = this }; | 
				
			||||
			this.Children = new ObservableCollection<RawObject>(); | 
				
			||||
			this.EndTag   = new RawTag() { Parent = this }; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override IEnumerable<RawObject> GetSeftAndAllNestedObjects() | 
				
			||||
		{ | 
				
			||||
			return new IEnumerable<RawObject>[] { | 
				
			||||
				new RawObject[] { this }, | 
				
			||||
				this.StartTag.GetSeftAndAllNestedObjects(), | 
				
			||||
				this.Children.SelectMany(x => x.GetSeftAndAllNestedObjects()), | 
				
			||||
				this.EndTag.GetSeftAndAllNestedObjects() | 
				
			||||
			}.SelectMany(x => x); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override void UpdateDataFrom(RawObject source) | 
				
			||||
		{ | 
				
			||||
			if (this.ReadCallID == source.ReadCallID) return; | 
				
			||||
			base.UpdateDataFrom(source); | 
				
			||||
			RawElement src = (RawElement)source; | 
				
			||||
			this.StartTag.UpdateDataFrom(src.StartTag); | 
				
			||||
			RawUtils.SmartListUpdate(src.Children, this.Children, this); | 
				
			||||
			this.EndTag.UpdateDataFrom(src.EndTag); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public XElement CreateXElement(bool autoUpdate) | 
				
			||||
		{ | 
				
			||||
			XElement elem = new XElement(string.Empty); | 
				
			||||
			UpdateXElement(elem); | 
				
			||||
			if (autoUpdate) this.StartTag.LocalDataChanged += delegate { UpdateXElement(elem); }; | 
				
			||||
			return elem; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void UpdateXElement(XElement elem) | 
				
			||||
		{ | 
				
			||||
			elem.Name = this.StartTag.Name; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override string ToString() | 
				
			||||
		{ | 
				
			||||
			return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Attributes.Count, this.Children.Count); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public class RawAttribute: RawObject | 
				
			||||
	{ | 
				
			||||
		public string Name { get; set; } | 
				
			||||
		public string EqualsSign { get; set; } | 
				
			||||
		public string Value { get; set; } | 
				
			||||
		 | 
				
			||||
		public override void UpdateDataFrom(RawObject source) | 
				
			||||
		{ | 
				
			||||
			if (this.ReadCallID == source.ReadCallID) return; | 
				
			||||
			base.UpdateDataFrom(source); | 
				
			||||
			RawAttribute src = (RawAttribute)source; | 
				
			||||
			if (this.Name != src.Name || | 
				
			||||
				this.EqualsSign != src.EqualsSign || | 
				
			||||
				this.Value != src.Value) | 
				
			||||
			{ | 
				
			||||
				this.Name = src.Name; | 
				
			||||
				this.EqualsSign = src.EqualsSign; | 
				
			||||
				this.Value = src.Value; | 
				
			||||
				OnLocalDataChanged(); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public XAttribute CreateXAttribute(bool autoUpdate) | 
				
			||||
		{ | 
				
			||||
			XAttribute attr = new XAttribute(string.Empty, string.Empty); | 
				
			||||
			UpdateXAttribute(attr); | 
				
			||||
			if (autoUpdate) this.LocalDataChanged += delegate { UpdateXAttribute(attr); }; | 
				
			||||
			return attr; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void UpdateXAttribute(XAttribute attr) | 
				
			||||
		{ | 
				
			||||
			attr.Value = this.Value; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override string ToString() | 
				
			||||
		{ | 
				
			||||
			return string.Format("[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.Value); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public class RawText: RawObject | 
				
			||||
	{ | 
				
			||||
		public string Value { get; set; } | 
				
			||||
		 | 
				
			||||
		public override void UpdateDataFrom(RawObject source) | 
				
			||||
		{ | 
				
			||||
			if (this.ReadCallID == source.ReadCallID) return; | 
				
			||||
			base.UpdateDataFrom(source); | 
				
			||||
			RawText src = (RawText)source; | 
				
			||||
			if (this.Value != src.Value) { | 
				
			||||
				this.Value = src.Value; | 
				
			||||
				OnLocalDataChanged(); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public XText CreateXText(bool autoUpdate) | 
				
			||||
		{ | 
				
			||||
			XText text = new XText(string.Empty); | 
				
			||||
			UpdateXText(text); | 
				
			||||
			if (autoUpdate) this.LocalDataChanged += delegate { UpdateXText(text); }; | 
				
			||||
			return text; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void UpdateXText(XText text) | 
				
			||||
		{ | 
				
			||||
			text.Value = this.Value; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public override string ToString() | 
				
			||||
		{ | 
				
			||||
			return string.Format("[{0} Text.Length={1}]", base.ToString(), this.Value.Length); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	public static class RawUtils | 
				
			||||
	{ | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Copy items from source list over to destination list.  
 | 
				
			||||
		/// Prefer updating items with matching offsets.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public static void SmartListUpdate(IList<RawObject> srcList, IList<RawObject> dstList, RawObject dstListOwner) | 
				
			||||
		{ | 
				
			||||
			// Items up to 'i' shall be matching
 | 
				
			||||
			for(int i = 0; i < srcList.Count;) { | 
				
			||||
				// Item is missing - 'i' is invalid index
 | 
				
			||||
				if (i >= dstList.Count) { | 
				
			||||
					RawObject clone = srcList[i].Clone(); | 
				
			||||
					clone.OnInserting(dstListOwner, i); | 
				
			||||
					dstList.Insert(i, clone); | 
				
			||||
					i++; continue; | 
				
			||||
				} | 
				
			||||
				RawObject srcItem = srcList[i]; | 
				
			||||
				RawObject dstItem = dstList[i]; | 
				
			||||
				// Matching and updated
 | 
				
			||||
				if (srcItem.ReadCallID == dstItem.ReadCallID) { | 
				
			||||
					i++; continue; | 
				
			||||
				} | 
				
			||||
				// Offsets and types are matching
 | 
				
			||||
				if (srcItem.StartOffset == dstItem.StartOffset && | 
				
			||||
				    srcItem.GetType() == dstItem.GetType()) | 
				
			||||
				{ | 
				
			||||
					dstItem.UpdateDataFrom(srcItem); | 
				
			||||
					i++; continue; | 
				
			||||
				} | 
				
			||||
				// Try to be smart by inserting or removing items
 | 
				
			||||
				// Dst offset matches with future src
 | 
				
			||||
				for(int srcItemIndex = i; srcItemIndex < srcList.Count; srcItemIndex++) { | 
				
			||||
					RawObject src = srcList[srcItemIndex]; | 
				
			||||
					if (src.StartOffset == dstItem.StartOffset && src.GetType() == dstItem.GetType()) { | 
				
			||||
						for(int j = i; j < srcItemIndex; j++) { | 
				
			||||
							RawObject clone = srcList[j].Clone(); | 
				
			||||
							clone.OnInserting(dstListOwner, j); | 
				
			||||
							dstList.Insert(j, clone); | 
				
			||||
						} | 
				
			||||
						i = srcItemIndex; | 
				
			||||
						goto continue2; | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
				// Scr offset matches with future dst
 | 
				
			||||
				for(int dstItemIndex = i; dstItemIndex < dstList.Count; dstItemIndex++) { | 
				
			||||
					RawObject dst = dstList[dstItemIndex]; | 
				
			||||
					if (srcItem.StartOffset == dst.StartOffset && srcItem.GetType() == dst.GetType()) { | 
				
			||||
						for(int j = 0; j < dstItemIndex - i; j++) { | 
				
			||||
							dstList[i].OnRemoving(dstListOwner, i); | 
				
			||||
							dstList.RemoveAt(i); | 
				
			||||
						} | 
				
			||||
						goto continue2; | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
				// No matches found - just update
 | 
				
			||||
				if (dstItem.GetType() == srcItem.GetType()) { | 
				
			||||
					dstItem.UpdateDataFrom(srcItem); | 
				
			||||
					i++; continue; | 
				
			||||
				} | 
				
			||||
				// Remove whitespace in hope that element update will occur next
 | 
				
			||||
				if (dstItem is RawText) { | 
				
			||||
					dstList[i].OnRemoving(dstListOwner, i); | 
				
			||||
					dstList.RemoveAt(i); | 
				
			||||
					continue; | 
				
			||||
				} | 
				
			||||
				// Otherwise just add the item
 | 
				
			||||
				{ | 
				
			||||
					RawObject clone = srcList[i].Clone(); | 
				
			||||
					clone.OnInserting(dstListOwner, i); | 
				
			||||
					dstList.Insert(i, clone); | 
				
			||||
					i++; continue; | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			// Remove extra items
 | 
				
			||||
			while(dstList.Count > srcList.Count) { | 
				
			||||
				dstList[srcList.Count].OnRemoving(dstListOwner, srcList.Count); | 
				
			||||
				dstList.RemoveAt(srcList.Count); | 
				
			||||
			} | 
				
			||||
			// Continue for inner loops
 | 
				
			||||
			continue2:; | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,363 @@
				@@ -0,0 +1,363 @@
					 | 
				
			||||
// <file>
 | 
				
			||||
//     <copyright see="prj:///doc/copyright.txt"/>
 | 
				
			||||
//     <license see="prj:///doc/license.txt"/>
 | 
				
			||||
//     <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
 | 
				
			||||
//     <version>$Revision$</version>
 | 
				
			||||
// </file>
 | 
				
			||||
 | 
				
			||||
using System; | 
				
			||||
using System.Diagnostics; | 
				
			||||
using System.Linq; | 
				
			||||
 | 
				
			||||
using ICSharpCode.AvalonEdit.Document; | 
				
			||||
 | 
				
			||||
// Missing XML comment
 | 
				
			||||
#pragma warning disable 1591
 | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.XmlParser | 
				
			||||
{ | 
				
			||||
	public class XmlParser | 
				
			||||
	{ | 
				
			||||
		RawDocument userDocument; | 
				
			||||
		TextDocument textDocument; | 
				
			||||
		TextSegmentCollection<RawObject> userDom; | 
				
			||||
		TextSegmentCollection<RawObject> parsedDom; | 
				
			||||
		 | 
				
			||||
		public XmlParser(TextDocument textDocument) | 
				
			||||
		{ | 
				
			||||
			this.userDocument = new RawDocument(); | 
				
			||||
			this.textDocument = textDocument; | 
				
			||||
			this.userDom = new TextSegmentCollection<RawObject>(textDocument); | 
				
			||||
			this.parsedDom = new TextSegmentCollection<RawObject>(textDocument); | 
				
			||||
			this.textDocument.Changed += TextDocument_Changed; | 
				
			||||
			this.userDocument.ObjectAttached += delegate(object sender, RawObjectEventArgs e) { | 
				
			||||
				this.userDom.Add(e.Object); | 
				
			||||
			}; | 
				
			||||
			this.userDocument.ObjectDettached += delegate(object sender, RawObjectEventArgs e) { | 
				
			||||
				this.userDom.Remove(e.Object); | 
				
			||||
			}; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public RawDocument Parse() | 
				
			||||
		{ | 
				
			||||
			currentLocation = 0; | 
				
			||||
			input = textDocument.Text; | 
				
			||||
			 | 
				
			||||
			RawDocument parsedDocument = ReadDocument(); | 
				
			||||
			userDocument.UpdateDataFrom(parsedDocument); | 
				
			||||
			return userDocument; | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		void TextDocument_Changed(object sender, DocumentChangeEventArgs e) | 
				
			||||
		{ | 
				
			||||
			int start = e.Offset - 2; | 
				
			||||
			int end = e.Offset + e.InsertionLength + 2; | 
				
			||||
			start = Math.Max(Math.Min(start, textDocument.TextLength - 1), 0); | 
				
			||||
			end = Math.Max(Math.Min(end, textDocument.TextLength - 1), 0); | 
				
			||||
			foreach(RawObject obj in parsedDom.FindOverlappingSegments(start, end - start)) { | 
				
			||||
				parsedDom.Remove(obj); | 
				
			||||
				Log("Removed cached item: {0}", obj); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		T ReadFromCache<T>(int location) where T: RawObject | 
				
			||||
		{ | 
				
			||||
			RawObject obj = parsedDom.FindFirstSegmentWithStartAfter(location); | 
				
			||||
			while(obj != null && obj.StartOffset == location) { | 
				
			||||
				if (obj is T) { | 
				
			||||
					currentLocation += obj.Length; | 
				
			||||
					return (T)obj; | 
				
			||||
				} | 
				
			||||
				obj = parsedDom.GetNextSegment(obj); | 
				
			||||
			} | 
				
			||||
			return null; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void Log(string text, params object[] pars) | 
				
			||||
		{ | 
				
			||||
			System.Diagnostics.Debug.WriteLine("XML Parser: " + text, pars); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void LogParsed(RawObject obj) | 
				
			||||
		{ | 
				
			||||
			System.Diagnostics.Debug.WriteLine("XML Parser: Parsed " + obj.ToString()); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		string input; | 
				
			||||
		int  currentLocation; | 
				
			||||
		 | 
				
			||||
		bool IsEndOfFile() | 
				
			||||
		{ | 
				
			||||
			return currentLocation == input.Length; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		bool HasMoreData() | 
				
			||||
		{ | 
				
			||||
			return currentLocation < input.Length; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		bool TryRead(char c) | 
				
			||||
		{ | 
				
			||||
			if (currentLocation == input.Length) return false; | 
				
			||||
			 | 
				
			||||
			if (input[currentLocation] == c) { | 
				
			||||
				currentLocation++; | 
				
			||||
				return true; | 
				
			||||
			} else { | 
				
			||||
				return false; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		bool TryPeek(char c) | 
				
			||||
		{ | 
				
			||||
			if (currentLocation == input.Length) return false; | 
				
			||||
			 | 
				
			||||
			return input[currentLocation] == c; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		bool TryPeek(string text) | 
				
			||||
		{ | 
				
			||||
			if (currentLocation + text.Length > input.Length) return false; | 
				
			||||
			 | 
				
			||||
			return input.Substring(currentLocation, text.Length) == text; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		bool TryMoveTo(params char[] c) | 
				
			||||
		{ | 
				
			||||
			while(true) { | 
				
			||||
				if (currentLocation == input.Length) return false; | 
				
			||||
				if (c.Contains(input[currentLocation])) return true; | 
				
			||||
				currentLocation++; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		string GetText(int start, int end) | 
				
			||||
		{ | 
				
			||||
			if (start == input.Length && end == input.Length) { | 
				
			||||
				return string.Empty; | 
				
			||||
			} else { | 
				
			||||
				return input.Substring(start, end - start); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		static char[] WhiteSpaceChars = new char[] {' ', '\n', '\r', '\t'}; | 
				
			||||
		static char[] WhiteSpaceAndReservedChars = new char[] {' ', '\n', '\r', '\t', '<', '=', '>', '/'}; | 
				
			||||
		 | 
				
			||||
		bool? IsWhiteSpace() | 
				
			||||
		{ | 
				
			||||
			if (currentLocation == input.Length) { | 
				
			||||
				return null; | 
				
			||||
			} else { | 
				
			||||
				return WhiteSpaceChars.Contains(input[currentLocation]); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		bool? IsWhiteSpaceOrReserved() | 
				
			||||
		{ | 
				
			||||
			if (currentLocation == input.Length) { | 
				
			||||
				return null; | 
				
			||||
			} else { | 
				
			||||
				return WhiteSpaceAndReservedChars.Contains(input[currentLocation]); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		string ReadName() | 
				
			||||
		{ | 
				
			||||
			Debug.Assert(HasMoreData()); | 
				
			||||
			 | 
				
			||||
			int start = currentLocation; | 
				
			||||
			TryMoveTo(WhiteSpaceAndReservedChars.ToArray()); | 
				
			||||
			return GetText(start, currentLocation); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		RawDocument ReadDocument() | 
				
			||||
		{ | 
				
			||||
			RawDocument doc = ReadFromCache<RawDocument>(currentLocation); | 
				
			||||
			if (doc != null) return doc; | 
				
			||||
			 | 
				
			||||
			doc = new RawDocument(); | 
				
			||||
			 | 
				
			||||
			doc.StartOffset = currentLocation; | 
				
			||||
			while(true) { | 
				
			||||
				if (IsEndOfFile()) { | 
				
			||||
					break; | 
				
			||||
				} else if (TryPeek('<')) { | 
				
			||||
					doc.Children.Add(ReadElement(doc)); | 
				
			||||
				} else { | 
				
			||||
					doc.Children.Add(ReadCharacterData(doc)); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			doc.EndOffset = currentLocation; | 
				
			||||
			 | 
				
			||||
			LogParsed(doc); | 
				
			||||
			parsedDom.Add(doc); | 
				
			||||
			return doc; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		RawElement ReadElement(RawObject parent) | 
				
			||||
		{ | 
				
			||||
			Debug.Assert(HasMoreData() && TryPeek('<')); | 
				
			||||
			 | 
				
			||||
			RawElement element = ReadFromCache<RawElement>(currentLocation); | 
				
			||||
			if (element != null) return element; | 
				
			||||
 | 
				
			||||
			element = new RawElement() { Parent = parent }; | 
				
			||||
			 | 
				
			||||
			element.StartOffset = currentLocation; | 
				
			||||
			element.StartTag = ReadTag(element); | 
				
			||||
			// Read content
 | 
				
			||||
			if (element.StartTag.ClosingBracket == ">") { | 
				
			||||
				while(true) { | 
				
			||||
					if (IsEndOfFile()) { | 
				
			||||
						break; | 
				
			||||
					} else if (TryPeek('<')) { | 
				
			||||
						if (TryPeek("</")) break; | 
				
			||||
						element.Children.Add(ReadElement(element)); | 
				
			||||
					} else { | 
				
			||||
						element.Children.Add(ReadCharacterData(element)); | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			// Read closing tag
 | 
				
			||||
			if (TryPeek("</")) { | 
				
			||||
				element.HasEndTag = true; | 
				
			||||
				element.EndTag = ReadTag(element); | 
				
			||||
			} | 
				
			||||
			element.EndOffset = currentLocation; | 
				
			||||
			 | 
				
			||||
			LogParsed(element); | 
				
			||||
			parsedDom.Add(element); | 
				
			||||
			return element; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		RawTag ReadTag(RawObject parent) | 
				
			||||
		{ | 
				
			||||
			Debug.Assert(HasMoreData() && TryPeek('<')); | 
				
			||||
			 | 
				
			||||
			RawTag tag = ReadFromCache<RawTag>(currentLocation); | 
				
			||||
			if (tag != null) return tag; | 
				
			||||
			 | 
				
			||||
			tag = new RawTag() { Parent = parent }; | 
				
			||||
			 | 
				
			||||
			tag.StartOffset = currentLocation; | 
				
			||||
			if (TryRead('<')) { | 
				
			||||
				tag.OpeningBracket = "<"; | 
				
			||||
				if (TryRead('/')) { | 
				
			||||
					tag.OpeningBracket += "/"; | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			if (HasMoreData()) { | 
				
			||||
				tag.Name = ReadName(); | 
				
			||||
			} | 
				
			||||
			// Read attributes
 | 
				
			||||
			while(true) { | 
				
			||||
				if (IsWhiteSpace() == true) { | 
				
			||||
					tag.Attributes.Add(ReadWhiteSpace(tag)); | 
				
			||||
				} | 
				
			||||
				if (TryRead('>')) { | 
				
			||||
					tag.ClosingBracket = ">"; | 
				
			||||
					break; | 
				
			||||
				} else 	if (TryRead('/')) { | 
				
			||||
					tag.ClosingBracket = "/"; | 
				
			||||
					if (TryRead('>')) { | 
				
			||||
						tag.ClosingBracket += ">"; | 
				
			||||
					} | 
				
			||||
					break; | 
				
			||||
				} | 
				
			||||
				if (TryPeek('<')) break; | 
				
			||||
				if (HasMoreData()) { | 
				
			||||
					tag.Attributes.Add(ReadAttribulte(tag)); | 
				
			||||
					continue; | 
				
			||||
				} | 
				
			||||
				break; | 
				
			||||
			} | 
				
			||||
			tag.EndOffset = currentLocation; | 
				
			||||
			 | 
				
			||||
			LogParsed(tag); | 
				
			||||
			parsedDom.Add(tag); | 
				
			||||
			return tag; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		RawText ReadWhiteSpace(RawObject parent) | 
				
			||||
		{ | 
				
			||||
			Debug.Assert(HasMoreData() && IsWhiteSpace() == true); | 
				
			||||
			 | 
				
			||||
			RawText ws = ReadFromCache<RawText>(currentLocation); | 
				
			||||
			if (ws != null) return ws; | 
				
			||||
			 | 
				
			||||
			ws = new RawText() { Parent = parent }; | 
				
			||||
			 | 
				
			||||
			ws.StartOffset = currentLocation; | 
				
			||||
			int start = currentLocation; | 
				
			||||
			while(IsWhiteSpace() == true) currentLocation++; | 
				
			||||
			ws.Value = GetText(start, currentLocation); | 
				
			||||
			ws.EndOffset = currentLocation; | 
				
			||||
			 | 
				
			||||
			parsedDom.Add(ws); | 
				
			||||
			return ws; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		RawAttribute ReadAttribulte(RawObject parent) | 
				
			||||
		{ | 
				
			||||
			Debug.Assert(HasMoreData()); | 
				
			||||
			 | 
				
			||||
			RawAttribute attr = ReadFromCache<RawAttribute>(currentLocation); | 
				
			||||
			if (attr != null) return attr; | 
				
			||||
			 | 
				
			||||
			attr = new RawAttribute() { Parent = parent }; | 
				
			||||
			 | 
				
			||||
			attr.StartOffset = currentLocation; | 
				
			||||
			if (HasMoreData()) { | 
				
			||||
				attr.Name = ReadName(); | 
				
			||||
			} | 
				
			||||
			int checkpoint = currentLocation; | 
				
			||||
			attr.EqualsSign = string.Empty;  | 
				
			||||
			if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value; | 
				
			||||
			if (TryRead('=')) { | 
				
			||||
				attr.EqualsSign += "="; | 
				
			||||
				if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value; | 
				
			||||
				if (IsWhiteSpaceOrReserved() == false) { | 
				
			||||
					// Read attribute value
 | 
				
			||||
					int start = currentLocation; | 
				
			||||
					if (TryRead('"')) { | 
				
			||||
						TryMoveTo('"', '<'); | 
				
			||||
						TryRead('"'); | 
				
			||||
						attr.Value = GetText(start, currentLocation); | 
				
			||||
					} else if (TryRead('\'')) { | 
				
			||||
						TryMoveTo('\'', '<'); | 
				
			||||
						TryRead('\''); | 
				
			||||
						attr.Value = GetText(start, currentLocation); | 
				
			||||
					} else { | 
				
			||||
						attr.Value = ReadName(); | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
			} else { | 
				
			||||
				attr.EqualsSign = null; | 
				
			||||
				currentLocation = checkpoint; | 
				
			||||
			} | 
				
			||||
			attr.EndOffset = currentLocation; | 
				
			||||
			 | 
				
			||||
			parsedDom.Add(attr); | 
				
			||||
			return attr; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		RawText ReadCharacterData(RawObject parent) | 
				
			||||
		{ | 
				
			||||
			Debug.Assert(HasMoreData()); | 
				
			||||
			 | 
				
			||||
			RawText charData = ReadFromCache<RawText>(currentLocation); | 
				
			||||
			if (charData != null) return charData; | 
				
			||||
			 | 
				
			||||
			charData = new RawText() { Parent = parent }; | 
				
			||||
			 | 
				
			||||
			charData.StartOffset = currentLocation; | 
				
			||||
			int start = currentLocation; | 
				
			||||
			TryMoveTo('<'); | 
				
			||||
			charData.Value = GetText(start, currentLocation); | 
				
			||||
			charData.EndOffset = currentLocation; | 
				
			||||
			 | 
				
			||||
			parsedDom.Add(charData); | 
				
			||||
			return charData; | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue