f2653b90187ff78d544300a06c70a40d2ba47259
[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 RSS_0_9_NAMESPACE_URI = "http://my.netscape.com/rdf/simple/0.9/";
56   public static final String DUBLINCORE_NAMESPACE_URI = "http://purl.org/dc/elements/1.1/";
57   public static final String EVENT_NAMESPACE_URI = "http://purl.org/rss/1.0/modules/event/";
58   public static final String TAXONOMY_NAMESPACE_URI = "http://web.resource.org/rss/1.0/modules/taxonomy/";
59   public static final String DUBLINCORE_TERMS_NAMESPACE_URI = "http://purl.org/dc/terms/";
60
61
62   // ML: to be localized:
63   public static final String V2V_NAMESPACE_URI = "http://v2v.indymedia.de/rss/";
64
65   private static final XMLReader.XMLName RDF_ABOUT_PARAMETER = new XMLReader.XMLName(RDF_NAMESPACE_URI, "about");
66   private static final XMLReader.XMLName RDF_SEQUENCE_TAG = new XMLReader.XMLName(RDF_NAMESPACE_URI, "Seq");
67   private static final XMLReader.XMLName RDF_BAG_PARAMETER = new XMLReader.XMLName(RDF_NAMESPACE_URI, "Bag");
68
69   private static final XMLReader.XMLName RSS_CHANNEL_TAG = new XMLReader.XMLName(RSS_1_0_NAMESPACE_URI, "channel");
70   private static final XMLReader.XMLName RSS_ITEM_TAG = new XMLReader.XMLName(RSS_1_0_NAMESPACE_URI, "item");
71   private static final XMLReader.XMLName RSS_ITEMS_TAG = new XMLReader.XMLName(RSS_1_0_NAMESPACE_URI, "items");
72
73   private List modules;
74   private Map namespaceURItoModule;
75   private Map moduleToPrefix;
76
77   public RSSReader() {
78     modules = new Vector();
79     namespaceURItoModule = new HashMap();
80     moduleToPrefix = new HashMap();
81
82     registerModule(new RSSBasicModule(RDF_NAMESPACE_URI, "RDF module"), "rdf");
83     registerModule(new RSSBasicModule(RSS_1_0_NAMESPACE_URI, "RSS 1.0 module"), "rss");
84     registerModule(new RSSBasicModule(RSS_0_9_NAMESPACE_URI, "RSS 0.9 module"), "rss");
85
86     RSSBasicModule dcModule = new RSSBasicModule(DUBLINCORE_NAMESPACE_URI, "RSS Dublin Core 1.1");
87     dcModule.addProperty("date", RSSModule.W3CDTF_PROPERTY_TYPE);
88     registerModule(dcModule, "dc");
89
90     RSSBasicModule dcTermsModule = new RSSBasicModule(DUBLINCORE_TERMS_NAMESPACE_URI, "RSS Qualified Dublin core");
91     dcTermsModule.addProperty("created", RSSModule.W3CDTF_PROPERTY_TYPE);
92     dcTermsModule.addProperty("issued", RSSModule.W3CDTF_PROPERTY_TYPE);
93     dcTermsModule.addProperty("modified", RSSModule.W3CDTF_PROPERTY_TYPE);
94     dcTermsModule.addProperty("dateAccepted", RSSModule.W3CDTF_PROPERTY_TYPE);
95     dcTermsModule.addProperty("dateCopyrighted", RSSModule.W3CDTF_PROPERTY_TYPE);
96     dcTermsModule.addProperty("dateSubmitted", RSSModule.W3CDTF_PROPERTY_TYPE);
97     registerModule(dcTermsModule, "dcterms");
98
99     RSSBasicModule v2vTermsModule = new RSSBasicModule(V2V_NAMESPACE_URI, "indymedia v2v RSS module");
100     v2vTermsModule.addMultiValuedProperty("topic", RSSModule.PCDATA_PROPERTY_TYPE);
101     v2vTermsModule.addMultiValuedProperty("genre", RSSModule.PCDATA_PROPERTY_TYPE);
102     v2vTermsModule.addMultiValuedProperty("link", RSSModule.PCDATA_PROPERTY_TYPE);
103     registerModule(v2vTermsModule, "v2v");
104
105     registerModule(new RSSBasicModule(EVENT_NAMESPACE_URI, "Event RSS module"), "ev");
106     registerModule(new RSSBasicModule(TAXONOMY_NAMESPACE_URI, "Taxonomy RSS module"), "taxo");
107   }
108
109   public void registerModule(RSSModule aModule, String aPrefix) {
110     modules.add(aModule);
111     namespaceURItoModule.put(aModule.getNamespaceURI(), aModule);
112     moduleToPrefix.put(aModule, aPrefix);
113   }
114
115   public RSSData parseInputStream(InputStream aStream) throws RSSExc, RSSFailure {
116     try {
117       XMLReader xmlReader = new XMLReader(true);
118       RSSData result = new RSSData();
119       xmlReader.parseInputStream(aStream, new RootSectionHandler(result));
120
121       return result;
122     }
123     catch (Throwable t) {
124       throw new RSSFailure(t);
125     }
126   }
127
128   public RSSData parseUrl(String anUrl) throws RSSExc, RSSFailure {
129     try {
130       InputStream inputStream = (InputStream) new URL(anUrl).getContent(new Class[] {InputStream.class});
131
132       if (inputStream==null)
133         throw new RSSExc("RSSChannel.parseUrl: Can't get url content");
134
135       return parseInputStream(inputStream);
136     }
137     catch (Throwable t) {
138       throw new RSSFailure(t);
139     }
140   }
141
142   private class RootSectionHandler extends XMLReader.AbstractSectionHandler {
143     private RSSData data;
144
145     public RootSectionHandler(RSSData aData) {
146       data = aData;
147     }
148
149     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
150       if (aTag.getLocalName().equals("RDF")) {
151         return new RDFSectionHandler(data);
152       }
153       else
154         throw new XMLReader.XMLReaderFailure(new RSSExc("'RDF' tag expected"));
155     };
156
157     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
158     };
159
160     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
161       if (aCharacters.trim().length()>0)
162         throw new XMLReader.XMLReaderExc("No character data allowed here");
163     };
164
165     public void finishSection() throws XMLReader.XMLReaderExc {
166     };
167   }
168
169   private class RDFSectionHandler extends XMLReader.AbstractSectionHandler {
170     private RSSData data;
171
172
173     public RDFSectionHandler(RSSData aData) {
174       data = aData;
175     }
176
177     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
178       String identifier = (String) anAttributes.get(RDF_ABOUT_PARAMETER);
179       String rdfClass = makeQualifiedName(aTag);
180
181       return new RDFResourceSectionHandler(rdfClass, identifier);
182     };
183
184     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
185       if (aHandler instanceof RDFResourceSectionHandler) {
186         data.addResource(((RDFResourceSectionHandler) aHandler).getResource());
187       }
188     };
189
190     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
191       if (aCharacters.trim().length()>0)
192         throw new XMLReader.XMLReaderExc("No character data allowed here");
193     };
194
195     public void finishSection() throws XMLReader.XMLReaderExc {
196     };
197   }
198
199   private XMLReader.SectionHandler makePropertyValueSectionHandler(XMLReader.XMLName aTag, Map anAttributes) {
200     RSSModule module = (RSSModule) namespaceURItoModule.get(aTag.getNamespaceURI());
201
202     if (module!=null) {
203       RSSModule.RSSModuleProperty property = module.getPropertyForName(aTag.getLocalName());
204
205       if (property!=null) {
206         switch (property.getType()) {
207           case
208             RSSModule.PCDATA_PROPERTY_TYPE:
209               return new PCDATASectionHandler();
210           case
211             RSSModule.RDFCOLLECTION_PROPERTY_TYPE:
212               return new RDFCollectionSectionHandler();
213 //          case
214 //            RSSModule.RDF_PROPERTY_TYPE:
215 //              return new RDFValueSectionHandler();
216           case
217             RSSModule.W3CDTF_PROPERTY_TYPE:
218               return new DateSectionHandler();
219         }
220       }
221
222     }
223
224     return new FlexiblePropertyValueSectionHandler();
225   }
226
227   private void usePropertyValueSectionHandler(RDFResource aResource, PropertyValueSectionHandler aHandler, XMLReader.XMLName aTag) {
228     RSSModule module = (RSSModule) namespaceURItoModule.get(aTag.getNamespaceURI());
229
230     if (module!=null) {
231       RSSModule.RSSModuleProperty property = module.getPropertyForName(aTag.getLocalName());
232
233       if (property!=null && property.getIsMultiValued()) {
234         List value = (List) aResource.get(makeQualifiedName(aTag));
235
236         if (value==null) {
237           value = new Vector();
238           aResource.set(makeQualifiedName(aTag), value);
239         }
240
241         value.add(aHandler.getValue());
242
243         return;
244       }
245     }
246
247     aResource.set(makeQualifiedName(aTag), aHandler.getValue());
248   }
249
250   private String makeQualifiedName(XMLReader.XMLName aName) {
251     String result=aName.getLocalName();
252     RSSModule module = (RSSModule) namespaceURItoModule.get(aName.getNamespaceURI());
253     if (module!=null) {
254       String prefix = (String) moduleToPrefix.get(module);
255
256       if (prefix!=null && prefix.length()>0)
257         result = prefix+":"+result;
258     }
259
260     return result;
261   }
262
263   private class RDFResourceSectionHandler extends XMLReader.AbstractSectionHandler {
264     private String image;
265     private XMLReader.XMLName currentTag;
266     private RDFResource resource;
267
268     public RDFResourceSectionHandler(String anRDFClass, String anIdentifier) {
269       resource = new RDFResource(anRDFClass, anIdentifier);
270     }
271
272     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
273       currentTag = aTag;
274
275       return makePropertyValueSectionHandler(aTag, anAttributes);
276     };
277
278     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
279       if (aHandler instanceof PropertyValueSectionHandler) {
280         usePropertyValueSectionHandler(resource, (PropertyValueSectionHandler) aHandler, currentTag);
281 //        resource.set(makeQualifiedName(currentTag), ( (PropertyValueSectionHandler) aHandler).getValue());
282       }
283     };
284
285     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
286       if (aCharacters.trim().length()>0)
287         throw new XMLReader.XMLReaderExc("No character data allowed here");
288     };
289
290     public void finishSection() throws XMLReader.XMLReaderExc {
291     };
292
293     public RDFResource getResource() {
294       return resource;
295     }
296   }
297
298   private abstract class PropertyValueSectionHandler extends XMLReader.AbstractSectionHandler {
299     public abstract Object getValue();
300   }
301
302   private class FlexiblePropertyValueSectionHandler extends PropertyValueSectionHandler {
303     private StringBuffer stringData;
304     private Object structuredData;
305
306     public FlexiblePropertyValueSectionHandler() {
307       stringData = new StringBuffer();
308       structuredData=null;
309     }
310
311     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
312       if (aTag.equals(RDF_SEQUENCE_TAG))
313         return new RDFSequenceSectionHandler();
314       else
315         return new DiscardingSectionHandler();
316     };
317
318     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
319       if (aHandler instanceof RDFSequenceSectionHandler) {
320         structuredData= ((RDFSequenceSectionHandler) aHandler).getItems();
321       }
322     };
323
324     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
325       stringData.append(aCharacters);
326     };
327
328     public void finishSection() throws XMLReader.XMLReaderExc {
329     };
330
331     public String getData() {
332       return stringData.toString();
333     }
334
335     public Object getValue() {
336       if (structuredData==null)
337         return stringData.toString();
338       else
339         return structuredData;
340     }
341   }
342
343   private class RDFCollectionSectionHandler extends PropertyValueSectionHandler {
344     private List items;
345
346     public RDFCollectionSectionHandler() {
347       items = new Vector();
348     }
349
350     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
351       if (aTag.equals(RDF_SEQUENCE_TAG))
352         return new RDFSequenceSectionHandler();
353       else
354         return new DiscardingSectionHandler();
355     };
356
357     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
358       if (aHandler instanceof RDFSequenceSectionHandler) {
359         items.addAll(((RDFSequenceSectionHandler) aHandler).getItems());
360       }
361     };
362
363     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
364       if (aCharacters.trim().length()>0)
365         throw new XMLReader.XMLReaderExc("No character data allowed here");
366     };
367
368     public void finishSection() throws XMLReader.XMLReaderExc {
369     };
370
371     public List getItems() {
372       return items;
373     }
374
375     public Object getValue() {
376       return items;
377     }
378   }
379
380   private class PCDATASectionHandler extends PropertyValueSectionHandler {
381     private StringBuffer data;
382
383     public PCDATASectionHandler() {
384       data = new StringBuffer();
385     }
386
387     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
388       throw new XMLReader.XMLReaderFailure(new RSSExc("No subtags allowed here"));
389     };
390
391     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
392     };
393
394     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
395       data.append(aCharacters);
396     };
397
398     public void finishSection() throws XMLReader.XMLReaderExc {
399     };
400
401     public String getData() {
402       return data.toString();
403     }
404
405     public Object getValue() {
406       return data.toString();
407     }
408   }
409
410   private class DateSectionHandler extends PropertyValueSectionHandler {
411     private StringBuffer data;
412
413     public DateSectionHandler() {
414       data = new StringBuffer();
415     }
416
417     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
418       throw new XMLReader.XMLReaderFailure(new RSSExc("No subtags allowed here"));
419     };
420
421     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
422     };
423
424     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
425       data.append(aCharacters);
426     };
427
428     public void finishSection() throws XMLReader.XMLReaderExc {
429     };
430
431     private final static String SPACE = "[\t\n\r ]*";
432     private final static String NUMBER = "[0-9]*";
433     private final static String SIGN = "[-+]";
434
435     public Object getValue() {
436       try {
437         String expression = data.toString().trim();
438
439         return DateTimeFunctions.parseW3CDTFString(expression);
440       }
441       catch (Throwable t) {
442
443         return null;
444       }
445     }
446   }
447
448
449   private class RDFSequenceSectionHandler extends XMLReader.AbstractSectionHandler {
450     private List items;
451
452     public RDFSequenceSectionHandler() {
453       items = new Vector();
454     }
455
456     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
457       if (aTag.equals("rdf:li")) {
458         String item = (String) anAttributes.get("rdf:resource");
459
460         if (item!=null)
461           items.add(item);
462       }
463
464       return new DiscardingSectionHandler();
465     };
466
467     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
468     };
469
470     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
471     };
472
473     public void finishSection() throws XMLReader.XMLReaderExc {
474     };
475
476     public List getItems() {
477       return items;
478     }
479   }
480
481   private class RDFLiteralSectionHandler extends PropertyValueSectionHandler {
482     private StringBuffer data;
483     private String tag;
484
485     public RDFLiteralSectionHandler() {
486       data = new StringBuffer();
487     }
488
489     protected StringBuffer getData() {
490       return data;
491     }
492
493     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
494       tag=aTag;
495       data.append("<"+tag+">");
496
497       return new RDFLiteralSectionHandler();
498     };
499
500     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
501       data.append(((RDFLiteralSectionHandler) aHandler).getData());
502       data.append("</"+tag+">");
503     };
504
505     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
506       data.append(aCharacters);
507     };
508
509     public void finishSection() throws XMLReader.XMLReaderExc {
510     };
511
512     public Object getValue() {
513       return data.toString();
514     }
515   }
516
517   private class DiscardingSectionHandler extends XMLReader.AbstractSectionHandler {
518     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
519       return this;
520     };
521
522     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
523     };
524
525     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
526     };
527
528     public void finishSection() throws XMLReader.XMLReaderExc {
529     };
530   }
531 }