/// <summary>
        /// Parses the given <see cref="FeedType"/> and returns a <see cref="IList&lt;Item&gt;"/>.
        /// </summary>
        /// <returns></returns>
        public IList<Item> Parse(Feed feed)
        {
            try
            {
                _cleaner = FeedCleaner.ResolveCleaner(feed);

                // Make sure we fill the original state object, not the 'info' variable which is a copy.
                switch (feed.FeedType)
                {
                    case FeedType.RSS:
                        return ParseRss(feed);
                    case FeedType.RDF:
                        return ParseRdf(feed);
                    case FeedType.Atom:
                        return ParseAtom(feed);
                    default:
                        throw new NotSupportedException(string.Format("{0} is not supported", feed.FeedType.ToString()));
                }
            }
            catch (Exception e)
            {
                Logger.Info("An Exception occured with FeedFetcher.Parse:\n\n{0}", e);
                return new List<Item>();
            }
        }
 public static FeedCleaner ResolveCleaner(Feed feed)
 {
     switch (feed.Cleaner)
     {
         case "AolNewsCleaner":
             return new AolNewsCleaner();
         case "SlashdotCleaner":
             return new SlashdotCleaner();
         default:
             return new FeedCleaner();
     }
 }
        /// <summary>
        /// All <see cref="Feed"/>s in the database. The Category and Site properties are null.
        /// </summary>
        /// <returns></returns>
        public static List<Feed> AllFeeds()
        {
            List<Feed> list = new List<Feed>();

            try
            {
                using (SqlConnection connection = new SqlConnection(ConnectionString))
                {
                    connection.Open();
                    using (SqlCommand command = new SqlCommand())
                    {
                        // Ignore read items if it's setup.
                        string sql = "SELECT * FROM feeds ";
                        command.Connection = connection;
                        command.CommandText = sql;

                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                Feed feed = new Feed();
                                feed.Id = (Guid)reader["id"];
                                feed.Cleaner = (string)reader["cleaner"];
                                feed.Url = (string)reader["url"];
                                feed.FeedType = (FeedType) ((byte)reader["type"]);

                                // Not needed
                                feed.Category = null;
                                feed.Site = null;

                                list.Add(feed);
                            }
                        }
                    }
                }
            }
            catch (SqlException e)
            {
                Logger.Warn("A SqlException occured getting AllFeeds from the database:\n\n{0}", e);
            }

            return list;
        }
        public IList<Feed> ListFeeds()
        {
            if (FeedCache.Current.IsCached())
                return FeedCache.Current.Items;

            IList<Feed> list = new List<Feed>();

            try
            {
                using (SqliteConnection connection = new SqliteConnection(FeedConnectionString))
                {
                    connection.Open();
                    using (SqliteCommand command = new SqliteCommand(connection))
                    {
                        command.CommandText = @"SELECT f.*,c.title as categoryTitle,s.title as siteTitle, s.url as siteUrl " +
                            "FROM feeds f INNER JOIN categories c ON UPPER(c.id) = UPPER(f.categoryid) " +
                            "INNER JOIN sites s on UPPER(s.id) = UPPER(f.siteid)";

                        using (SqliteDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                Category category = new Category();
                                category.Id = (Guid)reader["categoryid"];
                                category.Title = (string)reader["categoryTitle"];

                                Site site = new Site();
                                site.Id = (Guid)reader["siteid"];
                                site.Title = (string)reader["siteTitle"];
                                site.Url = (string)reader["siteUrl"];

                                Feed feed = new Feed();
                                feed.Id = (Guid)reader["id"];
                                feed.Category = category;
                                feed.Site = site;
                                feed.Cleaner = (string)reader["cleaner"];
                                feed.Url = (string)reader["url"];

                                long type = (long)reader["type"];
                                feed.FeedType = (FeedType)type;

                                list.Add(feed);
                            }
                        }
                    }
                }
            }
            catch (SqliteException e)
            {
                Logger.Warn("SqliteException occured while listing feeds: \n{0}", e);
            }

            FeedCache.Current.Update(list);
            return list;
        }
        /// <summary>
        /// Parses an Atom feed and returns a <see cref="IList&lt;Item&gt;"/>.
        /// </summary>
        public virtual IList<Item> ParseAtom(Feed feed)
        {
            try
            {
                string xml = LoadXml(feed.Url);
                using (StringReader reader = new StringReader(xml))
                {
                    XDocument doc = XDocument.Load(reader);

                    // Feed/Entry
                    var entries = from item in doc.Root.Elements().Where(i => i.Name.LocalName == "entry")
                                  select item;

                    List<Item> list = FillList(entries, feed, FeedType.Atom);
                    ParseImages(list);

                    return list;
                }
            }
            catch (Exception e)
            {
                Logger.Info("An error occured with FeedFetcher.ParseAtom '{0}':\n\n{1}", feed.Url, e);
                return new List<Item>();
            }
        }
        private List<Item> FillList(IEnumerable<XElement> elements, Feed feed, FeedType feedType)
        {
            List<Item> list = new List<Item>();
            int count = 0;

            foreach (XElement element in elements)
            {
                Item item = new Item();

                item.Id = Guid.NewGuid();
                item.Feed = feed;

                if (feedType == FeedType.Atom)
                {
                    string html = element.Elements().First(i => i.Name.LocalName == "content").Value;

                    item.Content = CleanContent(html);
                    item.RawHtml = html;
                    item.Link = element.Elements().First(i => i.Name.LocalName == "link").Attribute("href").Value;
                    item.PublishDate = ParseDate(element.Elements().First(i => i.Name.LocalName == "published").Value);
                    item.Title = CleanTitle(element.Elements().First(i => i.Name.LocalName == "title").Value);
                }
                else
                {
                    string html = element.Elements().First(i => i.Name.LocalName == "description").Value;

                    // <content:encoded> contains richer content
                    if (element.Elements().Any(i => i.Name.LocalName == "encoded"))
                        html = element.Elements().First(i => i.Name.LocalName == "encoded").Value;

                    // <enclosure> tag (RSS 2).
                    if (element.Elements().Any(i => i.Name.LocalName == "enclosure"))
                    {
                        XElement enclosure = element.Elements().First(i => i.Name.LocalName == "enclosure");
                        XAttribute enclosureUrl = enclosure.Attribute("url");
                        if (enclosureUrl != null)
                        {
                            item.ImageUrl = NormaliseImageUrl(feed.Url, enclosureUrl.Value);
                        }
                    }

                    item.Content = CleanContent(html);
                    item.RawHtml = html;
                    item.Link = element.Elements().First(i => i.Name.LocalName == "link").Value;
                    item.Title = CleanTitle(element.Elements().First(i => i.Name.LocalName == "title").Value);

                    XElement dateElement = null;

                    // The only difference we care about with RSS and RDF:
                    if (feedType == FeedType.RSS)
                        dateElement = element.Elements().FirstOrDefault(i => i.Name.LocalName == "pubDate");
                    else
                        dateElement = element.Elements().FirstOrDefault(i => i.Name.LocalName == "date");

                    if (dateElement != null)
                    {
                        item.PublishDate = ParseDate(dateElement.Value);

                        // Some naughty feeds set their publish date to the future
                        if (item.PublishDate > DateTime.UtcNow)
                            item.PublishDate = DateTime.UtcNow;
                    }
                    else
                    {
                        Logger.Info("Warning: {0} is set as {1} but no pubDate/date element was found for the PublishDate.", feed.Url, feed.FeedType);
                    }
                }

                // Check it has all the fields so it's not garbage
                if (!string.IsNullOrEmpty(item.Title) && !string.IsNullOrEmpty(item.Content) && !string.IsNullOrEmpty(item.Link))
                {
                    // Check it's not already in our list.
                    if (!AllItems.Exists(i => i.GetHashCode() == item.GetHashCode()))
                    {
                        // Restrict the database size over time to something sane
                        if (count >= _settings.MaximumItemsPerFeed)
                            break;

                        list.Add(item);
                    }

                    count++;
                }
            }

            return list;
        }