5a678255e35cf79bed29acfab50adba0b31dd013
[mir.git] / source / mir / rss / RSSReader.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30 package mir.rss;
31
32 import java.io.InputStream;
33 import java.net.URL;
34 import java.util.List;
35 import java.util.*;
36 import java.util.Vector;
37 import java.text.*;
38
39 import mir.util.XMLReader;
40 import mir.util.*;
41
42 /**
43  *
44  * <p>Title: </p>
45  * <p>Description: </p>
46  * <p>Copyright: Copyright (c) 2003</p>
47  * <p>Company: </p>
48  * @author not attributable
49  * @version 1.0
50  */
51
52 public class RSSReader {
53   private static final String RDF_NAMESPACE_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
54   private static final String RSS_1_0_NAMESPACE_URI = "http://purl.org/rss/1.0/";
55   private static final String DUBLINCORE_NAMESPACE_URI = "http://purl.org/dc/elements/1.1/";
56   private static final String EVENT_NAMESPACE_URI = "http://purl.org/rss/1.0/modules/event/";
57   private static final String TAXONOMY_NAMESPACE_URI = "http://web.resource.org/rss/1.0/modules/taxonomy/";
58
59   private static final XMLReader.XMLName RDF_ABOUT_PARAMETER = new XMLReader.XMLName(RDF_NAMESPACE_URI, "about");
60   private static final XMLReader.XMLName RDF_SEQUENCE_TAG = new XMLReader.XMLName(RDF_NAMESPACE_URI, "Seq");
61   private static final XMLReader.XMLName RDF_BAG_PARAMETER = new XMLReader.XMLName(RDF_NAMESPACE_URI, "Bag");
62
63   private static final XMLReader.XMLName RSS_CHANNEL_TAG = new XMLReader.XMLName(RSS_1_0_NAMESPACE_URI, "channel");
64   private static final XMLReader.XMLName RSS_ITEM_TAG = new XMLReader.XMLName(RSS_1_0_NAMESPACE_URI, "item");
65   private static final XMLReader.XMLName RSS_ITEMS_TAG = new XMLReader.XMLName(RSS_1_0_NAMESPACE_URI, "items");
66
67   private List modules;
68   private Map namespaceURItoModule;
69   private Map moduleToPrefix;
70   private RSSModule rdfModule;
71
72   public RSSReader() {
73     modules = new Vector();
74     namespaceURItoModule = new HashMap();
75     moduleToPrefix = new HashMap();
76
77     registerModule(new RSSBasicModule(RDF_NAMESPACE_URI, "RDF module"), "rdf");
78     registerModule(new RSSBasicModule(RSS_1_0_NAMESPACE_URI, "RSS 1.0 module"), "rss");
79
80     RSSBasicModule dcModule = new RSSBasicModule(DUBLINCORE_NAMESPACE_URI, "Dublin Core RSS module 1.1");
81     dcModule.addProperty("date", dcModule.W3CDTF_PROPERTY_TYPE);
82     registerModule(dcModule, "dc");
83
84     registerModule(new RSSBasicModule(EVENT_NAMESPACE_URI, "Event RSS module"), "ev");
85     registerModule(new RSSBasicModule(TAXONOMY_NAMESPACE_URI, "Taxonomy RSS module"), "taxo");
86   }
87
88   public void registerModule(RSSModule aModule, String aPrefix) {
89     modules.add(aModule);
90     namespaceURItoModule.put(aModule.getNamespaceURI(), aModule);
91     moduleToPrefix.put(aModule, aPrefix);
92   }
93
94   public RSSData parseInputStream(InputStream aStream) throws RSSExc, RSSFailure {
95     try {
96       XMLReader xmlReader = new XMLReader(true);
97       RSSData result = new RSSData();
98       xmlReader.parseInputStream(aStream, new RootSectionHandler(result));
99
100       return result;
101     }
102     catch (Throwable t) {
103       throw new RSSFailure(t);
104     }
105   }
106
107   public RSSData parseUrl(String anUrl) throws RSSExc, RSSFailure {
108     try {
109       InputStream inputStream = (InputStream) new URL(anUrl).getContent(new Class[] {InputStream.class});
110
111       if (inputStream==null)
112         throw new RSSExc("RSSChannel.parseUrl: Can't get url content");
113
114       return parseInputStream(inputStream);
115     }
116     catch (Throwable t) {
117       throw new RSSFailure(t);
118     }
119   }
120
121   private class RootSectionHandler extends XMLReader.AbstractSectionHandler {
122     private RSSData data;
123
124     public RootSectionHandler(RSSData aData) {
125       data = aData;
126     }
127
128     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
129       if (aTag.getLocalName().equals("RDF")) {
130         return new RDFSectionHandler(data);
131       }
132       else
133         throw new XMLReader.XMLReaderFailure(new RSSExc("'RDF' tag expected"));
134     };
135
136     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
137     };
138
139     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
140       if (aCharacters.trim().length()>0)
141         throw new XMLReader.XMLReaderExc("No character data allowed here");
142     };
143
144     public void finishSection() throws XMLReader.XMLReaderExc {
145     };
146   }
147
148   private class RDFSectionHandler extends XMLReader.AbstractSectionHandler {
149     private RSSData data;
150
151
152     public RDFSectionHandler(RSSData aData) {
153       data = aData;
154     }
155
156     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
157       String identifier = (String) anAttributes.get(RDF_ABOUT_PARAMETER);
158       String rdfClass = makeQualifiedName(aTag);
159
160       return new RDFResourceSectionHandler(rdfClass, identifier);
161     };
162
163     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
164       if (aHandler instanceof RDFResourceSectionHandler) {
165         data.addResource(((RDFResourceSectionHandler) aHandler).getResource());
166       }
167     };
168
169     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
170       if (aCharacters.trim().length()>0)
171         throw new XMLReader.XMLReaderExc("No character data allowed here");
172     };
173
174     public void finishSection() throws XMLReader.XMLReaderExc {
175     };
176   }
177
178   private XMLReader.SectionHandler makePropertyValueSectionHandler(XMLReader.XMLName aTag, Map anAttributes) {
179     RSSModule module = (RSSModule) namespaceURItoModule.get(aTag.getNamespaceURI());
180
181     if (aTag.getLocalName().equals("date"))
182       aTag = aTag;
183
184
185     if (module!=null) {
186       RSSModule.RSSModuleProperty property = module.getPropertyForName(aTag.getLocalName());
187
188       if (property!=null) {
189         switch (property.getType()) {
190           case
191             RSSModule.PCDATA_PROPERTY_TYPE:
192               return new PCDATASectionHandler();
193           case
194             RSSModule.RDFCOLLECTION_PROPERTY_TYPE:
195               return new RDFCollectionSectionHandler();
196 //          case
197 //            RSSModule.RDF_PROPERTY_TYPE:
198 //              return new RDFValueSectionHandler();
199           case
200             RSSModule.W3CDTF_PROPERTY_TYPE:
201               return new DateSectionHandler();
202         }
203       }
204
205     }
206
207     return new FlexiblePropertyValueSectionHandler();
208   }
209
210   private String makeQualifiedName(XMLReader.XMLName aName) {
211     String result=aName.getLocalName();
212     RSSModule module = (RSSModule) namespaceURItoModule.get(aName.getNamespaceURI());
213     if (module!=null) {
214       String prefix = (String) moduleToPrefix.get(module);
215
216       if (prefix!=null && prefix.length()>0)
217         result = prefix+":"+result;
218     }
219
220     return result;
221   }
222
223   private class RDFResourceSectionHandler extends XMLReader.AbstractSectionHandler {
224     private String image;
225     private XMLReader.XMLName currentTag;
226     private RDFResource resource;
227
228     public RDFResourceSectionHandler(String anRDFClass, String anIdentifier) {
229       resource = new RDFResource(anRDFClass, anIdentifier);
230     }
231
232     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
233       currentTag = aTag;
234
235       return makePropertyValueSectionHandler(aTag, anAttributes);
236     };
237
238     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
239       if (aHandler instanceof PropertyValueSectionHandler) {
240         resource.set(makeQualifiedName(currentTag), ( (PropertyValueSectionHandler) aHandler).getValue());
241       }
242     };
243
244     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
245       if (aCharacters.trim().length()>0)
246         throw new XMLReader.XMLReaderExc("No character data allowed here");
247     };
248
249     public void finishSection() throws XMLReader.XMLReaderExc {
250     };
251
252     public RDFResource getResource() {
253       return resource;
254     }
255   }
256
257   private abstract class PropertyValueSectionHandler extends XMLReader.AbstractSectionHandler {
258     public abstract Object getValue();
259   }
260
261   private class FlexiblePropertyValueSectionHandler extends PropertyValueSectionHandler {
262     private StringBuffer stringData;
263     private Object structuredData;
264
265     public FlexiblePropertyValueSectionHandler() {
266       stringData = new StringBuffer();
267       structuredData=null;
268     }
269
270     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
271       if (aTag.equals(RDF_SEQUENCE_TAG))
272         return new RDFSequenceSectionHandler();
273       else
274         return new DiscardingSectionHandler();
275     };
276
277     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
278       if (aHandler instanceof RDFSequenceSectionHandler) {
279         structuredData= ((RDFSequenceSectionHandler) aHandler).getItems();
280       }
281     };
282
283     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
284       stringData.append(aCharacters);
285     };
286
287     public void finishSection() throws XMLReader.XMLReaderExc {
288     };
289
290     public String getData() {
291       return stringData.toString();
292     }
293
294     public Object getValue() {
295       if (structuredData==null)
296         return stringData.toString();
297       else
298         return structuredData;
299     }
300   }
301
302   private class RDFCollectionSectionHandler extends PropertyValueSectionHandler {
303     private List items;
304
305     public RDFCollectionSectionHandler() {
306       items = new Vector();
307     }
308
309     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
310       if (aTag.equals(RDF_SEQUENCE_TAG))
311         return new RDFSequenceSectionHandler();
312       else
313         return new DiscardingSectionHandler();
314     };
315
316     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
317       if (aHandler instanceof RDFSequenceSectionHandler) {
318         items.addAll(((RDFSequenceSectionHandler) aHandler).getItems());
319       }
320     };
321
322     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
323       if (aCharacters.trim().length()>0)
324         throw new XMLReader.XMLReaderExc("No character data allowed here");
325     };
326
327     public void finishSection() throws XMLReader.XMLReaderExc {
328     };
329
330     public List getItems() {
331       return items;
332     }
333
334     public Object getValue() {
335       return items;
336     }
337   }
338
339   private class PCDATASectionHandler extends PropertyValueSectionHandler {
340     private StringBuffer data;
341
342     public PCDATASectionHandler() {
343       data = new StringBuffer();
344     }
345
346     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
347       throw new XMLReader.XMLReaderFailure(new RSSExc("No subtags allowed here"));
348     };
349
350     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
351     };
352
353     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
354       data.append(aCharacters);
355     };
356
357     public void finishSection() throws XMLReader.XMLReaderExc {
358     };
359
360     public String getData() {
361       return data.toString();
362     }
363
364     public Object getValue() {
365       return data.toString();
366     }
367   }
368
369   private class DateSectionHandler extends PropertyValueSectionHandler {
370     private StringBuffer data;
371
372     public DateSectionHandler() {
373       data = new StringBuffer();
374     }
375
376     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
377       throw new XMLReader.XMLReaderFailure(new RSSExc("No subtags allowed here"));
378     };
379
380     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
381     };
382
383     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
384       data.append(aCharacters);
385     };
386
387     public void finishSection() throws XMLReader.XMLReaderExc {
388     };
389
390     private final static String SPACE = "[\t\n\r ]*";
391     private final static String NUMBER = "[0-9]*";
392     private final static String SIGN = "[-+]";
393
394     public Object getValue() {
395       try {
396         String expression = data.toString().trim();
397         SimpleParser parser = new SimpleParser(expression);
398
399         String year="";
400         String month="";
401         String day="";
402         String hour="";
403         String minutes="";
404
405           year = parser.parse(NUMBER);
406           parser.skip("-");
407           month = parser.parse(NUMBER);
408           parser.skip("-");
409           day = parser.parse(NUMBER);
410           parser.skip("T");
411           hour = parser.parse(NUMBER);
412           parser.skip(":");
413           minutes = parser.parse(NUMBER);
414
415           SimpleDateFormat d = new SimpleDateFormat("yyyy-MM-dd HH:mm");
416           d.setLenient(true);
417           expression = year + "-" + month + "-" + day + " " + hour + ":" + minutes;
418
419         System.out.println(expression);
420
421         Date result = d.parse(expression);
422
423         return result;
424       }
425       catch (Throwable t) {
426         return null;
427       }
428     }
429   }
430
431
432   private class RDFSequenceSectionHandler extends XMLReader.AbstractSectionHandler {
433     private List items;
434
435     public RDFSequenceSectionHandler() {
436       items = new Vector();
437     }
438
439     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
440       if (aTag.equals("rdf:li")) {
441         String item = (String) anAttributes.get("rdf:resource");
442
443         if (item!=null)
444           items.add(item);
445       }
446
447       return new DiscardingSectionHandler();
448     };
449
450     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
451     };
452
453     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
454     };
455
456     public void finishSection() throws XMLReader.XMLReaderExc {
457     };
458
459     public List getItems() {
460       return items;
461     }
462   }
463
464   private class RDFLiteralSectionHandler extends PropertyValueSectionHandler {
465     private StringBuffer data;
466     private String tag;
467
468     public RDFLiteralSectionHandler() {
469       data = new StringBuffer();
470     }
471
472     protected StringBuffer getData() {
473       return data;
474     }
475
476     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
477       tag=aTag;
478       data.append("<"+tag+">");
479
480       return new RDFLiteralSectionHandler();
481     };
482
483     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
484       data.append(((RDFLiteralSectionHandler) aHandler).getData());
485       data.append("</"+tag+">");
486     };
487
488     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
489       data.append(aCharacters);
490     };
491
492     public void finishSection() throws XMLReader.XMLReaderExc {
493     };
494
495     public Object getValue() {
496       return data.toString();
497     }
498   }
499
500   private class DiscardingSectionHandler extends XMLReader.AbstractSectionHandler {
501     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
502       return this;
503     };
504
505     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
506     };
507
508     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
509     };
510
511     public void finishSection() throws XMLReader.XMLReaderExc {
512     };
513   }
514 }