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 @@ |
|||||||
|
<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 @@ |
|||||||
|
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 @@ |
|||||||
|
#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 @@ |
|||||||
|
#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 @@ |
|||||||
|
<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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
|
||||||
|
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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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