RSS news reader; initially copied from "all the news"
[phpeclipse.git] / archive / net.sourceforge.phpeclipse.news / src / net / sourceforge / phpeclipse / news / Item.java
1 package net.sourceforge.phpeclipse.news;
2
3 import java.text.DateFormat;
4 import java.text.SimpleDateFormat;
5 import java.util.Calendar;
6 import java.util.Date;
7 import java.util.GregorianCalendar;
8 import java.util.Locale;
9
10 import org.eclipse.swt.SWT;
11 import org.eclipse.swt.widgets.Table;
12 import org.eclipse.swt.widgets.TableItem;
13 import org.w3c.dom.Element;
14 import org.w3c.dom.Node;
15 import org.w3c.dom.NodeList;
16 import org.w3c.dom.Text;
17
18 /**
19  * @author jnegre - http://www.jnegre.org/
20  *
21  * (c)Copyright 2002 Jérôme Nègre
22  * 
23  */
24 public class Item {
25
26     protected static DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT);
27     protected static SimpleDateFormat pubDateParser = new SimpleDateFormat("EEE, d MMM yy hh:mm:ss z", new Locale("en","US"));
28
29     protected Channel channel;
30
31     protected String title;
32     protected String link;
33     protected String description;
34     protected String author;
35     protected String guid;
36     protected boolean isPermaLink = true;
37     protected String date;
38     
39     protected boolean readFlag = false;
40
41     /**
42      * Constructor for Item.
43      */
44     public Item(Channel channel, Element itemElement) {
45         this.channel = channel;
46         this.title = readValue("title", itemElement, 0);
47         this.link = readValue("link", itemElement, 0);
48         this.description = readValue("description", itemElement, 0);
49         this.author = readValue("author", itemElement, 0);
50         this.guid = readValue("guid", itemElement, 1);
51         String pubDate = readValue("pubDate", itemElement, 0);
52         String dcDate = readValue("dc:date", itemElement, 0);
53             
54         try {
55             Date theDate;
56             if(pubDate != null) {
57                 theDate = pubDateParser.parse(pubDate);
58             } else if(dcDate != null) {
59                 theDate = decodeDCDate(dcDate);
60             } else {
61                 theDate = new Date();
62             }
63             this.date = dateFormat.format(theDate);
64         } catch(Exception e) {
65             if(pubDate != null) {
66                 this.date = pubDate;
67             } else if(dcDate != null) {
68                 this.date = dcDate;
69             } else {
70                 this.date = e.toString();
71             }
72             Plugin.logInfo("Unable to parse date",e);
73         }
74     }
75
76     protected String readValue(String elementName, Element parent, int type) {
77         Element element = (Element)parent.getElementsByTagName(elementName).item(0);
78         if(element != null) {
79
80             switch(type) {
81                 case 1:
82                     if(element.hasAttribute("isPermaLink") && element.getAttribute("isPermaLink").equals("false")) {
83                         this.isPermaLink = false;
84                     }
85             }
86     
87             NodeList children = element.getChildNodes();
88             StringBuffer buffer = new StringBuffer();
89             for(int i=0; i<children.getLength(); i++) {
90                 Node node = children.item(i);
91                 if(node.getNodeType()==Node.TEXT_NODE || node.getNodeType()==Node.CDATA_SECTION_NODE) {
92                         buffer.append(((Text)node).getData());
93                 }
94             }
95             return buffer.toString().trim();
96         } else {
97             return null;
98         }
99     }
100
101     public String getUsableTitle() {
102         if(title != null) {
103             return title;
104         } else if (description != null) {
105             return description;
106         } else {
107             return "!! No title in feed !!";
108         }
109     }
110
111     public String getUsableLink() {
112         if(link != null) {
113             return link;
114         } else if (guid != null && isPermaLink) {
115             return guid;
116         } else {
117             return "about:blank";
118         }
119     }
120
121     public boolean isBanned() {
122         return Plugin.getDefault().isBannedTitle(title);
123     }
124
125     public TableItem toTableItem(Table table) {
126         TableItem tableItem = new TableItem(table, SWT.NONE);
127         fillTableItem(tableItem);
128         return tableItem;
129     }
130
131     public void fillTableItem(TableItem tableItem) {
132         tableItem.setText(new String[] {date,readFlag?"":"*",getUsableTitle()});
133         tableItem.setData(this);
134     }
135
136     /**
137      * Sets the readFlag and notifies the listeners
138      * that the status changed.
139      * @param readFlag The readFlag to set
140      */
141     public void setReadFlag(boolean readFlag) {
142         if(readFlag != this.readFlag) {
143             this.readFlag = readFlag;
144             channel.computeUnRead();
145         }
146     }
147
148     /**
149      * @see java.lang.Object#equals(Object)
150      */
151     public boolean equals(Object obj) {
152         return (obj instanceof Item)
153                 && ((Item)obj).getUID().equals(this.getUID());
154     }
155
156     protected static Date decodeDCDate(String string) throws Exception {
157         GregorianCalendar calendar = new GregorianCalendar(readInt(string,0,4),0,1,0,0,0);
158         calendar.set(Calendar.MILLISECOND,0);
159         calendar.set(Calendar.DST_OFFSET,0);
160         if(checkChar(string,4,'-')) {
161             calendar.set(Calendar.MONTH,readInt(string,5,2)-1);
162             if(checkChar(string,7,'-')) {
163                 calendar.set(Calendar.DATE,readInt(string,8,2));
164                 if(checkChar(string,10,'T')) {
165                     calendar.set(Calendar.HOUR_OF_DAY,readInt(string,11,2));
166                     calendar.set(Calendar.MINUTE,readInt(string,14,2));
167                     int length = string.length();
168                     int position = 16;
169                     
170                     //les secondes + millisecondes
171                     if(checkChar(string,16,':')) {
172                         calendar.set(Calendar.SECOND,readInt(string,17,2));
173                         position = 19;
174                         if(checkChar(string,position,'.')) {
175                             position += 1;
176                             StringBuffer millisecondBuffer = new StringBuffer("0.");
177                             while(position<length && Character.isDigit(string.charAt(position))) {
178                                 millisecondBuffer.append(string.charAt(position));
179                                 position += 1;
180                             }
181                             calendar.set(Calendar.MILLISECOND,(int)(Double.parseDouble(millisecondBuffer.toString())*1000));
182                         }
183
184                     }
185
186
187                     //TZD
188                     if(string.charAt(position) == 'Z') {
189                         calendar.set(Calendar.ZONE_OFFSET,0);
190                         if(length != position +1) {
191                             //trop de caractères
192                             throw new Exception("Invalid format of dc:date (extra tokens)");
193                         }
194                     } else if(string.charAt(position) == '+' || string.charAt(position) == '-') {
195                         int sign = 0;
196                         sign = string.charAt(position) == '+'?1:-1;
197                         int hour = readInt(string,position+1,2);
198                         int minute = readInt(string,position+4,2);
199                         calendar.set(Calendar.ZONE_OFFSET,sign*(hour*60*60*1000+minute*60*1000));
200                         if(length != position +6) {
201                             //trop de caractères
202                             throw new Exception("Invalid format of dc:date (extra tokens)");
203                         }
204                     } else {
205                         throw new Exception("Invalid format of dc:date (invalid TZD)");
206                     }
207                     
208                 }
209             }
210         }
211         return calendar.getTime();
212     }
213
214     private static int readInt(String buffer, int position, int length) {
215         int result = Integer.parseInt(buffer.substring(position,position+length));
216         return result;
217     }
218     
219     private static boolean checkChar(String buffer, int position, char expectedChar) {
220         if(buffer.length() > position && buffer.charAt(position) == expectedChar) {
221             return true;
222         } else {
223             return false;
224         }
225     }
226
227     /**
228      * @return the description of this item
229      */
230     public String getDescription() {
231         return description;
232     }
233     /**
234      * @return the author of this item
235      */
236     public String getAuthor() {
237         return author;
238     }
239
240     /**
241      * Returns the date.
242      * @return String
243      */
244     public String getDate() {
245         return date;
246     }
247
248     /**
249      * Returns the readFlag.
250      * @return boolean
251      */
252     public boolean isReadFlag() {
253         return readFlag;
254     }
255
256     /**
257      * Returns the channel.
258      * @return Channel
259      */
260     public Channel getChannel() {
261         return channel;
262     }
263
264     /**
265      * Returns a unique ID used to remember which
266      * items were read in the ChannelStore
267      * @return
268      */
269     public String getUID() {
270         return getUsableLink() + ") ~ (" + getUsableTitle();
271     }
272     
273 }