Skip to content

Commit

Permalink
*3.1.7*
Browse files Browse the repository at this point in the history
____
- Minor improvements.
- Renamed `DefaultXmppParser` -> `XmppStreamParser`
- Added helper methods for fast loading XML from string and streams.
- Improvements to help detect when parser really completed parsing.
- Added missing TimeSpan parser in `TryParseHelpers`.
  • Loading branch information
nathan130200 committed May 11, 2024
1 parent 33a5410 commit da1838e
Show file tree
Hide file tree
Showing 14 changed files with 447 additions and 521 deletions.
103 changes: 38 additions & 65 deletions XmppSharp.Expat/ExpatXmppParser.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
using System.Xml;
using Expat;
using XmppSharp.Dom;
using XmppSharp.Expat;
using XmppSharp.Exceptions;
using XmppSharp.Factory;
using XmppSharp.Protocol.Base;

namespace XmppSharp.Parsers;
namespace XmppSharp.Parser;

/// <summary>
/// An enhanced XMPP parser built using Expat library.
/// </summary>
public partial class ExpatXmppParser : BaseXmppParser
{
private Parser _parser;
private ExpatParser _parser;
private Element _currentElem;
private XmlNamespaceManager _nsStack;
private NameTable _xmlNames;
Expand All @@ -33,37 +32,37 @@ void AddNamespacesToScope(IReadOnlyDictionary<string, string> attrs)
}
}

public ExpatXmppParser(ExpatEncodingType encoding = ExpatEncodingType.Utf8)
public ExpatXmppParser(EncodingType encoding = EncodingType.UTF8)
{
this._nsStack = new(this._xmlNames = new NameTable());

this._parser = new Parser(encoding);
this._parser = new ExpatParser(encoding);

this._parser.OnElementStart += e =>
this._parser.OnElementStart += (name, attributes) =>
{
this._nsStack.PushScope();

AddNamespacesToScope(e.Attributes);
AddNamespacesToScope(attributes);

var qname = Xml.ExtractQualifiedName(e.Name);
var qname = Xml.ExtractQualifiedName(name);

var ns = this._nsStack.LookupNamespace(qname.HasPrefix ? qname.Prefix : string.Empty);

if (e.Name is "iq" or "message" or "presence") // work-around
if (name is "iq" or "message" or "presence") // work-around
ns ??= Namespace.Client;

var element = ElementFactory.Create(e.Name, ns);
var element = ElementFactory.Create(name, ns);

//foreach (var (key, value) in _nsStack.GetNamespacesInScope(XmlNamespaceScope.Local))
//{
// var att = string.IsNullOrWhiteSpace(key) ? "xmlns" : $"xmlns:{key}";
// element.SetAttribute(att, value);
//}

foreach (var (key, value) in e.Attributes)
foreach (var (key, value) in attributes)
element.SetAttribute(key, value);

if (e.Name == "stream:stream")
if (name == "stream:stream")
AsyncHelper.RunSync(() => FireStreamStart(element as StreamStream));
else
{
Expand All @@ -72,11 +71,11 @@ public ExpatXmppParser(ExpatEncodingType encoding = ExpatEncodingType.Utf8)
}
};

this._parser.OnElementEnd += e =>
this._parser.OnElementEnd += (name) =>
{
this._nsStack.PopScope();

if (e.Value == "stream:stream")
if (name == "stream:stream")
AsyncHelper.RunSync(() => FireStreamEnd());
else
{
Expand All @@ -86,10 +85,10 @@ public ExpatXmppParser(ExpatEncodingType encoding = ExpatEncodingType.Utf8)
AsyncHelper.RunSync(() => FireStreamElement(_currentElem));
else
{
if (e.Value != _currentElem.TagName)
if (name != _currentElem.TagName)
{
var ex = new JabberStreamException(StreamErrorCondition.InvalidXml, "Parent end tag mismatch.");
ex.Data.Add("Actual", e.Value);
ex.Data.Add("Actual", name);
ex.Data.Add("Expected", _currentElem.TagName);
throw ex;
}
Expand All @@ -99,36 +98,34 @@ public ExpatXmppParser(ExpatEncodingType encoding = ExpatEncodingType.Utf8)
}
};

this._parser.OnText += e =>
this._parser.OnText += (type, text) =>
{
if (_currentElem != null)
if (_currentElem == null)
return;

if (type == ContentNodeType.Text)
{
var trimWS = _currentElem.GetAttribute("xml:space") != "preserve";
var trimWhitespace = _currentElem.GetAttribute("xml:space") != "preserve";

// skip whitespace if not explicit declared.
if (string.IsNullOrWhiteSpace(e.Value) && trimWS)
if (trimWhitespace && text.All(XmlConvert.IsWhitespaceChar))
return;

var val = e.Value;

if (trimWS) // same for trailing whitespace
val = TrimWhitespace(val);
if (trimWhitespace) // same for trailing whitespace
text = TrimWhitespace(text);

if (_currentElem.LastNode is Text text)
text.Value += val;
if (_currentElem.LastNode is Text node)
node.Value += text;
else
_currentElem.AddChild(new Text(val));
_currentElem.AddChild(new Text(text));
}
else if (type == ContentNodeType.Cdata)
{
this._currentElem.AddChild(new Cdata(text));
}
else if (type == ContentNodeType.Comment)
{
this._currentElem.AddChild(new Comment(text));
}
};

this._parser.OnCdata += e =>
{
this._currentElem?.AddChild(new Cdata(e.Value));
};

this._parser.OnComment += e =>
{
this._currentElem?.AddChild(new Comment(e.Value));
};
}

Expand Down Expand Up @@ -165,34 +162,10 @@ public void Reset()
this._parser.Reset();
}

public void Write(byte[] buffer, int offset, int length, bool isFinalBlock = false)
{
this.EnsureNotDisposed();

byte[] temp;

try
{
temp = GC.AllocateUninitializedArray<byte>(length, true);
Buffer.BlockCopy(buffer, offset, temp, 0, length);
this._parser.Feed(temp, length, isFinalBlock);
}
finally
{
temp = null;
}
}

public void Write(byte[] buffer, int length, bool isFinalBlock = false)
{
this.EnsureNotDisposed();
this._parser.Feed(buffer, length, isFinalBlock);
}

public void Write(byte[] buffer, bool isFinalBlock = false)
{
this.EnsureNotDisposed();
this._parser.Feed(buffer, buffer.Length, isFinalBlock);
//this._parser.WriteBuffer(buffer, length, isFinalBlock);
}

protected override void Disposing()
Expand Down
Loading

0 comments on commit da1838e

Please sign in to comment.