RSS 0.91 import support
[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   public static final String RDF_NAMESPACE_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
54   public static final String RSS_1_0_NAMESPACE_URI = "http://purl.org/rss/1.0/";
55   public static final String DUBLINCORE_NAMESPACE_URI = "http://purl.org/dc/elements/1.1/";
56   public static final String EVENT_NAMESPACE_URI = "http://purl.org/rss/1.0/modules/event/";
57   public 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
398         return DateTimeFunctions.parseW3CDTFString(expression);
399       }
400       catch (Throwable t) {
401
402         return null;
403       }
404     }
405   }
406
407
408   private class RDFSequenceSectionHandler extends XMLReader.AbstractSectionHandler {
409     private List items;
410
411     public RDFSequenceSectionHandler() {
412       items = new Vector();
413     }
414
415     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
416       if (aTag.equals("rdf:li")) {
417         String item = (String) anAttributes.get("rdf:resource");
418
419         if (item!=null)
420           items.add(item);
421       }
422
423       return new DiscardingSectionHandler();
424     };
425
426     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
427     };
428
429     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
430     };
431
432     public void finishSection() throws XMLReader.XMLReaderExc {
433     };
434
435     public List getItems() {
436       return items;
437     }
438   }
439
440   private class RDFLiteralSectionHandler extends PropertyValueSectionHandler {
441     private StringBuffer data;
442     private String tag;
443
444     public RDFLiteralSectionHandler() {
445       data = new StringBuffer();
446     }
447
448     protected StringBuffer getData() {
449       return data;
450     }
451
452     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
453       tag=aTag;
454       data.append("<"+tag+">");
455
456       return new RDFLiteralSectionHandler();
457     };
458
459     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
460       data.append(((RDFLiteralSectionHandler) aHandler).getData());
461       data.append("</"+tag+">");
462     };
463
464     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
465       data.append(aCharacters);
466     };
467
468     public void finishSection() throws XMLReader.XMLReaderExc {
469     };
470
471     public Object getValue() {
472       return data.toString();
473     }
474   }
475
476   private class DiscardingSectionHandler extends XMLReader.AbstractSectionHandler {
477     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
478       return this;
479     };
480
481     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
482     };
483
484     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
485     };
486
487     public void finishSection() throws XMLReader.XMLReaderExc {
488     };
489   }
490 }