|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using System.Xml;
namespace System.ServiceModel.Syndication
{
public delegate bool TryParseDateTimeCallback(XmlDateTimeData data, out DateTimeOffset dateTimeOffset);
public delegate bool TryParseUriCallback(XmlUriData data, out Uri uri);
[DataContract]
public abstract class SyndicationFeedFormatter
{
private SyndicationFeed _feed;
protected SyndicationFeedFormatter()
{
_feed = null;
DateTimeParser = GetDefaultDateTimeParser();
}
protected SyndicationFeedFormatter(SyndicationFeed feedToWrite)
{
if (feedToWrite is null)
{
throw new ArgumentNullException(nameof(feedToWrite));
}
_feed = feedToWrite;
DateTimeParser = GetDefaultDateTimeParser();
}
public SyndicationFeed Feed => _feed;
public TryParseUriCallback UriParser { get; set; } = DefaultUriParser;
// Different DateTimeParsers are needed for Atom and Rss so can't set inline
public TryParseDateTimeCallback DateTimeParser { get; set; }
internal virtual TryParseDateTimeCallback GetDefaultDateTimeParser() => NotImplementedDateTimeParser;
private bool NotImplementedDateTimeParser(XmlDateTimeData XmlDateTimeData, out DateTimeOffset dateTimeOffset)
{
dateTimeOffset = default;
return false;
}
public abstract string Version { get; }
public abstract bool CanRead(XmlReader reader);
public abstract void ReadFrom(XmlReader reader);
public override string ToString() => $"{GetType()}, SyndicationVersion={Version}";
public abstract void WriteTo(XmlWriter writer);
protected internal static SyndicationCategory CreateCategory(SyndicationFeed feed)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
return GetNonNullValue(feed.CreateCategory(), SR.FeedCreatedNullCategory);
}
protected internal static SyndicationCategory CreateCategory(SyndicationItem item)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
return GetNonNullValue(item.CreateCategory(), SR.ItemCreatedNullCategory);
}
protected internal static SyndicationItem CreateItem(SyndicationFeed feed)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
return GetNonNullValue(feed.CreateItem(), SR.FeedCreatedNullItem);
}
protected internal static SyndicationLink CreateLink(SyndicationFeed feed)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
return GetNonNullValue(feed.CreateLink(), SR.FeedCreatedNullPerson);
}
protected internal static SyndicationLink CreateLink(SyndicationItem item)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
return GetNonNullValue(item.CreateLink(), SR.ItemCreatedNullPerson);
}
protected internal static SyndicationPerson CreatePerson(SyndicationFeed feed)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
return GetNonNullValue(feed.CreatePerson(), SR.FeedCreatedNullPerson);
}
protected internal static SyndicationPerson CreatePerson(SyndicationItem item)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
return GetNonNullValue(item.CreatePerson(), SR.ItemCreatedNullPerson);
}
protected internal static void LoadElementExtensions(XmlReader reader, SyndicationFeed feed, int maxExtensionSize)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
feed.LoadElementExtensions(reader, maxExtensionSize);
}
protected internal static void LoadElementExtensions(XmlReader reader, SyndicationItem item, int maxExtensionSize)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
item.LoadElementExtensions(reader, maxExtensionSize);
}
protected internal static void LoadElementExtensions(XmlReader reader, SyndicationCategory category, int maxExtensionSize)
{
if (category is null)
{
throw new ArgumentNullException(nameof(category));
}
category.LoadElementExtensions(reader, maxExtensionSize);
}
protected internal static void LoadElementExtensions(XmlReader reader, SyndicationLink link, int maxExtensionSize)
{
if (link is null)
{
throw new ArgumentNullException(nameof(link));
}
link.LoadElementExtensions(reader, maxExtensionSize);
}
protected internal static void LoadElementExtensions(XmlReader reader, SyndicationPerson person, int maxExtensionSize)
{
if (person is null)
{
throw new ArgumentNullException(nameof(person));
}
person.LoadElementExtensions(reader, maxExtensionSize);
}
protected internal static bool TryParseAttribute(string name, string ns, string value, SyndicationFeed feed, string version)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
if (FeedUtils.IsXmlns(name, ns))
{
return true;
}
return feed.TryParseAttribute(name, ns, value, version);
}
protected internal static bool TryParseAttribute(string name, string ns, string value, SyndicationItem item, string version)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
if (FeedUtils.IsXmlns(name, ns))
{
return true;
}
return item.TryParseAttribute(name, ns, value, version);
}
protected internal static bool TryParseAttribute(string name, string ns, string value, SyndicationCategory category, string version)
{
if (category is null)
{
throw new ArgumentNullException(nameof(category));
}
if (FeedUtils.IsXmlns(name, ns))
{
return true;
}
return category.TryParseAttribute(name, ns, value, version);
}
protected internal static bool TryParseAttribute(string name, string ns, string value, SyndicationLink link, string version)
{
if (link is null)
{
throw new ArgumentNullException(nameof(link));
}
if (FeedUtils.IsXmlns(name, ns))
{
return true;
}
return link.TryParseAttribute(name, ns, value, version);
}
protected internal static bool TryParseAttribute(string name, string ns, string value, SyndicationPerson person, string version)
{
if (person is null)
{
throw new ArgumentNullException(nameof(person));
}
if (FeedUtils.IsXmlns(name, ns))
{
return true;
}
return person.TryParseAttribute(name, ns, value, version);
}
protected internal static bool TryParseContent(XmlReader reader, SyndicationItem item, string contentType, string version, out SyndicationContent content)
{
return item.TryParseContent(reader, contentType, version, out content);
}
protected internal static bool TryParseElement(XmlReader reader, SyndicationFeed feed, string version)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
return feed.TryParseElement(reader, version);
}
protected internal static bool TryParseElement(XmlReader reader, SyndicationItem item, string version)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
return item.TryParseElement(reader, version);
}
protected internal static bool TryParseElement(XmlReader reader, SyndicationCategory category, string version)
{
if (category is null)
{
throw new ArgumentNullException(nameof(category));
}
return category.TryParseElement(reader, version);
}
protected internal static bool TryParseElement(XmlReader reader, SyndicationLink link, string version)
{
if (link is null)
{
throw new ArgumentNullException(nameof(link));
}
return link.TryParseElement(reader, version);
}
protected internal static bool TryParseElement(XmlReader reader, SyndicationPerson person, string version)
{
if (person is null)
{
throw new ArgumentNullException(nameof(person));
}
return person.TryParseElement(reader, version);
}
protected internal static void WriteAttributeExtensions(XmlWriter writer, SyndicationFeed feed, string version)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
feed.WriteAttributeExtensions(writer, version);
}
protected internal static void WriteAttributeExtensions(XmlWriter writer, SyndicationItem item, string version)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
item.WriteAttributeExtensions(writer, version);
}
protected internal static void WriteAttributeExtensions(XmlWriter writer, SyndicationCategory category, string version)
{
if (category is null)
{
throw new ArgumentNullException(nameof(category));
}
category.WriteAttributeExtensions(writer, version);
}
protected internal static void WriteAttributeExtensions(XmlWriter writer, SyndicationLink link, string version)
{
if (link is null)
{
throw new ArgumentNullException(nameof(link));
}
link.WriteAttributeExtensions(writer, version);
}
protected internal static void WriteAttributeExtensions(XmlWriter writer, SyndicationPerson person, string version)
{
if (person is null)
{
throw new ArgumentNullException(nameof(person));
}
person.WriteAttributeExtensions(writer, version);
}
protected internal static void WriteElementExtensions(XmlWriter writer, SyndicationFeed feed, string version)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
feed.WriteElementExtensions(writer, version);
}
protected internal static void WriteElementExtensions(XmlWriter writer, SyndicationItem item, string version)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
item.WriteElementExtensions(writer, version);
}
protected internal static void WriteElementExtensions(XmlWriter writer, SyndicationCategory category, string version)
{
if (category is null)
{
throw new ArgumentNullException(nameof(category));
}
category.WriteElementExtensions(writer, version);
}
protected internal static void WriteElementExtensions(XmlWriter writer, SyndicationLink link, string version)
{
if (link is null)
{
throw new ArgumentNullException(nameof(link));
}
link.WriteElementExtensions(writer, version);
}
protected internal static void WriteElementExtensions(XmlWriter writer, SyndicationPerson person, string version)
{
if (person is null)
{
throw new ArgumentNullException(nameof(person));
}
person.WriteElementExtensions(writer, version);
}
protected internal virtual void SetFeed(SyndicationFeed feed)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
_feed = feed;
}
internal Uri UriFromString(string uriString, UriKind uriKind, string localName, string namespaceURI, XmlReader reader)
{
return UriFromString(UriParser, uriString, uriKind, localName, namespaceURI, reader);
}
internal static Uri UriFromString(TryParseUriCallback uriParser, string uriString, UriKind uriKind, string localName, string namespaceURI, XmlReader reader)
{
Uri uri = null;
var elementQualifiedName = new XmlQualifiedName(localName, namespaceURI);
var xmlUriData = new XmlUriData(uriString, uriKind, elementQualifiedName);
object[] args = new object[] { xmlUriData, uri };
try
{
foreach (Delegate parser in uriParser.GetInvocationList())
{
if ((bool)parser.Method.Invoke(parser.Target, args))
{
uri = (Uri)args[args.Length - 1];
return uri;
}
}
}
catch (Exception e)
{
throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingUri), e);
}
DefaultUriParser(xmlUriData, out uri);
return uri;
}
internal DateTimeOffset DateFromString(string dateTimeString, XmlReader reader)
{
try
{
DateTimeOffset dateTimeOffset = default;
var elementQualifiedName = new XmlQualifiedName(reader.LocalName, reader.NamespaceURI);
var xmlDateTimeData = new XmlDateTimeData(dateTimeString, elementQualifiedName);
object[] args = new object[] { xmlDateTimeData, dateTimeOffset };
foreach (Delegate dateTimeParser in DateTimeParser.GetInvocationList())
{
if ((bool)dateTimeParser.Method.Invoke(dateTimeParser.Target, args))
{
dateTimeOffset = (DateTimeOffset)args[args.Length - 1];
return dateTimeOffset;
}
}
}
catch (Exception e)
{
throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDateTime), e);
}
throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDateTime));
}
internal static bool DefaultUriParser(XmlUriData XmlUriData, out Uri uri)
{
uri = new Uri(XmlUriData.UriString, XmlUriData.UriKind);
return true;
}
internal static void CloseBuffer(XmlBuffer buffer, XmlDictionaryWriter extWriter)
{
if (buffer == null)
{
return;
}
extWriter.WriteEndElement();
buffer.CloseSection();
buffer.Close();
}
internal static void CreateBufferIfRequiredAndWriteNode(ref XmlBuffer buffer, ref XmlDictionaryWriter extWriter, XmlReader reader, int maxExtensionSize)
{
if (buffer == null)
{
buffer = new XmlBuffer(maxExtensionSize);
extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max);
extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag);
}
extWriter.WriteNode(reader, false);
}
internal static SyndicationFeed CreateFeedInstance(Type feedType)
{
if (feedType.Equals(typeof(SyndicationFeed)))
{
return new SyndicationFeed();
}
else
{
return (SyndicationFeed)Activator.CreateInstance(feedType);
}
}
internal static void LoadElementExtensions(XmlBuffer buffer, XmlDictionaryWriter writer, SyndicationFeed feed)
{
if (feed is null)
{
throw new ArgumentNullException(nameof(feed));
}
CloseBuffer(buffer, writer);
feed.LoadElementExtensions(buffer);
}
internal static void LoadElementExtensions(XmlBuffer buffer, XmlDictionaryWriter writer, SyndicationItem item)
{
Debug.Assert(item != null);
CloseBuffer(buffer, writer);
item.LoadElementExtensions(buffer);
}
internal static void LoadElementExtensions(XmlBuffer buffer, XmlDictionaryWriter writer, SyndicationCategory category)
{
Debug.Assert(category != null);
CloseBuffer(buffer, writer);
category.LoadElementExtensions(buffer);
}
internal static void LoadElementExtensions(XmlBuffer buffer, XmlDictionaryWriter writer, SyndicationLink link)
{
Debug.Assert(link != null);
CloseBuffer(buffer, writer);
link.LoadElementExtensions(buffer);
}
internal static void LoadElementExtensions(XmlBuffer buffer, XmlDictionaryWriter writer, SyndicationPerson person)
{
Debug.Assert(person != null);
CloseBuffer(buffer, writer);
person.LoadElementExtensions(buffer);
}
internal static void MoveToStartElement(XmlReader reader)
{
Debug.Assert(reader != null, "reader != null");
if (!reader.IsStartElement())
{
XmlExceptionHelper.ThrowStartElementExpected(XmlDictionaryReader.CreateDictionaryReader(reader));
}
}
protected abstract SyndicationFeed CreateFeedInstance();
private static T GetNonNullValue<T>(T value, string errorMsg)
{
if (value == null)
{
throw new InvalidOperationException(errorMsg);
}
return value;
}
private static class XmlExceptionHelper
{
private static void ThrowXmlException(XmlDictionaryReader reader, string res, string arg1)
{
string s = SR.Format(res, arg1);
if (reader is IXmlLineInfo lineInfo && lineInfo.HasLineInfo())
{
s += " " + SR.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition);
}
throw new XmlException(s);
}
private static string GetName(string prefix, string localName)
{
if (prefix.Length == 0)
return localName;
else
return string.Concat(prefix, ":", localName);
}
private static string GetWhatWasFound(XmlDictionaryReader reader)
{
if (reader.EOF)
return SR.XmlFoundEndOfFile;
switch (reader.NodeType)
{
case XmlNodeType.Element:
return SR.Format(SR.XmlFoundElement, GetName(reader.Prefix, reader.LocalName), reader.NamespaceURI);
case XmlNodeType.EndElement:
return SR.Format(SR.XmlFoundEndElement, GetName(reader.Prefix, reader.LocalName), reader.NamespaceURI);
case XmlNodeType.Text:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
return SR.Format(SR.XmlFoundText, reader.Value);
case XmlNodeType.Comment:
return SR.Format(SR.XmlFoundComment, reader.Value);
case XmlNodeType.CDATA:
return SR.Format(SR.XmlFoundCData, reader.Value);
}
return SR.Format(SR.XmlFoundNodeType, reader.NodeType);
}
public static void ThrowStartElementExpected(XmlDictionaryReader reader)
{
ThrowXmlException(reader, SR.XmlStartElementExpected, GetWhatWasFound(reader));
}
}
}
}
|