mirror of
https://github.com/ckaczor/FeedCenter.git
synced 2026-02-16 18:47:28 -05:00
Move application files to sub-folder
This commit is contained in:
137
Application/FeedParsers/AtomParser.cs
Normal file
137
Application/FeedParsers/AtomParser.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using Common.Debug;
|
||||
using Common.Xml;
|
||||
using System.Xml;
|
||||
|
||||
namespace FeedCenter.FeedParsers
|
||||
{
|
||||
internal class AtomParser : FeedParserBase
|
||||
{
|
||||
public AtomParser(Feed feed) : base(feed) { }
|
||||
|
||||
public override FeedReadResult ParseFeed(string feedText)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the XML document
|
||||
var document = new XmlDocument { XmlResolver = null };
|
||||
|
||||
// Load the XML document from the text
|
||||
document.LoadXml(feedText);
|
||||
|
||||
// Create the namespace manager
|
||||
var namespaceManager = document.GetAllNamespaces();
|
||||
|
||||
// Get the root node
|
||||
XmlNode rootNode = document.DocumentElement;
|
||||
|
||||
// If we didn't find a root node then bail
|
||||
if (rootNode == null)
|
||||
return FeedReadResult.UnknownError;
|
||||
|
||||
// Initialize the sequence number for items
|
||||
var sequence = 0;
|
||||
|
||||
// Loop over all nodes in the root node
|
||||
foreach (XmlNode node in rootNode.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (node.Name)
|
||||
{
|
||||
case "title":
|
||||
Feed.Title = System.Net.WebUtility.HtmlDecode(node.InnerText).Trim();
|
||||
break;
|
||||
|
||||
case "link":
|
||||
string rel = null;
|
||||
|
||||
if (node.Attributes == null)
|
||||
break;
|
||||
|
||||
XmlNode relNode = node.Attributes["rel"];
|
||||
|
||||
if (relNode != null)
|
||||
rel = relNode.InnerText;
|
||||
|
||||
if (string.IsNullOrEmpty(rel) || rel == "alternate")
|
||||
Feed.Link = node.Attributes["href"].InnerText.Trim();
|
||||
|
||||
break;
|
||||
|
||||
case "subtitle":
|
||||
Feed.Description = node.InnerText;
|
||||
break;
|
||||
|
||||
case "entry":
|
||||
HandleFeedItem(namespaceManager, node, ref sequence);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FeedReadResult.Success;
|
||||
}
|
||||
catch (XmlException xmlException)
|
||||
{
|
||||
Tracer.WriteLine("XML error: " + xmlException.Message + "\n" + feedText);
|
||||
|
||||
return FeedReadResult.InvalidXml;
|
||||
}
|
||||
}
|
||||
|
||||
protected override FeedItem ParseFeedItem(XmlNamespaceManager namespaceManager, XmlNode node)
|
||||
{
|
||||
// Create a new feed item
|
||||
var feedItem = FeedItem.Create();
|
||||
|
||||
// Loop over all nodes in the feed node
|
||||
foreach (XmlNode childNode in node.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (childNode.Name.ToLower())
|
||||
{
|
||||
case "title":
|
||||
feedItem.Title = System.Net.WebUtility.HtmlDecode(childNode.InnerText).Trim();
|
||||
break;
|
||||
|
||||
case "id":
|
||||
feedItem.Guid = childNode.InnerText;
|
||||
break;
|
||||
|
||||
case "content":
|
||||
feedItem.Description = System.Net.WebUtility.HtmlDecode(childNode.InnerText);
|
||||
break;
|
||||
|
||||
case "link":
|
||||
string rel = null;
|
||||
|
||||
if (childNode.Attributes == null)
|
||||
break;
|
||||
|
||||
XmlNode relNode = childNode.Attributes["rel"];
|
||||
|
||||
if (relNode != null)
|
||||
rel = relNode.InnerText.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(rel) || rel == "alternate")
|
||||
{
|
||||
var link = childNode.Attributes["href"].InnerText;
|
||||
|
||||
if (link.StartsWith("/"))
|
||||
{
|
||||
link = Feed.Link + link;
|
||||
link = link.Replace("//", "/");
|
||||
}
|
||||
|
||||
feedItem.Link = link;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(feedItem.Guid))
|
||||
feedItem.Guid = feedItem.Link;
|
||||
|
||||
return feedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
159
Application/FeedParsers/FeedParserBase.cs
Normal file
159
Application/FeedParsers/FeedParserBase.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using Common.Debug;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
namespace FeedCenter.FeedParsers
|
||||
{
|
||||
[Serializable]
|
||||
internal class InvalidFeedFormatException : ApplicationException
|
||||
{
|
||||
internal InvalidFeedFormatException(Exception exception)
|
||||
: base(string.Empty, exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class FeedParserBase
|
||||
{
|
||||
#region Member variables
|
||||
|
||||
protected readonly Feed Feed;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
protected FeedParserBase(Feed feed)
|
||||
{
|
||||
Feed = feed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public abstract FeedReadResult ParseFeed(string feedText);
|
||||
|
||||
protected abstract FeedItem ParseFeedItem(XmlNamespaceManager namespaceManager, XmlNode node);
|
||||
|
||||
protected void HandleFeedItem(XmlNamespaceManager namespaceManager, XmlNode node, ref int sequence)
|
||||
{
|
||||
// Build a feed item from the node
|
||||
FeedItem newFeedItem = ParseFeedItem(namespaceManager, node);
|
||||
|
||||
if (newFeedItem == null)
|
||||
return;
|
||||
|
||||
// Check for feed items with no guid or link
|
||||
if (string.IsNullOrWhiteSpace(newFeedItem.Guid) && string.IsNullOrWhiteSpace(newFeedItem.Link))
|
||||
return;
|
||||
|
||||
// Look for an item that has the same guid
|
||||
FeedItem existingFeedItem = Feed.Items.FirstOrDefault(item => item.Guid == newFeedItem.Guid && item.ID != newFeedItem.ID);
|
||||
|
||||
// Check to see if we already have this feed item
|
||||
if (existingFeedItem == null)
|
||||
{
|
||||
Tracer.WriteLine("New link: " + newFeedItem.Link);
|
||||
|
||||
// Associate the new item with the right feed
|
||||
newFeedItem.Feed = Feed;
|
||||
|
||||
// Set the item as new
|
||||
newFeedItem.New = true;
|
||||
|
||||
// Add the item to the list
|
||||
Feed.Items.Add(newFeedItem);
|
||||
|
||||
// Feed was updated
|
||||
Feed.LastUpdated = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
Tracer.WriteLine("Existing link: " + newFeedItem.Link);
|
||||
|
||||
// Update the fields in the existing item
|
||||
existingFeedItem.Link = newFeedItem.Link;
|
||||
existingFeedItem.Title = newFeedItem.Title;
|
||||
existingFeedItem.Guid = newFeedItem.Guid;
|
||||
existingFeedItem.Description = newFeedItem.Description;
|
||||
|
||||
// Item is no longer new
|
||||
existingFeedItem.New = false;
|
||||
|
||||
// Switch over to the existing item for the rest
|
||||
newFeedItem = existingFeedItem;
|
||||
}
|
||||
|
||||
// Item was last seen now
|
||||
newFeedItem.LastFound = Feed.LastChecked;
|
||||
|
||||
// Set the sequence
|
||||
newFeedItem.Sequence = sequence;
|
||||
|
||||
// Increment the sequence
|
||||
sequence++;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Parser creation and detection
|
||||
|
||||
public static FeedParserBase CreateFeedParser(Feed feed, string feedText)
|
||||
{
|
||||
FeedType feedType = DetectFeedType(feedText);
|
||||
|
||||
switch (feedType)
|
||||
{
|
||||
case FeedType.Rss:
|
||||
return new RssParser(feed);
|
||||
|
||||
case FeedType.Rdf:
|
||||
return new RdfParser(feed);
|
||||
|
||||
case FeedType.Atom:
|
||||
return new AtomParser(feed);
|
||||
}
|
||||
|
||||
throw new ArgumentException(string.Format("Feed type {0} is not supported", feedType));
|
||||
}
|
||||
|
||||
public static FeedType DetectFeedType(string feedText)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the XML document
|
||||
var document = new XmlDocument { XmlResolver = null };
|
||||
|
||||
// Load the XML document from the text
|
||||
document.LoadXml(feedText);
|
||||
|
||||
// Loop over all child nodes
|
||||
foreach (XmlNode node in document.ChildNodes)
|
||||
{
|
||||
switch (node.Name)
|
||||
{
|
||||
case "rss":
|
||||
return FeedType.Rss;
|
||||
|
||||
case "rdf:RDF":
|
||||
return FeedType.Rdf;
|
||||
|
||||
case "feed":
|
||||
return FeedType.Atom;
|
||||
}
|
||||
}
|
||||
|
||||
// No clue!
|
||||
return FeedType.Unknown;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new InvalidFeedFormatException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
114
Application/FeedParsers/RdfParser.cs
Normal file
114
Application/FeedParsers/RdfParser.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Common.Debug;
|
||||
using Common.Xml;
|
||||
using System.Xml;
|
||||
|
||||
namespace FeedCenter.FeedParsers
|
||||
{
|
||||
internal class RdfParser : FeedParserBase
|
||||
{
|
||||
public RdfParser(Feed feed) : base(feed) { }
|
||||
|
||||
public override FeedReadResult ParseFeed(string feedText)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the XML document
|
||||
var document = new XmlDocument { XmlResolver = null };
|
||||
|
||||
// Load the XML document from the text
|
||||
document.LoadXml(feedText);
|
||||
|
||||
// Create the namespace manager
|
||||
XmlNamespaceManager namespaceManager = document.GetAllNamespaces();
|
||||
|
||||
// Get the root node
|
||||
XmlNode rootNode = document.DocumentElement;
|
||||
|
||||
// If we didn't find a root node then bail
|
||||
if (rootNode == null)
|
||||
return FeedReadResult.UnknownError;
|
||||
|
||||
// Get the channel node
|
||||
XmlNode channelNode = rootNode.SelectSingleNode("default:channel", namespaceManager);
|
||||
|
||||
if (channelNode == null)
|
||||
return FeedReadResult.InvalidXml;
|
||||
|
||||
// Loop over all nodes in the channel node
|
||||
foreach (XmlNode node in channelNode.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (node.Name)
|
||||
{
|
||||
case "title":
|
||||
Feed.Title = System.Net.WebUtility.HtmlDecode(node.InnerText).Trim();
|
||||
break;
|
||||
|
||||
case "link":
|
||||
Feed.Link = node.InnerText.Trim();
|
||||
break;
|
||||
|
||||
case "description":
|
||||
Feed.Description = node.InnerText;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the sequence number for items
|
||||
int sequence = 0;
|
||||
|
||||
// Loop over all nodes in the channel node
|
||||
foreach (XmlNode node in rootNode.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (node.Name)
|
||||
{
|
||||
case "item":
|
||||
HandleFeedItem(namespaceManager, node, ref sequence);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FeedReadResult.Success;
|
||||
}
|
||||
catch (XmlException xmlException)
|
||||
{
|
||||
Tracer.WriteLine("XML error: " + xmlException.Message + "\n" + feedText);
|
||||
|
||||
return FeedReadResult.InvalidXml;
|
||||
}
|
||||
}
|
||||
|
||||
protected override FeedItem ParseFeedItem(XmlNamespaceManager namespaceManager, XmlNode node)
|
||||
{
|
||||
// Create a new feed item
|
||||
FeedItem feedItem = FeedItem.Create();
|
||||
|
||||
// Loop over all nodes in the feed node
|
||||
foreach (XmlNode childNode in node.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (childNode.Name.ToLower())
|
||||
{
|
||||
case "title":
|
||||
feedItem.Title = System.Net.WebUtility.HtmlDecode(childNode.InnerText).Trim();
|
||||
break;
|
||||
|
||||
case "link":
|
||||
feedItem.Link = childNode.InnerText.Trim();
|
||||
|
||||
// RDF doesn't have a GUID node so we'll just use the link
|
||||
feedItem.Guid = feedItem.Link;
|
||||
|
||||
break;
|
||||
|
||||
case "description":
|
||||
feedItem.Description = System.Net.WebUtility.HtmlDecode(childNode.InnerText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return feedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Application/FeedParsers/RssParser.cs
Normal file
121
Application/FeedParsers/RssParser.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using Common.Debug;
|
||||
using Common.Xml;
|
||||
using System.Xml;
|
||||
|
||||
namespace FeedCenter.FeedParsers
|
||||
{
|
||||
internal class RssParser : FeedParserBase
|
||||
{
|
||||
public RssParser(Feed feed) : base(feed) { }
|
||||
|
||||
public override FeedReadResult ParseFeed(string feedText)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the XML document
|
||||
var document = new XmlDocument { XmlResolver = null };
|
||||
|
||||
// Load the XML document from the text
|
||||
document.LoadXml(feedText);
|
||||
|
||||
// Create the namespace manager
|
||||
XmlNamespaceManager namespaceManager = document.GetAllNamespaces();
|
||||
|
||||
// Get the root node
|
||||
XmlNode rootNode = document.DocumentElement;
|
||||
|
||||
// If we didn't find a root node then bail
|
||||
if (rootNode == null)
|
||||
return FeedReadResult.UnknownError;
|
||||
|
||||
// Get the channel node
|
||||
XmlNode channelNode = rootNode.SelectSingleNode("default:channel", namespaceManager);
|
||||
|
||||
if (channelNode == null)
|
||||
return FeedReadResult.InvalidXml;
|
||||
|
||||
// Initialize the sequence number for items
|
||||
int sequence = 0;
|
||||
|
||||
// Loop over all nodes in the channel node
|
||||
foreach (XmlNode node in channelNode.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (node.Name)
|
||||
{
|
||||
case "title":
|
||||
Feed.Title = System.Net.WebUtility.HtmlDecode(node.InnerText).Trim();
|
||||
break;
|
||||
|
||||
case "link":
|
||||
Feed.Link = node.InnerText.Trim();
|
||||
break;
|
||||
|
||||
case "description":
|
||||
Feed.Description = node.InnerText;
|
||||
break;
|
||||
|
||||
case "item":
|
||||
HandleFeedItem(namespaceManager, node, ref sequence);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FeedReadResult.Success;
|
||||
}
|
||||
catch (XmlException xmlException)
|
||||
{
|
||||
Tracer.WriteLine("XML error: " + xmlException.Message + "\n" + feedText);
|
||||
|
||||
return FeedReadResult.InvalidXml;
|
||||
}
|
||||
}
|
||||
|
||||
protected override FeedItem ParseFeedItem(XmlNamespaceManager namespaceManager, XmlNode node)
|
||||
{
|
||||
// Create a new feed item
|
||||
FeedItem feedItem = FeedItem.Create();
|
||||
|
||||
// Loop over all nodes in the feed node
|
||||
foreach (XmlNode childNode in node.ChildNodes)
|
||||
{
|
||||
// Handle each node that we find
|
||||
switch (childNode.Name.ToLower())
|
||||
{
|
||||
case "title":
|
||||
feedItem.Title = System.Net.WebUtility.HtmlDecode(childNode.InnerText).Trim();
|
||||
break;
|
||||
|
||||
case "link":
|
||||
feedItem.Link = childNode.InnerText.Trim();
|
||||
break;
|
||||
|
||||
case "guid":
|
||||
feedItem.Guid = childNode.InnerText.Trim();
|
||||
|
||||
bool permaLink = true;
|
||||
|
||||
if (childNode.Attributes != null)
|
||||
{
|
||||
var permaLinkNode = childNode.Attributes.GetNamedItem("isPermaLink");
|
||||
permaLink = (permaLinkNode == null || permaLinkNode.Value == "true");
|
||||
}
|
||||
|
||||
if (permaLink)
|
||||
feedItem.Link = feedItem.Guid;
|
||||
|
||||
break;
|
||||
|
||||
case "description":
|
||||
feedItem.Description = System.Net.WebUtility.HtmlDecode(childNode.InnerText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(feedItem.Guid))
|
||||
feedItem.Guid = feedItem.Link;
|
||||
|
||||
return feedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user