Browse Source

XML Parser: Visitor and PrettyPrinter

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4605 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 16 years ago
parent
commit
767e88579e
  1. 13
      samples/XmlDOM/Window1.xaml.cs
  2. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  3. 27
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs
  4. 121
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs
  5. 45
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

13
samples/XmlDOM/Window1.xaml.cs

@ -28,6 +28,8 @@ namespace XmlDOM @@ -28,6 +28,8 @@ namespace XmlDOM
{
XmlParser parser;
bool textDirty = true;
public Window1()
{
InitializeComponent();
@ -35,10 +37,11 @@ namespace XmlDOM @@ -35,10 +37,11 @@ namespace XmlDOM
protected override void OnInitialized(EventArgs e)
{
editor.Document.Changed += delegate { textDirty = true; };
parser = new XmlParser(editor.Document);
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(0.25);
timer.Interval = TimeSpan.FromSeconds(0.5);
timer.Tick += delegate { Button_Click(null, null); };
timer.Start();
@ -47,10 +50,18 @@ namespace XmlDOM @@ -47,10 +50,18 @@ namespace XmlDOM
void Button_Click(object sender, RoutedEventArgs e)
{
if (!textDirty) return;
RawDocument doc = parser.Parse();
if (treeView.Items.Count == 0) {
treeView.Items.Add(doc);
}
PrettyPrintXmlVisitor visitor = new PrettyPrintXmlVisitor();
visitor.VisitDocument(doc);
string prettyPrintedText = visitor.Output;
if (prettyPrintedText != editor.Document.Text) {
MessageBox.Show("Original and pretty printer version of XML differ");
}
textDirty = false;
}
void BindObject(object sender, EventArgs e)

1
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -310,6 +310,7 @@ @@ -310,6 +310,7 @@
<CodeAnalysisDictionary Include="Properties\CodeAnalysisDictionary.xml" />
<Compile Include="XmlParser\Collections.cs" />
<Compile Include="XmlParser\RawObjects.cs" />
<Compile Include="XmlParser\Visitors.cs" />
<Compile Include="XmlParser\XmlParser.cs" />
<Resource Include="themes\RightArrow.cur" />
<EmbeddedResource Include="Highlighting\Resources\ASPX.xshd" />

27
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs

@ -79,6 +79,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -79,6 +79,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return new RawObject[] { this };
}
public abstract void AcceptVisitor(IXmlVisitor visitor);
public virtual void UpdateDataFrom(RawObject source)
{
this.ReadCallID = source.ReadCallID;
@ -324,6 +326,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -324,6 +326,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (ObjectDettached != null) ObjectDettached(this, new RawObjectEventArgs() { Object = obj } );
}
public override void AcceptVisitor(IXmlVisitor visitor)
{
visitor.VisitDocument(this);
}
XDocument xDoc;
public XDocument GetXDocument()
@ -378,6 +385,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -378,6 +385,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public bool IsDocumentType { get { return DTDNames.Contains(OpeningBracket); } }
public bool IsUnknownBang { get { return OpeningBracket == "<!"; } }
public override void AcceptVisitor(IXmlVisitor visitor)
{
visitor.VisitTag(this);
}
public override void UpdateDataFrom(RawObject source)
{
if (this.ReadCallID == source.ReadCallID) return;
@ -424,6 +436,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -424,6 +436,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
}
}
public override void AcceptVisitor(IXmlVisitor visitor)
{
visitor.VisitElement(this);
}
XElement xElem;
public XElement GetXElement()
@ -500,6 +517,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -500,6 +517,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// TODO: Provide method to dereference Value - &
public override void AcceptVisitor(IXmlVisitor visitor)
{
visitor.VisitAttribute(this);
}
public override void UpdateDataFrom(RawObject source)
{
if (this.ReadCallID == source.ReadCallID) return;
@ -584,6 +606,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -584,6 +606,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public RawTextType Type { get; set; }
public string Value { get; set; }
public override void AcceptVisitor(IXmlVisitor visitor)
{
visitor.VisitText(this);
}
public override void UpdateDataFrom(RawObject source)
{
if (this.ReadCallID == source.ReadCallID) return;

121
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
// <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.Text;
namespace ICSharpCode.AvalonEdit.XmlParser
{
/// <summary>
/// Visitor for the XML tree
/// </summary>
public interface IXmlVisitor
{
/// <summary> Visit RawDocument </summary>
void VisitDocument(RawDocument document);
/// <summary> Visit RawElement </summary>
void VisitElement(RawElement element);
/// <summary> Visit RawTag </summary>
void VisitTag(RawTag tag);
/// <summary> Visit RawAttribute </summary>
void VisitAttribute(RawAttribute attribute);
/// <summary> Visit RawText </summary>
void VisitText(RawText text);
}
/// <summary>
/// Derive from this class to create visitor for the XML tree
/// </summary>
public abstract class AbstractXmlVisitor : IXmlVisitor
{
/// <summary> Visit RawDocument </summary>
public virtual void VisitDocument(RawDocument document)
{
foreach(RawObject child in document.Children) child.AcceptVisitor(this);
}
/// <summary> Visit RawElement </summary>
public virtual void VisitElement(RawElement element)
{
foreach(RawObject child in element.Children) child.AcceptVisitor(this);
}
/// <summary> Visit RawTag </summary>
public virtual void VisitTag(RawTag tag)
{
foreach(RawObject child in tag.Children) child.AcceptVisitor(this);
}
/// <summary> Visit RawAttribute </summary>
public virtual void VisitAttribute(RawAttribute attribute)
{
}
/// <summary> Visit RawText </summary>
public virtual void VisitText(RawText text)
{
}
}
/// <summary>
/// Converts the XML tree back to text.
/// The text should exactly match the original.
/// </summary>
public class PrettyPrintXmlVisitor: AbstractXmlVisitor
{
StringBuilder sb = new StringBuilder();
/// <summary>
/// Gets the pretty printed text
/// </summary>
public string Output {
get {
return sb.ToString();
}
}
/// <summary> Visit RawDocument </summary>
public override void VisitDocument(RawDocument document)
{
base.VisitDocument(document);
}
/// <summary> Visit RawElement </summary>
public override void VisitElement(RawElement element)
{
base.VisitElement(element);
}
/// <summary> Visit RawTag </summary>
public override void VisitTag(RawTag tag)
{
sb.Append(tag.OpeningBracket);
sb.Append(tag.Name);
base.VisitTag(tag);
sb.Append(tag.ClosingBracket);
}
/// <summary> Visit RawAttribute </summary>
public override void VisitAttribute(RawAttribute attribute)
{
sb.Append(attribute.Name);
sb.Append(attribute.EqualsSign);
sb.Append(attribute.Value);
}
/// <summary> Visit RawText </summary>
public override void VisitText(RawText text)
{
sb.Append(text.Value);
}
}
}

45
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

@ -88,7 +88,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -88,7 +88,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// </remarks>
public class XmlParser
{
RawDocument userDocument = new RawDocument();
// TODO: Error reporting
// TODO: Trace touched memory
// TODO: Simple tag matching heuristic
// TODO: Simple attribute value closing heurisitc
// TODO: Backtracking for unclosed long Text sections
RawDocument userDocument;
XDocument userLinqDocument;
TextDocument textDocument;
TextSegmentCollection<RawObject> parsedItems = new TextSegmentCollection<RawObject>();
@ -97,9 +103,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -97,9 +103,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// <summary>
/// Create new parser, but do not parse the text yet.
/// </summary>
public XmlParser(TextDocument textDocument)
public XmlParser(string input)
{
this.input = input;
this.userDocument = new RawDocument();
this.userLinqDocument = userDocument.GetXDocument();
}
/// <summary>
/// Create new parser, but do not parse the text yet.
/// </summary>
public XmlParser(TextDocument textDocument): this(textDocument.Text)
{
this.textDocument = textDocument;
this.textDocument.Changed += delegate(object sender, DocumentChangeEventArgs e) {
changesSinceLastParse.Add(e);
@ -111,18 +126,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -111,18 +126,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// </summary>
public RawDocument Parse()
{
input = textDocument.Text;
readingEnd = input.Length;
currentLocation = 0;
// Update source text
if (textDocument != null) {
input = textDocument.Text;
}
// Use chages to invalidate cache
foreach(DocumentChangeEventArgs change in changesSinceLastParse) {
// Update offsets of all items
parsedItems.UpdateOffsets(change);
// Remove any items affected by the change
int start = change.Offset - 2;
int end = change.Offset + change.InsertionLength + 2;
start = Math.Max(Math.Min(start, textDocument.TextLength - 1), 0);
end = Math.Max(Math.Min(end, textDocument.TextLength - 1), 0);
int start = Math.Max(change.Offset - 2, 0);
int end = Math.Max(change.Offset + change.InsertionLength + 2, 0);
foreach(RawObject obj in parsedItems.FindOverlappingSegments(start, end - start)) {
parsedItems.Remove(obj);
Log("Removed cached item {0}", obj);
@ -130,6 +145,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -130,6 +145,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser
}
changesSinceLastParse.Clear();
currentLocation = 0;
readingEnd = input.Length;
RawDocument parsedDocument = ReadDocument();
// Just in case parse method was called redundantly
if (parsedDocument.ReadCallID != userDocument.ReadCallID) {
@ -357,8 +375,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -357,8 +375,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// </summary>
bool TryReadName(out string res)
{
AssertHasMoreData();
int start = currentLocation;
TryMoveToAnyOf(WhiteSpaceAndReservedChars.ToArray());
if (start == currentLocation) {
@ -433,8 +449,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -433,8 +449,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser
break;
} else if (TryPeek('<')) {
RawObject content = ReadElementOrTag();
if (content is RawTag && ((RawTag)content).IsEndTag) break;
element.AddChild(content);
if (content is RawTag && ((RawTag)content).IsEndTag) break;
} else {
element.AddChildren(ReadText(RawTextType.CharacterData));
}
@ -479,7 +495,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -479,7 +495,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser
while(true) {
// Chech for all forbiden 'name' charcters first - see ReadName
if (IsEndOfFile()) break;
if (TryPeekWhiteSpace()) tag.AddChildren(ReadText(RawTextType.WhiteSpace));
if (TryPeekWhiteSpace()) {
tag.AddChildren(ReadText(RawTextType.WhiteSpace));
continue; // End of file might be next
}
if (TryPeek('<')) break;
if (TryPeek('>') || TryPeek('/') || TryPeek('?')) break; // End tag

Loading…
Cancel
Save