File: System\ServiceModel\Syndication\Atom10FeedFormatter.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Syndication
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Diagnostics.CodeAnalysis;
    using System.Xml.Schema;
    using System.Collections;
    using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
    using System.ServiceModel.Channels;
    using System.Runtime.CompilerServices;
 
    [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
    [XmlRoot(ElementName = Atom10Constants.FeedTag, Namespace = Atom10Constants.Atom10Namespace)]
    public class Atom10FeedFormatter : SyndicationFeedFormatter, IXmlSerializable
    {
        internal static readonly TimeSpan zeroOffset = new TimeSpan(0, 0, 0);
        internal const string XmlNs = "http://www.w3.org/XML/1998/namespace";
        internal const string XmlNsNs = "http://www.w3.org/2000/xmlns/";
        static readonly XmlQualifiedName Atom10Href = new XmlQualifiedName(Atom10Constants.HrefTag, string.Empty);
        static readonly XmlQualifiedName Atom10Label = new XmlQualifiedName(Atom10Constants.LabelTag, string.Empty);
        static readonly XmlQualifiedName Atom10Length = new XmlQualifiedName(Atom10Constants.LengthTag, string.Empty);
        static readonly XmlQualifiedName Atom10Relative = new XmlQualifiedName(Atom10Constants.RelativeTag, string.Empty);
        static readonly XmlQualifiedName Atom10Scheme = new XmlQualifiedName(Atom10Constants.SchemeTag, string.Empty);
        static readonly XmlQualifiedName Atom10Term = new XmlQualifiedName(Atom10Constants.TermTag, string.Empty);
        static readonly XmlQualifiedName Atom10Title = new XmlQualifiedName(Atom10Constants.TitleTag, string.Empty);
        static readonly XmlQualifiedName Atom10Type = new XmlQualifiedName(Atom10Constants.TypeTag, string.Empty);
        static readonly UriGenerator idGenerator = new UriGenerator();
        const string Rfc3339LocalDateTimeFormat = "yyyy-MM-ddTHH:mm:sszzz";
        const string Rfc3339UTCDateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ";
        Type feedType;
        int maxExtensionSize;
        bool preserveAttributeExtensions;
        bool preserveElementExtensions;
 
        public Atom10FeedFormatter()
            : this(typeof(SyndicationFeed))
        {
        }
 
        public Atom10FeedFormatter(Type feedTypeToCreate)
            : base()
        {
            if (feedTypeToCreate == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feedTypeToCreate");
            }
            if (!typeof(SyndicationFeed).IsAssignableFrom(feedTypeToCreate))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("feedTypeToCreate",
                    SR.GetString(SR.InvalidObjectTypePassed, "feedTypeToCreate", "SyndicationFeed"));
            }
            this.maxExtensionSize = int.MaxValue;
            this.preserveAttributeExtensions = this.preserveElementExtensions = true;
            this.feedType = feedTypeToCreate;
        }
 
        public Atom10FeedFormatter(SyndicationFeed feedToWrite)
            : base(feedToWrite)
        {
            // No need to check that the parameter passed is valid - it is checked by the c'tor of the base class
            this.maxExtensionSize = int.MaxValue;
            preserveAttributeExtensions = preserveElementExtensions = true;
            this.feedType = feedToWrite.GetType();
        }
 
        public bool PreserveAttributeExtensions
        {
            get { return this.preserveAttributeExtensions; }
            set { this.preserveAttributeExtensions = value; }
        }
 
        public bool PreserveElementExtensions
        {
            get { return this.preserveElementExtensions; }
            set { this.preserveElementExtensions = value; }
        }
 
        public override string Version
        {
            get { return SyndicationVersions.Atom10; }
        }
 
        protected Type FeedType
        {
            get
            {
                return this.feedType;
            }
        }
 
        public override bool CanRead(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            return reader.IsStartElement(Atom10Constants.FeedTag, Atom10Constants.Atom10Namespace);
        }
 
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")]
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }
 
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")]
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            TraceFeedReadBegin();
            ReadFeed(reader);
            TraceFeedReadEnd();
        }
 
        [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")]
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
            TraceFeedWriteBegin();
            WriteFeed(writer);
            TraceFeedWriteEnd();
        }
 
        public override void ReadFrom(XmlReader reader)
        {
            TraceFeedReadBegin();
            if (!CanRead(reader))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI)));
            }
            ReadFeed(reader);
            TraceFeedReadEnd();
        }
 
        public override void WriteTo(XmlWriter writer)
        {
            if (writer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
            }
            TraceFeedWriteBegin();
            writer.WriteStartElement(Atom10Constants.FeedTag, Atom10Constants.Atom10Namespace);
            WriteFeed(writer);
            writer.WriteEndElement();
            TraceFeedWriteEnd();
        }
 
        internal static void ReadCategory(XmlReader reader, SyndicationCategory category, string version, bool preserveAttributeExtensions, bool preserveElementExtensions, int maxExtensionSize)
        {
            MoveToStartElement(reader);
            bool isEmpty = reader.IsEmptyElement;
            if (reader.HasAttributes)
            {
                while (reader.MoveToNextAttribute())
                {
                    if (reader.LocalName == Atom10Constants.TermTag && reader.NamespaceURI == string.Empty)
                    {
                        category.Name = reader.Value;
                    }
                    else if (reader.LocalName == Atom10Constants.SchemeTag && reader.NamespaceURI == string.Empty)
                    {
                        category.Scheme = reader.Value;
                    }
                    else if (reader.LocalName == Atom10Constants.LabelTag && reader.NamespaceURI == string.Empty)
                    {
                        category.Label = reader.Value;
                    }
                    else
                    {
                        string ns = reader.NamespaceURI;
                        string name = reader.LocalName;
                        if (FeedUtils.IsXmlns(name, ns))
                        {
                            continue;
                        }
                        string val = reader.Value;
                        if (!TryParseAttribute(name, ns, val, category, version))
 
                        {
                            if (preserveAttributeExtensions)
                            {
                                category.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val);
                            }
                            else
                            {
                                TraceSyndicationElementIgnoredOnRead(reader);
                            }
                        }
                    }
                }
            }
 
            if (!isEmpty)
            {
                reader.ReadStartElement();
                XmlBuffer buffer = null;
                XmlDictionaryWriter extWriter = null;
                try
                {
                    while (reader.IsStartElement())
                    {
                        if (TryParseElement(reader, category, version))
                        {
                            continue;
                        }
                        else if (!preserveElementExtensions)
                        {
                            TraceSyndicationElementIgnoredOnRead(reader);
                            reader.Skip();
                        }
                        else
                        {
                            CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, maxExtensionSize);
                        }
                    }
                    LoadElementExtensions(buffer, extWriter, category);
                }
                finally
                {
                    if (extWriter != null)
                    {
                        ((IDisposable) extWriter).Dispose();
                    }
                }
                reader.ReadEndElement();
            }
            else
            {
                reader.ReadStartElement();
            }
        }
 
        internal static TextSyndicationContent ReadTextContentFrom(XmlReader reader, string context, bool preserveAttributeExtensions)
        {
            string type = reader.GetAttribute(Atom10Constants.TypeTag);
            return ReadTextContentFromHelper(reader, type, context, preserveAttributeExtensions);
        }
 
        internal static void WriteCategory(XmlWriter writer, SyndicationCategory category, string version)
        {
            writer.WriteStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace);
            WriteAttributeExtensions(writer, category, version);
            string categoryName = category.Name ?? string.Empty;
            if (!category.AttributeExtensions.ContainsKey(Atom10Term))
            {
                writer.WriteAttributeString(Atom10Constants.TermTag, categoryName);
            }
            if (!string.IsNullOrEmpty(category.Label) && !category.AttributeExtensions.ContainsKey(Atom10Label))
            {
                writer.WriteAttributeString(Atom10Constants.LabelTag, category.Label);
            }
            if (!string.IsNullOrEmpty(category.Scheme) && !category.AttributeExtensions.ContainsKey(Atom10Scheme))
            {
                writer.WriteAttributeString(Atom10Constants.SchemeTag, category.Scheme);
            }
            WriteElementExtensions(writer, category, version);
            writer.WriteEndElement();
        }
 
        internal void ReadItemFrom(XmlReader reader, SyndicationItem result)
        {
            ReadItemFrom(reader, result, null);
        }
 
        internal bool TryParseFeedElementFrom(XmlReader reader, SyndicationFeed result)
        {
            if (reader.IsStartElement(Atom10Constants.AuthorTag, Atom10Constants.Atom10Namespace))
            {
                result.Authors.Add(ReadPersonFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace))
            {
                result.Categories.Add(ReadCategoryFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.ContributorTag, Atom10Constants.Atom10Namespace))
            {
                result.Contributors.Add(ReadPersonFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.GeneratorTag, Atom10Constants.Atom10Namespace))
            {
                result.Generator = reader.ReadElementString();
            }
            else if (reader.IsStartElement(Atom10Constants.IdTag, Atom10Constants.Atom10Namespace))
            {
                result.Id = reader.ReadElementString();
            }
            else if (reader.IsStartElement(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace))
            {
                result.Links.Add(ReadLinkFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.LogoTag, Atom10Constants.Atom10Namespace))
            {
                result.ImageUrl = new Uri(reader.ReadElementString(), UriKind.RelativeOrAbsolute);
            }
            else if (reader.IsStartElement(Atom10Constants.RightsTag, Atom10Constants.Atom10Namespace))
            {
                result.Copyright = ReadTextContentFrom(reader, "//atom:feed/atom:rights[@type]");
            }
            else if (reader.IsStartElement(Atom10Constants.SubtitleTag, Atom10Constants.Atom10Namespace))
            {
                result.Description = ReadTextContentFrom(reader, "//atom:feed/atom:subtitle[@type]");
            }
            else if (reader.IsStartElement(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace))
            {
                result.Title = ReadTextContentFrom(reader, "//atom:feed/atom:title[@type]");
            }
            else if (reader.IsStartElement(Atom10Constants.UpdatedTag, Atom10Constants.Atom10Namespace))
            {
                reader.ReadStartElement();
                result.LastUpdatedTime = DateFromString(reader.ReadString(), reader);
                reader.ReadEndElement();
            }
            else
            {
                return false;
            }
            return true;
        }
 
        internal bool TryParseItemElementFrom(XmlReader reader, SyndicationItem result)
        {
            if (reader.IsStartElement(Atom10Constants.AuthorTag, Atom10Constants.Atom10Namespace))
            {
                result.Authors.Add(ReadPersonFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace))
            {
                result.Categories.Add(ReadCategoryFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.ContentTag, Atom10Constants.Atom10Namespace))
            {
                result.Content = ReadContentFrom(reader, result);
            }
            else if (reader.IsStartElement(Atom10Constants.ContributorTag, Atom10Constants.Atom10Namespace))
            {
                result.Contributors.Add(ReadPersonFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.IdTag, Atom10Constants.Atom10Namespace))
            {
                result.Id = reader.ReadElementString();
            }
            else if (reader.IsStartElement(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace))
            {
                result.Links.Add(ReadLinkFrom(reader, result));
            }
            else if (reader.IsStartElement(Atom10Constants.PublishedTag, Atom10Constants.Atom10Namespace))
            {
                reader.ReadStartElement();
                result.PublishDate = DateFromString(reader.ReadString(), reader);
                reader.ReadEndElement();
            }
            else if (reader.IsStartElement(Atom10Constants.RightsTag, Atom10Constants.Atom10Namespace))
            {
                result.Copyright = ReadTextContentFrom(reader, "//atom:feed/atom:entry/atom:rights[@type]");
            }
            else if (reader.IsStartElement(Atom10Constants.SourceFeedTag, Atom10Constants.Atom10Namespace))
            {
                reader.ReadStartElement();
                result.SourceFeed = ReadFeedFrom(reader, new SyndicationFeed(), true); //  isSourceFeed 
                reader.ReadEndElement();
            }
            else if (reader.IsStartElement(Atom10Constants.SummaryTag, Atom10Constants.Atom10Namespace))
            {
                result.Summary = ReadTextContentFrom(reader, "//atom:feed/atom:entry/atom:summary[@type]");
            }
            else if (reader.IsStartElement(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace))
            {
                result.Title = ReadTextContentFrom(reader, "//atom:feed/atom:entry/atom:title[@type]");
            }
            else if (reader.IsStartElement(Atom10Constants.UpdatedTag, Atom10Constants.Atom10Namespace))
            {
                reader.ReadStartElement();
                result.LastUpdatedTime = DateFromString(reader.ReadString(), reader);
                reader.ReadEndElement();
            }
            else
            {
                return false;
            }
            return true;
        }
 
        internal void WriteContentTo(XmlWriter writer, string elementName, SyndicationContent content)
        {
            if (content != null)
            {
                content.WriteTo(writer, elementName, Atom10Constants.Atom10Namespace);
            }
        }
 
        internal void WriteElement(XmlWriter writer, string elementName, string value)
        {
            if (value != null)
            {
                writer.WriteElementString(elementName, Atom10Constants.Atom10Namespace, value);
            }
        }
 
        internal void WriteFeedAuthorsTo(XmlWriter writer, Collection<SyndicationPerson> authors)
        {
            for (int i = 0; i < authors.Count; ++i)
            {
                SyndicationPerson p = authors[i];
                WritePersonTo(writer, p, Atom10Constants.AuthorTag);
            }
        }
 
        internal void WriteFeedContributorsTo(XmlWriter writer, Collection<SyndicationPerson> contributors)
        {
            for (int i = 0; i < contributors.Count; ++i)
            {
                SyndicationPerson p = contributors[i];
                WritePersonTo(writer, p, Atom10Constants.ContributorTag);
            }
        }
 
        internal void WriteFeedLastUpdatedTimeTo(XmlWriter writer, DateTimeOffset lastUpdatedTime, bool isRequired)
        {
            if (lastUpdatedTime == DateTimeOffset.MinValue && isRequired)
            {
                lastUpdatedTime = DateTimeOffset.UtcNow;
            }
            if (lastUpdatedTime != DateTimeOffset.MinValue)
            {
                WriteElement(writer, Atom10Constants.UpdatedTag, AsString(lastUpdatedTime));
            }
        }
 
        internal void WriteItemAuthorsTo(XmlWriter writer, Collection<SyndicationPerson> authors)
        {
            for (int i = 0; i < authors.Count; ++i)
            {
                SyndicationPerson p = authors[i];
                WritePersonTo(writer, p, Atom10Constants.AuthorTag);
            }
        }
 
        internal void WriteItemContents(XmlWriter dictWriter, SyndicationItem item)
        {
            WriteItemContents(dictWriter, item, null);
        }
 
        internal void WriteItemContributorsTo(XmlWriter writer, Collection<SyndicationPerson> contributors)
        {
            for (int i = 0; i < contributors.Count; ++i)
            {
                SyndicationPerson p = contributors[i];
                WritePersonTo(writer, p, Atom10Constants.ContributorTag);
            }
        }
 
        internal void WriteItemLastUpdatedTimeTo(XmlWriter writer, DateTimeOffset lastUpdatedTime)
        {
            if (lastUpdatedTime == DateTimeOffset.MinValue)
            {
                lastUpdatedTime = DateTimeOffset.UtcNow;
            }
            writer.WriteElementString(Atom10Constants.UpdatedTag,
                Atom10Constants.Atom10Namespace,
                AsString(lastUpdatedTime));
        }
 
        internal void WriteLink(XmlWriter writer, SyndicationLink link, Uri baseUri)
        {
            writer.WriteStartElement(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace);
            Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, link.BaseUri);
            if (baseUriToWrite != null)
            {
                writer.WriteAttributeString("xml", "base", XmlNs, FeedUtils.GetUriString(baseUriToWrite));
            }
            link.WriteAttributeExtensions(writer, SyndicationVersions.Atom10);
            if (!string.IsNullOrEmpty(link.RelationshipType) && !link.AttributeExtensions.ContainsKey(Atom10Relative))
            {
                writer.WriteAttributeString(Atom10Constants.RelativeTag, link.RelationshipType);
            }
            if (!string.IsNullOrEmpty(link.MediaType) && !link.AttributeExtensions.ContainsKey(Atom10Type))
            {
                writer.WriteAttributeString(Atom10Constants.TypeTag, link.MediaType);
            }
            if (!string.IsNullOrEmpty(link.Title) && !link.AttributeExtensions.ContainsKey(Atom10Title))
            {
                writer.WriteAttributeString(Atom10Constants.TitleTag, link.Title);
            }
            if (link.Length != 0 && !link.AttributeExtensions.ContainsKey(Atom10Length))
            {
                writer.WriteAttributeString(Atom10Constants.LengthTag, Convert.ToString(link.Length, CultureInfo.InvariantCulture));
            }
            if (!link.AttributeExtensions.ContainsKey(Atom10Href))
            {
                writer.WriteAttributeString(Atom10Constants.HrefTag, FeedUtils.GetUriString(link.Uri));
            }
            link.WriteElementExtensions(writer, SyndicationVersions.Atom10);
            writer.WriteEndElement();
        }
 
        protected override SyndicationFeed CreateFeedInstance()
        {
            return SyndicationFeedFormatter.CreateFeedInstance(this.feedType);
        }
 
        protected virtual SyndicationItem ReadItem(XmlReader reader, SyndicationFeed feed)
        {
            if (feed == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed");
            }
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            SyndicationItem item = CreateItem(feed);
            TraceItemReadBegin();
            ReadItemFrom(reader, item, feed.BaseUri);
            TraceItemReadEnd();
            return item;
        }
 
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "The out parameter is needed to enable implementations that read in items from the stream on demand")]
        protected virtual IEnumerable<SyndicationItem> ReadItems(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead)
        {
            if (feed == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed");
            }
            if (reader == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
            }
            NullNotAllowedCollection<SyndicationItem> items = new NullNotAllowedCollection<SyndicationItem>();
            while (reader.IsStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace))
            {
                items.Add(ReadItem(reader, feed));
            }
            areAllItemsRead = true;
            return items;
        }
 
        protected virtual void WriteItem(XmlWriter writer, SyndicationItem item, Uri feedBaseUri)
        {
            TraceItemWriteBegin();
            writer.WriteStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace);
            WriteItemContents(writer, item, feedBaseUri);
            writer.WriteEndElement();
            TraceItemWriteEnd();
        }
 
        protected virtual void WriteItems(XmlWriter writer, IEnumerable<SyndicationItem> items, Uri feedBaseUri)
        {
            if (items == null)
            {
                return;
            }
            foreach (SyndicationItem item in items)
            {
                this.WriteItem(writer, item, feedBaseUri);
            }
        }
 
        static TextSyndicationContent ReadTextContentFromHelper(XmlReader reader, string type, string context, bool preserveAttributeExtensions)
        {
            if (string.IsNullOrEmpty(type))
            {
                type = Atom10Constants.PlaintextType;
            }
 
            TextSyndicationContentKind kind;
            switch (type)
            {
                case Atom10Constants.PlaintextType:
                    kind = TextSyndicationContentKind.Plaintext;
                    break;
                case Atom10Constants.HtmlType:
                    kind = TextSyndicationContentKind.Html;
                    break;
                case Atom10Constants.XHtmlType:
                    kind = TextSyndicationContentKind.XHtml;
                    break;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.GetString(SR.Atom10SpecRequiresTextConstruct, context, type))));
            }
 
            Dictionary<XmlQualifiedName, string> attrs = null;
            if (reader.HasAttributes)
            {
                while (reader.MoveToNextAttribute())
                {
                    if (reader.LocalName == Atom10Constants.TypeTag && reader.NamespaceURI == string.Empty)
                    {
                        continue;
                    }
                    string ns = reader.NamespaceURI;
                    string name = reader.LocalName;
                    if (FeedUtils.IsXmlns(name, ns))
                    {
                        continue;
                    }
                    if (preserveAttributeExtensions)
                    {
                        string value = reader.Value;
                        if (attrs == null)
                        {
                            attrs = new Dictionary<XmlQualifiedName, string>();
                        }
                        attrs.Add(new XmlQualifiedName(name, ns), value);
                    }
                    else
                    {
                        TraceSyndicationElementIgnoredOnRead(reader);
                    }
                }
            }
            reader.MoveToElement();
            string val = (kind == TextSyndicationContentKind.XHtml) ? reader.ReadInnerXml() : reader.ReadElementString();
            TextSyndicationContent result = new TextSyndicationContent(val, kind);
            if (attrs != null)
            {
                foreach (XmlQualifiedName attr in attrs.Keys)
                {
                    if (!FeedUtils.IsXmlns(attr.Name, attr.Namespace))
                    {
                        result.AttributeExtensions.Add(attr, attrs[attr]);
                    }
                }
            }
            return result;
        }
 
        string AsString(DateTimeOffset dateTime)
        {
            if (dateTime.Offset == zeroOffset)
            {
                return dateTime.ToUniversalTime().ToString(Rfc3339UTCDateTimeFormat, CultureInfo.InvariantCulture);
            }
            else
            {
                return dateTime.ToString(Rfc3339LocalDateTimeFormat, CultureInfo.InvariantCulture);
            }
        }
 
        DateTimeOffset DateFromString(string dateTimeString, XmlReader reader)
        {
            dateTimeString = dateTimeString.Trim();
            if (dateTimeString.Length < 20)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new XmlException(FeedUtils.AddLineInfo(reader,
                    SR.ErrorParsingDateTime)));
            }
            if (dateTimeString[19] == '.')
            {
                // remove any fractional seconds, we choose to ignore them
                int i = 20;
                while (dateTimeString.Length > i && char.IsDigit(dateTimeString[i]))
                {
                    ++i;
                }
                dateTimeString = dateTimeString.Substring(0, 19) + dateTimeString.Substring(i);
            }
            DateTimeOffset localTime;
            if (DateTimeOffset.TryParseExact(dateTimeString, Rfc3339LocalDateTimeFormat,
                CultureInfo.InvariantCulture.DateTimeFormat,
                DateTimeStyles.None, out localTime))
            {
                return localTime;
            }
            DateTimeOffset utcTime;
            if (DateTimeOffset.TryParseExact(dateTimeString, Rfc3339UTCDateTimeFormat,
                CultureInfo.InvariantCulture.DateTimeFormat,
                DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out utcTime))
            {
                return utcTime;
            }
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                new XmlException(FeedUtils.AddLineInfo(reader,
                SR.ErrorParsingDateTime)));
        }
 
        void ReadCategory(XmlReader reader, SyndicationCategory category)
        {
            ReadCategory(reader, category, this.Version, this.PreserveAttributeExtensions, this.PreserveElementExtensions, this.maxExtensionSize);
        }
 
        SyndicationCategory ReadCategoryFrom(XmlReader reader, SyndicationFeed feed)
        {
            SyndicationCategory result = CreateCategory(feed);
            ReadCategory(reader, result);
            return result;
        }
 
        SyndicationCategory ReadCategoryFrom(XmlReader reader, SyndicationItem item)
        {
            SyndicationCategory result = CreateCategory(item);
            ReadCategory(reader, result);
            return result;
        }
 
        SyndicationContent ReadContentFrom(XmlReader reader, SyndicationItem item)
        {
            MoveToStartElement(reader);
            string type = reader.GetAttribute(Atom10Constants.TypeTag, string.Empty);
 
            SyndicationContent result;
            if (TryParseContent(reader, item, type, this.Version, out result))
            {
                return result;
            }
 
            if (string.IsNullOrEmpty(type))
            {
                type = Atom10Constants.PlaintextType;
            }
            string src = reader.GetAttribute(Atom10Constants.SourceTag, string.Empty);
 
            if (string.IsNullOrEmpty(src) && type != Atom10Constants.PlaintextType && type != Atom10Constants.HtmlType && type != Atom10Constants.XHtmlType)
            {
                return new XmlSyndicationContent(reader);
            }
 
            if (!string.IsNullOrEmpty(src))
            {
                result = new UrlSyndicationContent(new Uri(src, UriKind.RelativeOrAbsolute), type);
                bool isEmpty = reader.IsEmptyElement;
                if (reader.HasAttributes)
                {
                    while (reader.MoveToNextAttribute())
                    {
                        if (reader.LocalName == Atom10Constants.TypeTag && reader.NamespaceURI == string.Empty)
                        {
                            continue;
                        }
                        else if (reader.LocalName == Atom10Constants.SourceTag && reader.NamespaceURI == string.Empty)
                        {
                            continue;
                        }
                        else if (!FeedUtils.IsXmlns(reader.LocalName, reader.NamespaceURI))
                        {
                            if (this.preserveAttributeExtensions)
                            {
                                result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value);
                            }
                            else
                            {
                                TraceSyndicationElementIgnoredOnRead(reader);
                            }
                        }
                    }
                }
                reader.ReadStartElement();
                if (!isEmpty)
                {
                    reader.ReadEndElement();
                }
                return result;
            }
            else
            {
                return ReadTextContentFromHelper(reader, type, "//atom:feed/atom:entry/atom:content[@type]", this.preserveAttributeExtensions);
            }
        }
 
        void ReadFeed(XmlReader reader)
        {
            SetFeed(CreateFeedInstance());
            ReadFeedFrom(reader, this.Feed, false);
        }
 
        SyndicationFeed ReadFeedFrom(XmlReader reader, SyndicationFeed result, bool isSourceFeed)
        {
            reader.MoveToContent();
            try
            {
                bool elementIsEmpty = false;
                if (!isSourceFeed)
                {
                    MoveToStartElement(reader);
                    elementIsEmpty = reader.IsEmptyElement;
                    if (reader.HasAttributes)
                    {
                        while (reader.MoveToNextAttribute())
                        {
                            if (reader.LocalName == "lang" && reader.NamespaceURI == XmlNs)
                            {
                                result.Language = reader.Value;
                            }
                            else if (reader.LocalName == "base" && reader.NamespaceURI == XmlNs)
                            {
                                result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value);
                            }
                            else
                            {
                                string ns = reader.NamespaceURI;
                                string name = reader.LocalName;
                                if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns))
                                {
                                    continue;
                                }
                                string val = reader.Value;
                                if (!TryParseAttribute(name, ns, val, result, this.Version))
                                {
                                    if (this.preserveAttributeExtensions)
                                    {
                                        result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value);
                                    }
                                    else
                                    {
                                        TraceSyndicationElementIgnoredOnRead(reader);
                                    }
                                }
                            }
                        }
                    }
                    reader.ReadStartElement();
                }
 
                XmlBuffer buffer = null;
                XmlDictionaryWriter extWriter = null;
                bool areAllItemsRead = true;
                bool readItemsAtLeastOnce = false;
 
                if (!elementIsEmpty)
                {   
                    try
                    {
                        while (reader.IsStartElement())
                        {
                            if (TryParseFeedElementFrom(reader, result))
                            {
                                // nothing, we parsed something, great
                            }
                            else if (reader.IsStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace) && !isSourceFeed)
                            {
                                if (readItemsAtLeastOnce)
                                {
                                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.FeedHasNonContiguousItems, this.GetType().ToString())));
                                }
                                result.Items = ReadItems(reader, result, out areAllItemsRead);
                                readItemsAtLeastOnce = true;
                                // if the derived class is reading the items lazily, then stop reading from the stream
                                if (!areAllItemsRead)
                                {
                                    break;
                                }
                            }
                            else
                            {
                                if (!TryParseElement(reader, result, this.Version))
                                {
                                    if (this.preserveElementExtensions)
                                    {
                                        CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, this.maxExtensionSize);
                                    }
                                    else
                                    {
                                        TraceSyndicationElementIgnoredOnRead(reader);
                                        reader.Skip();
                                    }
                                }
                            }
                        }
                        LoadElementExtensions(buffer, extWriter, result);
                    }
                    finally
                    {
                        if (extWriter != null)
                        {
                            ((IDisposable)extWriter).Dispose();
                        }
                    }
                }
                if (!isSourceFeed && areAllItemsRead)
                {
                    reader.ReadEndElement(); // feed
                }
            }
            catch (FormatException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e));
            }
            catch (ArgumentException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e));
            }
            return result;
        }
 
        void ReadItemFrom(XmlReader reader, SyndicationItem result, Uri feedBaseUri)
        {
            try
            {
                result.BaseUri = feedBaseUri;
                MoveToStartElement(reader);
                bool isEmpty = reader.IsEmptyElement;
                if (reader.HasAttributes)
                {
                    while (reader.MoveToNextAttribute())
                    {
                        string ns = reader.NamespaceURI;
                        string name = reader.LocalName;
                        if (name == "base" && ns == XmlNs)
                        {
                            result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value);
                            continue;
                        }
                        if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns))
                        {
                            continue;
                        }
                        string val = reader.Value;
                        if (!TryParseAttribute(name, ns, val, result, this.Version))
                        {
                            if (this.preserveAttributeExtensions)
                            {
                                result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value);
                            }
                            else
                            {
                                TraceSyndicationElementIgnoredOnRead(reader);
                            }
                        }
                    }
                }
                reader.ReadStartElement();
                if (!isEmpty)
                {
                    XmlBuffer buffer = null;
                    XmlDictionaryWriter extWriter = null;
                    try
                    {
                        while (reader.IsStartElement())
                        {
                            if (TryParseItemElementFrom(reader, result))
                            {
                                // nothing, we parsed something, great
                            }
                            else
                            {
                                if (!TryParseElement(reader, result, this.Version))
                                {
                                    if (this.preserveElementExtensions)
                                    {
                                        CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, this.maxExtensionSize);
                                    }
                                    else
                                    {
                                        TraceSyndicationElementIgnoredOnRead(reader);
                                        reader.Skip();
                                    }
                                }
                            }
                        }
                        LoadElementExtensions(buffer, extWriter, result);
                    }
                    finally
                    {
                        if (extWriter != null)
                        {
                            ((IDisposable) extWriter).Dispose();
                        }
                    }
                    reader.ReadEndElement(); // item
                }
            }
            catch (FormatException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e));
            }
            catch (ArgumentException e)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e));
            }
        }
 
        void ReadLink(XmlReader reader, SyndicationLink link, Uri baseUri)
        {
            bool isEmpty = reader.IsEmptyElement;
            string mediaType = null;
            string relationship = null;
            string title = null;
            string lengthStr = null;
            string val = null;
            link.BaseUri = baseUri;
            if (reader.HasAttributes)
            {
                while (reader.MoveToNextAttribute())
                {
                    if (reader.LocalName == "base" && reader.NamespaceURI == XmlNs)
                    {
                        link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, reader.Value);
                    }
                    else if (reader.LocalName == Atom10Constants.TypeTag && reader.NamespaceURI == string.Empty)
                    {
                        mediaType = reader.Value;
                    }
                    else if (reader.LocalName == Atom10Constants.RelativeTag && reader.NamespaceURI == string.Empty)
                    {
                        relationship = reader.Value;
                    }
                    else if (reader.LocalName == Atom10Constants.TitleTag && reader.NamespaceURI == string.Empty)
                    {
                        title = reader.Value;
                    }
                    else if (reader.LocalName == Atom10Constants.LengthTag && reader.NamespaceURI == string.Empty)
                    {
                        lengthStr = reader.Value;
                    }
                    else if (reader.LocalName == Atom10Constants.HrefTag && reader.NamespaceURI == string.Empty)
                    {
                        val = reader.Value;
                    }
                    else if (!FeedUtils.IsXmlns(reader.LocalName, reader.NamespaceURI))
                    {
                        if (this.preserveAttributeExtensions)
                        {
                            link.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value);
                        }
                        else
                        {
                            TraceSyndicationElementIgnoredOnRead(reader);
                        }
                    }
                }
            }
 
            long length = 0;
            if (!string.IsNullOrEmpty(lengthStr))
            {
                length = Convert.ToInt64(lengthStr, CultureInfo.InvariantCulture.NumberFormat);
            }
            reader.ReadStartElement();
            if (!isEmpty)
            {
                XmlBuffer buffer = null;
                XmlDictionaryWriter extWriter = null;
                try
                {
                    while (reader.IsStartElement())
                    {
                        if (TryParseElement(reader, link, this.Version))
                        {
                            continue;
                        }
                        else if (!this.preserveElementExtensions)
                        {
                            SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader);
                            reader.Skip();
                        }
                        else
                        {
                            CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, this.maxExtensionSize);
                        }
                    }
                    LoadElementExtensions(buffer, extWriter, link);
                }
                finally
                {
                    if (extWriter != null)
                    {
                        ((IDisposable) extWriter).Dispose();
                    }
                }
                reader.ReadEndElement();
            }
            link.Length = length;
            link.MediaType = mediaType;
            link.RelationshipType = relationship;
            link.Title = title;
            link.Uri = (val != null) ? new Uri(val, UriKind.RelativeOrAbsolute) : null;
        }
 
        SyndicationLink ReadLinkFrom(XmlReader reader, SyndicationFeed feed)
        {
            SyndicationLink result = CreateLink(feed);
            ReadLink(reader, result, feed.BaseUri);
            return result;
        }
 
        SyndicationLink ReadLinkFrom(XmlReader reader, SyndicationItem item)
        {
            SyndicationLink result = CreateLink(item);
            ReadLink(reader, result, item.BaseUri);
            return result;
        }
 
        SyndicationPerson ReadPersonFrom(XmlReader reader, SyndicationFeed feed)
        {
            SyndicationPerson result = CreatePerson(feed);
            ReadPersonFrom(reader, result);
            return result;
        }
 
        SyndicationPerson ReadPersonFrom(XmlReader reader, SyndicationItem item)
        {
            SyndicationPerson result = CreatePerson(item);
            ReadPersonFrom(reader, result);
            return result;
        }
 
        void ReadPersonFrom(XmlReader reader, SyndicationPerson result)
        {
            bool isEmpty = reader.IsEmptyElement;
            if (reader.HasAttributes)
            {
                while (reader.MoveToNextAttribute())
                {
                    string ns = reader.NamespaceURI;
                    string name = reader.LocalName;
                    if (FeedUtils.IsXmlns(name, ns))
                    {
                        continue;
                    }
                    string val = reader.Value;
                    if (!TryParseAttribute(name, ns, val, result, this.Version))
                    {
                        if (this.preserveAttributeExtensions)
                        {
                            result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value);
                        }
                        else
                        {
                            TraceSyndicationElementIgnoredOnRead(reader);
                        }
                    }
                }
            }
            reader.ReadStartElement();
            if (!isEmpty)
            {
                XmlBuffer buffer = null;
                XmlDictionaryWriter extWriter = null;
                try
                {
                    while (reader.IsStartElement())
                    {
                        if (reader.IsStartElement(Atom10Constants.NameTag, Atom10Constants.Atom10Namespace))
                        {
                            result.Name = reader.ReadElementString();
                        }
                        else if (reader.IsStartElement(Atom10Constants.UriTag, Atom10Constants.Atom10Namespace))
                        {
                            result.Uri = reader.ReadElementString();
                        }
                        else if (reader.IsStartElement(Atom10Constants.EmailTag, Atom10Constants.Atom10Namespace))
                        {
                            result.Email = reader.ReadElementString();
                        }
                        else
                        {
                            if (!TryParseElement(reader, result, this.Version))
                            {
                                if (this.preserveElementExtensions)
                                {
                                    CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, this.maxExtensionSize);
                                }
                                else
                                {
                                    TraceSyndicationElementIgnoredOnRead(reader);
                                    reader.Skip();
                                }
                            }
                        }
                    }
                    LoadElementExtensions(buffer, extWriter, result);
                }
                finally
                {
                    if (extWriter != null)
                    {
                        ((IDisposable) extWriter).Dispose();
                    }
                }
                reader.ReadEndElement();
            }
        }
 
        TextSyndicationContent ReadTextContentFrom(XmlReader reader, string context)
        {
            return ReadTextContentFrom(reader, context, this.PreserveAttributeExtensions);
        }
 
        void WriteCategoriesTo(XmlWriter writer, Collection<SyndicationCategory> categories)
        {
            for (int i = 0; i < categories.Count; ++i)
            {
                WriteCategory(writer, categories[i], this.Version);
            }
        }
 
        void WriteFeed(XmlWriter writer)
        {
            if (this.Feed == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.FeedFormatterDoesNotHaveFeed)));
            }
            WriteFeedTo(writer, this.Feed, false); //  isSourceFeed 
        }
 
        void WriteFeedTo(XmlWriter writer, SyndicationFeed feed, bool isSourceFeed)
        {
            if (!isSourceFeed)
            {
                if (!string.IsNullOrEmpty(feed.Language))
                {
                    writer.WriteAttributeString("xml", "lang", XmlNs, feed.Language);
                }
                if (feed.BaseUri != null)
                {
                    writer.WriteAttributeString("xml", "base", XmlNs, FeedUtils.GetUriString(feed.BaseUri));
                }
                WriteAttributeExtensions(writer, feed, this.Version);
            }
            bool isElementRequired = !isSourceFeed;
            TextSyndicationContent title = feed.Title;
            if (isElementRequired)
            {
                title = title ?? new TextSyndicationContent(string.Empty);
            }
            WriteContentTo(writer, Atom10Constants.TitleTag, title);
            WriteContentTo(writer, Atom10Constants.SubtitleTag, feed.Description);
            string id = feed.Id;
            if (isElementRequired)
            {
                id = id ?? idGenerator.Next();
            }
            WriteElement(writer, Atom10Constants.IdTag, id);
            WriteContentTo(writer, Atom10Constants.RightsTag, feed.Copyright);
            WriteFeedLastUpdatedTimeTo(writer, feed.LastUpdatedTime, isElementRequired);
            WriteCategoriesTo(writer, feed.Categories);
            if (feed.ImageUrl != null)
            {
                WriteElement(writer, Atom10Constants.LogoTag, feed.ImageUrl.ToString());
            }
            WriteFeedAuthorsTo(writer, feed.Authors);
            WriteFeedContributorsTo(writer, feed.Contributors);
            WriteElement(writer, Atom10Constants.GeneratorTag, feed.Generator);
 
            for (int i = 0; i < feed.Links.Count; ++i)
            {
                WriteLink(writer, feed.Links[i], feed.BaseUri);
            }
 
            WriteElementExtensions(writer, feed, this.Version);
 
            if (!isSourceFeed)
            {
                WriteItems(writer, feed.Items, feed.BaseUri);
            }
        }
 
        void WriteItemContents(XmlWriter dictWriter, SyndicationItem item, Uri feedBaseUri)
        {
            Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(feedBaseUri, item.BaseUri);
            if (baseUriToWrite != null)
            {
                dictWriter.WriteAttributeString("xml", "base", XmlNs, FeedUtils.GetUriString(baseUriToWrite));
            }
            WriteAttributeExtensions(dictWriter, item, this.Version);
 
            string id = item.Id ?? idGenerator.Next();
            WriteElement(dictWriter, Atom10Constants.IdTag, id);
 
            TextSyndicationContent title = item.Title ?? new TextSyndicationContent(string.Empty);
            WriteContentTo(dictWriter, Atom10Constants.TitleTag, title);
            WriteContentTo(dictWriter, Atom10Constants.SummaryTag, item.Summary);
            if (item.PublishDate != DateTimeOffset.MinValue)
            {
                dictWriter.WriteElementString(Atom10Constants.PublishedTag,
                    Atom10Constants.Atom10Namespace,
                    AsString(item.PublishDate));
            }
            WriteItemLastUpdatedTimeTo(dictWriter, item.LastUpdatedTime);
            WriteItemAuthorsTo(dictWriter, item.Authors);
            WriteItemContributorsTo(dictWriter, item.Contributors);
            for (int i = 0; i < item.Links.Count; ++i)
            {
                WriteLink(dictWriter, item.Links[i], item.BaseUri);
            }
            WriteCategoriesTo(dictWriter, item.Categories);
            WriteContentTo(dictWriter, Atom10Constants.ContentTag, item.Content);
            WriteContentTo(dictWriter, Atom10Constants.RightsTag, item.Copyright);
            if (item.SourceFeed != null)
            {
                dictWriter.WriteStartElement(Atom10Constants.SourceFeedTag, Atom10Constants.Atom10Namespace);
                WriteFeedTo(dictWriter, item.SourceFeed, true); //  isSourceFeed 
                dictWriter.WriteEndElement();
            }
            WriteElementExtensions(dictWriter, item, this.Version);
        }
 
        void WritePersonTo(XmlWriter writer, SyndicationPerson p, string elementName)
        {
            writer.WriteStartElement(elementName, Atom10Constants.Atom10Namespace);
            WriteAttributeExtensions(writer, p, this.Version);
            WriteElement(writer, Atom10Constants.NameTag, p.Name);
            if (!string.IsNullOrEmpty(p.Uri))
            {
                writer.WriteElementString(Atom10Constants.UriTag, Atom10Constants.Atom10Namespace, p.Uri);
            }
            if (!string.IsNullOrEmpty(p.Email))
            {
                writer.WriteElementString(Atom10Constants.EmailTag, Atom10Constants.Atom10Namespace, p.Email);
            }
            WriteElementExtensions(writer, p, this.Version);
            writer.WriteEndElement();
        }
    }
 
    [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
    [XmlRoot(ElementName = Atom10Constants.FeedTag, Namespace = Atom10Constants.Atom10Namespace)]
    public class Atom10FeedFormatter<TSyndicationFeed> : Atom10FeedFormatter
        where TSyndicationFeed : SyndicationFeed, new ()
    {
        // constructors
        public Atom10FeedFormatter()
            : base(typeof(TSyndicationFeed))
        {
        }
        public Atom10FeedFormatter(TSyndicationFeed feedToWrite)
            : base(feedToWrite)
        {
        }
 
        protected override SyndicationFeed CreateFeedInstance()
        {
            return new TSyndicationFeed();
        }
    }
}