scala-news-reader

rss/atom news reader in scala

git clone https://9o.is/git/scala-news-reader.git

Feed.scala

(2803B)


      1 package com.joereader.lib.rss
      2 
      3 import com.sun.syndication.feed.synd._
      4 import scala.collection.JavaConversions._
      5 import scala.collection.SortedSet
      6 
      7 /* An RSS Feed. */
      8 case class Feed(name: String,
      9   description: String,
     10   imageUrl: Option[String],
     11   writers: List[FeedAuthor],
     12   entries: List[FeedEntry],
     13   links: List[String])
     14 
     15 object Feed {
     16 
     17   def build(feed: SyndFeed, links: List[String]) = {
     18 
     19     val entries = FeedEntry.build(feed.getEntries.
     20       asInstanceOf[java.util.List[SyndEntry]].toList)
     21 
     22     def image =
     23       if (feed.getImage != null)
     24         Some(feed.getImage.getUrl)
     25       else None
     26 
     27     Feed(
     28       feed.getTitle,
     29       feed.getDescription,
     30       image,
     31       entries.map(_.author).distinct,
     32       entries,
     33       links)
     34   }
     35   
     36   def empty = Feed("", "", None, Nil, Nil, Nil)
     37 
     38   /**
     39    * Merges multiple feeds into one. If any feed contains one entry with same
     40    * title and date from an entry of another feed, the second feed will be
     41    * ignored.
     42    * @param feeds the feeds to merge
     43    * @return one merged feed
     44    */
     45   def merge(feeds: List[Feed]): Feed =
     46     if (feeds.nonEmpty) {
     47 
     48       def name(feeds: List[Feed]): String = {
     49         if (feeds.isEmpty) ""
     50         else if (!feeds.head.name.isEmpty) feeds.head.name
     51         else name(feeds.tail)
     52       }
     53 
     54       def description(feeds: List[Feed]): String = {
     55         if (feeds.isEmpty) ""
     56         else if (!feeds.head.description.isEmpty) feeds.head.description
     57         else description(feeds.tail)
     58       }
     59 
     60       def imageUrl(feeds: List[Feed]): Option[String] = {
     61         if (feeds.isEmpty) None
     62         else if (feeds.head.imageUrl.isDefined) feeds.head.imageUrl
     63         else imageUrl(feeds.tail)
     64       }
     65 
     66       var _writers = feeds.head.writers
     67       var _entries: List[FeedEntry] = Nil
     68       var _links: List[String] = Nil
     69 
     70       for (f <- 0 until feeds.size) {
     71         var repeat = false
     72         for (entry1 <- feeds(f).entries)
     73           for (entry2 <- _entries)
     74             if (entry1.title == entry2.title &&
     75               entry1.date.compareTo(entry2.date) == 0) repeat = true
     76 
     77         if (!repeat) {
     78           _writers = _writers ::: feeds(f).writers
     79           _entries = _entries ::: feeds(f).entries
     80           _links = _links ::: feeds(f).links
     81         }
     82       }
     83 
     84       // remove any repeated values
     85       _links = SortedSet(_links: _*).toList
     86       _writers = SortedSet(_writers: _*)(FeedAuthorOrdering).toList
     87 
     88       Feed(
     89         name(feeds),
     90         description(feeds),
     91         imageUrl(feeds),
     92         _writers,
     93         _entries,
     94         _links)
     95 
     96     } else Feed.empty
     97 }
     98 
     99 case class FeedImage(src: String)
    100 
    101 case class FeedAuthor(name: String, imgUrl: Option[String] = None)
    102 
    103 private object FeedAuthorOrdering extends Ordering[FeedAuthor] {
    104   def compare(a: FeedAuthor, b: FeedAuthor) = a.name compare b.name
    105 }