Source code: com/fm/rss/rssChannel.java
1 /****************************************************************************
2 * Copyright (c) 2003 Andrew Duka | aduka@users.sourceforge.net
3 * All right reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 ****************************************************************************/
11 package com.fm.rss;
12
13 import java.util.*;
14
15 import org.w3c.dom.*;
16 import com.fm.update.Updateable;
17 import com.fm.update.updateException;
18 import com.fm.rss.filter.RssItemFilter;
19 import com.fm.rss.filter.rssFilterFactory;
20
21 import javax.xml.parsers.ParserConfigurationException;
22
23 /**
24 * Entities of the class represent RSS channels.
25 *
26 * @author Andrew Duka (Andrew.Duka@oktet.ru)
27 */
28 public class rssChannel extends rssAbstractEntry implements ItemContainer, Updateable
29 {
30 private HashMap itemList; /** array of the channels items */
31
32 private String link; /** channel parent URI */
33 private String source; /** channel RSS feed source URI */
34 private String creator; /** channel source URI */
35
36 private String language; /** channel source URI */
37 private int ttl; /** number of minutes a channel may be cached */
38 private String imageTitle; /** image title if image element is present,
39 empty string otherwise */
40 private String imageURL;
41 private String imageLink;
42
43 private int keepPeriod; /** number of days item may be stored.
44 if item is older than keepPeriod days it wont
45 be loaded or saved */
46
47 private boolean isUpdateOperation;
48
49 public static int MAX_KEEP_PERIOD = Integer.MAX_VALUE; /** enough days for human history */
50
51
52 public rssChannel()
53 {
54 title = "";
55 description = "";
56 dateCreated = new Date();
57 idHash = 0;
58 creator = "";
59 link = "";
60 language = "en-us";
61 source = "";
62 itemList = new HashMap();
63 imageLink = "";
64 imageTitle = "";
65 imageURL = "";
66 ttl = -1;
67 isUpdateOperation = false;
68 keepPeriod = rssChannel.MAX_KEEP_PERIOD;
69 }
70
71
72 /**
73 * Returns channel as XML document element
74 *
75 * @return DOM Element object with representation of the item container
76 */
77 public Element toDomElement()
78 {
79 Element root_elem;
80 Element temp;
81 Element sub_entry = null;
82
83 if (this.docAdapter == null)
84 {
85 try
86 {
87 docAdapter = documentAdapter.newInstance();
88 }
89 catch (ParserConfigurationException pce)
90 {
91 //catch exception here
92 pce.printStackTrace();
93 return null;
94 }
95 }
96
97 root_elem = this.docAdapter.createElement("rss-channel");
98
99 try
100 {
101 // ID & Version
102 root_elem.setAttribute("id", Integer.toString(this.getID()));
103 root_elem.setAttribute("version", "1.0");
104 // Title, Description, dateCreated
105 temp = this.docAdapter.createElement("title", "dc",
106 documentAdapter.DC_NAMESPACE_URI);
107 temp.appendChild(this.docAdapter.createTextNode(getTitle()));
108 root_elem.appendChild(temp);
109
110 temp = this.docAdapter.createElement("description", "dc",
111 documentAdapter.DC_NAMESPACE_URI);
112
113 String descr = getDescription();
114 temp.appendChild(this.docAdapter.createTextNode(descr));
115 root_elem.appendChild(temp);
116
117 temp = this.docAdapter.createElement("source", "dc",
118 documentAdapter.DC_NAMESPACE_URI);
119 temp.appendChild(this.docAdapter.createTextNode(this.getSource()));
120 root_elem.appendChild(temp);
121
122
123 temp = this.docAdapter.createElement("dateCreated");
124 temp.appendChild(this.docAdapter.
125 createTextNode(rssDateHandler.
126 dateToString(getDateCreated(),
127 rssDateHandler.RSS_OUTPUT_PATTERN)));
128 root_elem.appendChild(temp);
129
130 temp = this.docAdapter.createElement("language", "dc",
131 documentAdapter.DCTERMS_NAMESPACE_URI);
132 temp.appendChild(this.docAdapter.createTextNode(this.getLanguage()));
133 root_elem.appendChild(temp);
134
135
136 temp = this.docAdapter.createElement("creator", "dc",
137 documentAdapter.DC_NAMESPACE_URI);
138 temp.appendChild(this.docAdapter.createTextNode(this.getCreator()));
139 root_elem.appendChild(temp);
140
141 temp = this.docAdapter.createElement("link");
142 temp.appendChild(this.docAdapter.createTextNode(this.getLink()));
143 root_elem.appendChild(temp);
144
145 temp = this.docAdapter.createElement("keepPeriod");
146 temp.appendChild(this.docAdapter.createTextNode((new Integer(this.getKeepPeriod())).toString()));
147 root_elem.appendChild(temp);
148
149 //have image assoicated
150 if (this.imageURL.length() > 0)
151 {
152 temp = this.docAdapter.createElement("image");
153 Element i_tel = this.docAdapter.createElement("url");
154 i_tel.appendChild(this.docAdapter.createTextNode(this.getImageURL()));
155 temp.appendChild(i_tel);
156
157 if (this.imageTitle.length() > 0)
158 {
159 i_tel = this.docAdapter.createElement("title");
160 i_tel.appendChild(this.docAdapter.createTextNode(this.getImageTitle()));
161 temp.appendChild(i_tel);
162 }
163
164 if (this.imageLink.length() > 0)
165 {
166 i_tel = this.docAdapter.createElement("link");
167 i_tel.appendChild(this.docAdapter.createTextNode(this.getImageLink()));
168 temp.appendChild(i_tel);
169 }
170 root_elem.appendChild(temp);
171 }
172
173 // have ttl
174 if (this.ttl > 0)
175 {
176 temp = this.docAdapter.createElement("ttl");
177 temp.appendChild(this.docAdapter.createTextNode(Integer.toString(this.ttl)));
178 root_elem.appendChild(temp);
179 }
180 //System.out.println("ROOT: " + root_elem.toString());
181
182 // dumping channel items
183 synchronized (itemList)
184 {
185 if (keepPeriod != 0)
186 {
187 Vector v = getItemVector();
188
189 if (keepPeriod < MAX_KEEP_PERIOD)
190 {
191 Calendar c = Calendar.getInstance();
192 int day = c.get(Calendar.DAY_OF_YEAR) - keepPeriod;
193 c.set(Calendar.DAY_OF_YEAR, day);
194 RssItemFilter f = rssFilterFactory.createDateFilter(c.getTime());
195 v = f.filter(v);
196 }
197
198 for (Iterator i = v.iterator(); i.hasNext();)
199 {
200 sub_entry = ((rssAbstractEntry)i.next()).toDomElement();
201 if (sub_entry != null)
202 {
203 root_elem.appendChild(sub_entry);
204 }
205 else
206 {
207 //possibly report error here
208 }
209 }
210 }
211 }
212 }
213 catch (DOMException e)
214 {
215 e.printStackTrace();
216 return null;
217 }
218
219 return root_elem;
220 }
221
222
223 /**
224 * Update channel items. This method will connect to the server,
225 * retreive RSS feed and aggregate new items (items which have been
226 * published after last time this method had been called).
227 *
228 * @throws com.fm.update.updateException if error occured during update
229 */
230 public void update() throws updateException
231 {
232 isUpdateOperation = true;
233
234 try
235 {
236 parse(getSource());
237 isUpdateOperation = false; // restoring
238 }
239 catch (rssParseException e)
240 {
241 isUpdateOperation = false; // restoring
242 throw new updateException(e.toString());
243 }
244 }
245
246
247 /**
248 * Returns Map of the channel items.
249 *
250 * @return ArrayList object with the channel items
251 */
252 public Map getItems()
253 {
254 return itemList;
255 }
256
257 /**
258 * Return Vector of the items
259 *
260 * Items are unsorted and if user wants the sorted list, the sorting filter
261 * should be applied. The items in the list may be sorted naturally, due to
262 * extraction order (items appear in the list as they appear in the channel),
263 * but such implementation nuances mustn't be taken seriously.
264 *
265 * @return
266 */
267 public Vector getItemVector()
268 {
269 return new Vector(itemList.values());
270 }
271
272 /**
273 * Replaces the channel's item list with given one.
274 *
275 * @param newIMap new Map with the items
276 *
277 */
278 public void setItemList(Map newIMap)
279 {
280 itemList = new HashMap(newIMap);
281 }
282
283
284 /**
285 * Parse given Element object and initialize channel params and item list.
286 *
287 * <p>As soon as there are more than one description format for RSS channels,
288 * the parse method actually does only simple checks for description format
289 * and then forwards given Element object to the appropriate private method.
290 * The three major description formats are: RSSv1, RSSv2 (by Userland), JNR
291 * (internal format based on DublinCore elements, which intended for the
292 * local storing of the channels,categories and items data). Since RSSv2
293 * backward compatible with 0.9x formats, the channels described using 0.9x
294 * format will be handled with <code>parseRSSv2()</code>.</p>
295 *
296 * @param el
297 * @throws com.fm.rss.rssParseException
298 */
299 public synchronized void parse(Element el) throws rssParseException
300 {
301 String el_t = el.getNodeName();
302
303 if (el_t.equalsIgnoreCase("rdf:RDF")) // RSS1
304 {
305 try
306 {
307 this.parseRSSv1(el);
308 }
309 catch (rssParseException pe)
310 {
311 pe.printStackTrace();
312 throw new rssParseException("RSSv1 parse exception: " + pe.toString());
313 }
314 }
315 else if (el_t.equalsIgnoreCase("rss-channel")) // JNR
316 {
317 try
318 {
319 this.parseJNRFormat(el);
320 }
321 catch (rssParseException pe)
322 {
323 throw new rssParseException("JNR format parse exception: " + pe.toString());
324 }
325 }
326 else if (el_t.equalsIgnoreCase("rss")) // JNR
327 {
328 try
329 {
330 this.parseRSSv2(el);
331 }
332 catch (rssParseException pe)
333 {
334 throw new rssParseException("RSS format parse exception: " + pe.toString());
335 }
336 }
337 }
338
339 /**
340 * Parse element object representing RSS channel encoded using RSS v1
341 * specification
342 *
343 * @param el Element object
344 * @throws com.fm.rss.rssParseException
345 */
346 private void parseRSSv1(Element el) throws rssParseException
347 {
348 Node curr_node;
349 Node temp; /* All optional elements may return null as a value,
350 so we need temporal string to check this out */
351
352 NodeList t_nl = el.getElementsByTagName("channel");
353
354 if (t_nl.getLength() == 0)
355 {
356 throw new rssParseException("RSS channel: invalid RSS1 channel spec - channel element is absent");
357 }
358
359 String node_name;
360 NodeList nl = (t_nl.item(0)).getChildNodes();
361 int child_num = nl.getLength();
362
363 for (int i=0; i < child_num; i++)
364 {
365 curr_node = nl.item(i);
366
367 if (curr_node.getNodeType() == Node.ELEMENT_NODE)
368 {
369 node_name = curr_node.getNodeName();
370 if (node_name.equalsIgnoreCase("title"))
371 {
372 if ((temp = curr_node.getFirstChild()) != null)
373 this.setTitle(temp.getNodeValue());
374 else
375 this.setTitle("");
376 }
377 else if (node_name.equalsIgnoreCase("description"))
378 {
379 if ((temp = curr_node.getFirstChild()) != null)
380 this.setDescription(temp.getNodeValue());
381 else
382 this.setDescription("");
383 }
384 else if (node_name.equalsIgnoreCase("dc:date"))
385 {
386 if ((temp = curr_node.getFirstChild()) != null)
387 setDateCreated(rssDateHandler.getDateInstanceFromDC(temp.getNodeValue()));
388 }
389 else if (node_name.equalsIgnoreCase("dc:creator"))
390 {
391 if ((temp = curr_node.getFirstChild()) != null)
392 this.setCreator(temp.getNodeValue());
393 else
394 this.setCreator("");
395 }
396 else if (node_name.equalsIgnoreCase("link"))
397 {
398 if ((temp = curr_node.getFirstChild()) != null)
399 this.setLink(temp.getNodeValue());
400 else
401 throw new rssParseException("RSS channel: link element contains no data");
402 }
403 else if (node_name.equalsIgnoreCase("dc:language"))
404 {
405 if ((temp = curr_node.getFirstChild()) != null)
406 this.setLanguage(temp.getNodeValue());
407 else
408 this.setLanguage("en-us"); // default value
409 }
410 }//end of ELEMENT_NODE
411 }//end of for
412
413 //IMAGE
414 nl = el.getElementsByTagName("image");
415 child_num = nl.getLength();
416 for (int i=0; i < child_num; i++)
417 {
418 curr_node = nl.item(i);
419
420 // we need only complete image description on nothing at all
421 if (curr_node.getParentNode() != el)
422 {
423 continue;
424 }
425
426 NamedNodeMap atts = curr_node.getAttributes();
427 Node t_node;
428 boolean isNoAbout = true;
429
430 if ((atts != null) && ((t_node = atts.getNamedItem("rdf:about")) != null))
431 {
432 this.setImageURL(t_node.getNodeValue());
433 isNoAbout = false;
434 }
435
436 NodeList t_ls = curr_node.getChildNodes();
437 String t_name;
438
439 for (int t_i = 0; t_i < t_ls.getLength(); t_i++)
440 {
441 t_node = t_ls.item(t_i);
442
443 if (t_node.getNodeType() == Node.ELEMENT_NODE)
444 {
445 //no parse exceptions, only known values will be set
446 //image url is set using about attribute
447 t_name = curr_node.getNodeName();
448 if (t_name.equalsIgnoreCase("title"))
449 {
450 if ((temp = t_node.getFirstChild()) != null)
451 this.setImageTitle(temp.getNodeValue());
452 }
453 if (t_name.equalsIgnoreCase("link") && (isNoAbout == true))
454 {
455 if ((temp = t_node.getFirstChild()) != null)
456 {
457 this.setImageLink(temp.getNodeValue());
458 isNoAbout = false;
459 }
460 }
461 }
462 }
463 if (isNoAbout)
464 {
465 // possibly should be removed, because image element is optional
466 throw new rssParseException("RSS v1 parse error: image source isn't specified");
467 }
468
469 }
470
471 // ITEMS
472 nl = el.getElementsByTagName("item");
473 child_num = nl.getLength();
474 rssItem new_i;
475
476 for (int i=0; i < child_num; i++)
477 {
478 curr_node = nl.item(i);
479 new_i = new rssItem();
480
481 try
482 {
483 new_i.parse((Element)curr_node);
484 new_i.setID(new_i.generateID());
485 }
486 catch (rssParseException pe)
487 {
488 throw new rssParseException("rssChannel parse error: "+ pe.toString());
489 }
490
491 this.addItem(new_i);
492 //System.out.println("Item description: " + new_i.getDescription());
493
494 }//end of for
495 }
496
497 /**
498 * Parse element object representing RSS channels in RSSv2 (and >= 0.91) format.
499 *
500 * @param el Element object
501 * @throws com.fm.rss.rssParseException
502 */
503 private void parseRSSv2(Element el) throws rssParseException
504 {
505 Node curr_node;
506 Node temp; /* All optional elements may return null as a value,
507 so we need temporal string to check this out */
508
509 NodeList t_nl = el.getElementsByTagName("channel");
510
511 if (t_nl.getLength() == 0)
512 {
513 throw new rssParseException("RSS channel: invalid RSS2 channel spec - channel element is absent");
514 }
515
516 String node_name;
517 rssItem new_i;
518 NodeList nl = (t_nl.item(0)).getChildNodes();
519 int child_num = nl.getLength();
520
521 for (int i=0; i < child_num; i++)
522 {
523 curr_node = nl.item(i);
524
525 if (curr_node.getNodeType() == Node.ELEMENT_NODE)
526 {
527 node_name = curr_node.getNodeName();
528 if (node_name.equalsIgnoreCase("title"))
529 {
530 if ((temp = curr_node.getFirstChild()) != null)
531 this.setTitle(temp.getNodeValue());
532 else
533 this.setTitle("");
534 }
535 else if (node_name.equalsIgnoreCase("description"))
536 {
537 if ((temp = curr_node.getFirstChild()) != null)
538 this.setDescription(temp.getNodeValue());
539 else
540 this.setDescription("");
541 }
542 else if (node_name.equalsIgnoreCase("link"))
543 {
544 if ((temp = curr_node.getFirstChild()) != null)
545 this.setLink(temp.getNodeValue());
546 else
547 throw new rssParseException("RSS channel: link element contains no data");
548 }
549 else if (node_name.equalsIgnoreCase("pubDate"))
550 {
551 if ((temp = curr_node.getFirstChild()) != null)
552 setDateCreated(rssDateHandler.getDateInstanceFromRSS(temp.getNodeValue()));
553 }
554 else if (node_name.equalsIgnoreCase("managingEditor"))
555 {
556 if ((temp = curr_node.getFirstChild()) != null)
557 this.setCreator(temp.getNodeValue());
558 else
559 this.setCreator("");
560 }
561 else if (node_name.equalsIgnoreCase("image")) //we've got image
562 {
563 Node t_node;
564 NodeList t_ls = curr_node.getChildNodes();
565 String t_name;
566
567 for (int t_i = 0; t_i < t_ls.getLength(); t_i++)
568 {
569 t_node = t_ls.item(t_i);
570
571 if (t_node.getNodeType() == Node.ELEMENT_NODE)
572 {
573 //no parse exceptions, only known values will be set
574 //image url is set using about attribute
575 t_name = curr_node.getNodeName();
576 if (t_name.equalsIgnoreCase("title"))
577 {
578 if ((temp = t_node.getFirstChild()) != null)
579 this.setImageTitle(temp.getNodeValue());
580 }
581 if (t_name.equalsIgnoreCase("link"))
582 {
583 if ((temp = t_node.getFirstChild()) != null)
584 this.setImageLink(temp.getNodeValue());
585 }
586 if (t_name.equalsIgnoreCase("url"))
587 {
588 if ((temp = t_node.getFirstChild()) != null)
589 this.setImageURL(temp.getNodeValue());
590 }
591 }
592 }
593 }
594 else if (node_name.equalsIgnoreCase("language"))
595 {
596 if ((temp = curr_node.getFirstChild()) != null)
597 this.setLanguage(temp.getNodeValue());
598 else
599 this.setLanguage("en-us"); // default value
600 }
601 else if (node_name.equalsIgnoreCase("item"))
602 {
603 new_i = new rssItem();
604 try
605 {
606 new_i.parse((Element)curr_node);
607 new_i.setID(new_i.generateID());
608 }
609 catch (rssParseException pe)
610 {
611 throw new rssParseException("rssChannel parse error: "+ pe.toString());
612 }
613
614 this.addItem(new_i);
615 }
616 }//end of ELEMENT_NODE
617 }//end of for
618 }
619
620 /**
621 * Parse element object representing RSS channel encoded using RSS v1
622 * specification
623 *
624 * @param el Element object
625 * @throws com.fm.rss.rssParseException
626 */
627 private void parseJNRFormat(Element el) throws rssParseException
628 {
629 Node curr_node;
630 Node temp;
631 NodeList nl = el.getChildNodes();
632 String node_name;
633 rssItem new_i;
634 int child_num = nl.getLength();
635
636 Attr id_attr = el.getAttributeNode("id");
637 String idstr;
638 if ((id_attr == null) || ((idstr = id_attr.getValue()) == null))
639 {
640 throw new rssParseException("Channel parse error: rss-channel ID isn't specified");
641 }
642
643 // SETTING ID
644 int id;
645 try
646 {
647 id = Integer.parseInt(idstr);
648 this.setID(id);
649 }
650 catch (NumberFormatException nfe)
651 {
652 throw new rssParseException("Channel parse error: invalid ID format or can't convert string into int value");
653 }
654
655
656 for (int i=0; i < child_num; i++)
657 {
658 curr_node = nl.item(i);
659
660 if (curr_node.getNodeType() == Node.ELEMENT_NODE)
661 {
662 node_name = curr_node.getNodeName();
663 if (node_name.equalsIgnoreCase("dc:title"))
664 {
665 if ((temp = curr_node.getFirstChild()) != null)
666 this.setTitle(temp.getNodeValue());
667 else
668 this.setTitle("");
669 }
670 else if (node_name.equalsIgnoreCase("dc:description"))
671 {
672 if ((temp = curr_node.getFirstChild()) != null)
673 this.setDescription(temp.getNodeValue());
674 else
675 this.setDescription("");
676 }
677 else if (node_name.equalsIgnoreCase("dateCreated"))
678 {
679 if ((temp = curr_node.getFirstChild()) != null)
680 setDateCreated(rssDateHandler.getDateInstanceFromRSS(temp.getNodeValue()));
681 }
682 else if (node_name.equalsIgnoreCase("dc:creator"))
683 {
684 if ((temp = curr_node.getFirstChild()) != null)
685 this.setCreator(temp.getNodeValue());
686 else
687 this.setCreator("");
688 }
689 else if (node_name.equalsIgnoreCase("dc:source"))
690 {
691 if ((temp = curr_node.getFirstChild()) != null)
692 this.setSource(temp.getNodeValue());
693 else
694 {
695 throw new rssParseException("JNR format parse error: dc:source element is empty");
696 }
697
698 }
699 else if (node_name.equalsIgnoreCase("link"))
700 {
701 if ((temp = curr_node.getFirstChild()) != null)
702 this.setLink(temp.getNodeValue());
703 else
704 throw new rssParseException("RSS channel: link element contains no data");
705 }
706 else if (node_name.equalsIgnoreCase("keepPeriod"))
707 {
708 if ((temp = curr_node.getFirstChild()) != null)
709 this.setKeepPeriod(Integer.parseInt(temp.getNodeValue()));
710 }
711 else if (node_name.equalsIgnoreCase("image")) //we've got image
712 {
713 Node t_node;
714 NodeList t_ls = curr_node.getChildNodes();
715 String t_name;
716
717 for (int t_i = 0; t_i < t_ls.getLength(); t_i++)
718 {
719 t_node = t_ls.item(t_i);
720
721 if (t_node.getNodeType() == Node.ELEMENT_NODE)
722 {
723 //no parse exceptions, only known values will be set
724 //image url is set using about attribute
725 t_name = curr_node.getNodeName();
726 if (t_name.equalsIgnoreCase("title"))
727 {
728 if ((temp = t_node.getFirstChild()) != null)
729 this.setImageTitle(temp.getNodeValue());
730 }
731 if (t_name.equalsIgnoreCase("link"))
732 {
733 if ((temp = t_node.getFirstChild()) != null)
734 this.setImageLink(temp.getNodeValue());
735 }
736 if (t_name.equalsIgnoreCase("url"))
737 {
738 if ((temp = t_node.getFirstChild()) != null)
739 this.setImageURL(temp.getNodeValue());
740 }
741 }
742 }
743 }
744 else if (node_name.equalsIgnoreCase("ttl"))
745 {
746 if ((temp = curr_node.getFirstChild()) != null)
747 this.setTtl(Integer.parseInt(temp.getNodeValue()));
748 }
749 else if (node_name.equalsIgnoreCase("dc:language"))
750 {
751 if ((temp = curr_node.getFirstChild()) != null)
752 this.setLanguage(temp.getNodeValue());
753 else
754 this.setLanguage("en-us"); // default value
755 }
756 else if (node_name.equalsIgnoreCase("item"))
757 {
758 new_i = new rssItem();
759 try
760 {
761 new_i.parse((Element)curr_node);
762 new_i.setID(new_i.generateID());
763 }
764 catch (rssParseException pe)
765 {
766 throw new rssParseException("rssChannel parse error: "+ pe.toString());
767 }
768
769 this.addItem(new_i);
770 }
771 }//end of ELEMENT_NODE
772 }//end of for
773
774 }
775
776 /**
777 * Adds new RSS item to the channel's item list
778 *
779 * @param ni new item
780 *
781 * @return boolean true if item was added, false
782 * otherwise (item already exists or add failure)
783 */
784 public boolean addItem(rssItem ni)
785 {
786 synchronized (itemList)
787 {
788 if (itemList.containsKey(new Integer(ni.getID())))
789 {
790 return false;
791 }
792 else
793 {
794 // MAX - means we're keep all, 0 - will be rejected on saving
795 if (keepPeriod == MAX_KEEP_PERIOD || keepPeriod == 0) {
796 itemList.put(new Integer(ni.getID()),ni);
797 }
798 else {
799 Calendar c = Calendar.getInstance();
800 int day = c.get(Calendar.DAY_OF_YEAR) - keepPeriod;
801 c.set(Calendar.DAY_OF_YEAR, day);
802
803 if (ni.getDateCreated().compareTo(c.getTime()) >= 0) {
804 itemList.put(new Integer(ni.getID()),ni);
805 }
806 }
807
808 }
809 }
810 return true;
811 }
812
813 /**
814 * Overrides default from AbstractEntry
815 * @param title
816 */
817 public void setTitle(String title)
818 {
819 if (isUpdateOperation == false)
820 this.title = new String(title);
821 }
822
823 /**
824 * Overrides default from AbstractEntry
825 * @param description
826 */
827 public void setDescription(String description)
828 {
829 if (isUpdateOperation == false)
830 this.description = new String(description);
831 }
832
833 public String getSource()
834 {
835 return source;
836 }
837
838 public void setSource(String source)
839 {
840 if (isUpdateOperation == false)
841 this.source = new String(source);
842 }
843
844 public String getLanguage()
845 {
846 return language;
847 }
848
849 public void setLanguage(String language)
850 {
851 this.language = new String(language);
852 }
853
854 public String getCreator()
855 {
856 return creator;
857 }
858
859 public void setCreator(String creator)
860 {
861 this.creator = new String(creator);
862 }
863
864 public String getLink()
865 {
866 return link;
867 }
868
869 public void setLink(String link)
870 {
871 this.link = new String(link);
872 }
873
874 public int getTtl()
875 {
876 return ttl;
877 }
878
879 public void setTtl(int ttl)
880 {
881 if (isUpdateOperation == false)
882 this.ttl = ttl;
883 }
884
885 public String getImageTitle()
886 {
887 return imageTitle;
888 }
889
890 public void setImageTitle(String imageTitle)
891 {
892 this.imageTitle = new String(imageTitle);
893 }
894
895 public String getImageURL()
896 {
897 return imageURL;
898 }
899
900 public void setImageURL(String imageURL)
901 {
902 this.imageURL = new String(imageURL);
903 }
904
905 public String getImageLink()
906 {
907 return imageLink;
908 }
909
910 public void setImageLink(String imageLink)
911 {
912 this.imageLink = new String(imageLink);
913 }
914
915 /**
916 * Returns true if channel has unread(fresh) items
917 *
918 */
919 public boolean isFresh()
920 {
921 synchronized (itemList) {
922 for (Iterator i = itemList.keySet().iterator(); i.hasNext();)
923 if (((rssItem) itemList.get(i.next())).isFresh)
924 return true;
925 }
926 return false;
927 }
928
929
930 /**
931 * Set keep period in days
932 */
933 public void setKeepPeriod(int p) {
934 keepPeriod = p;
935 }
936
937 /**
938 * Get keep period in days
939 */
940 public int getKeepPeriod() {
941 return keepPeriod;
942 }
943
944 /**
945 * Filter items
946 *
947 * <p>Remove unwanted items using keepPeriod rule</p>
948 */
949 public void filterItems() {
950 if ((keepPeriod > 0) && (keepPeriod < MAX_KEEP_PERIOD))
951 {
952 Calendar c = Calendar.getInstance();
953 int day = c.get(Calendar.DAY_OF_YEAR) - keepPeriod;
954 c.set(Calendar.DAY_OF_YEAR, day);
955 Date d = c.getTime();
956
957 synchronized (itemList) {
958 for (Iterator i = itemList.keySet().iterator(); i.hasNext();)
959 {
960 if (((rssItem) itemList.get(i.next())).getDateCreated().compareTo(d) < 0)
961 i.remove();
962 }
963 }
964 }
965 }
966
967 /**
968 * Filter items using specified filter
969 *
970 * @param f
971 */
972 public void filterItems(RssItemFilter f) {
973
974 if (f == null)
975 return;
976
977 Vector v = f.filter(getItemVector());
978 itemList = new HashMap();
979 rssItem item = null;
980
981 for (int i=0, n = v.size(); i < n; i++) {
982 item = (rssItem)v.get(i);
983 itemList.put(new Integer(item.getID()), item);
984 }
985
986
987 }
988
989
990
991 }