tiny fix
[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   public static final String CONTENT_NAMESPACE_URI = "http://purl.org/rss/1.0/modules/content/";
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     registerModule(new RSSBasicModule(CONTENT_NAMESPACE_URI  , "Content RSS module"), "content");
108   }
109
110   public void registerModule(RSSModule aModule, String aPrefix) {
111     modules.add(aModule);
112     namespaceURItoModule.put(aModule.getNamespaceURI(), aModule);
113     moduleToPrefix.put(aModule, aPrefix);
114   }
115
116   public RSSData parseInputStream(InputStream aStream) throws RSSExc, RSSFailure {
117     try {
118       XMLReader xmlReader = new XMLReader(true);
119       RSSData result = new RSSData();
120       xmlReader.parseInputStream(aStream, new RootSectionHandler(result));
121
122       return result;
123     }
124     catch (Throwable t) {
125       throw new RSSFailure(t);
126     }
127   }
128
129   public RSSData parseUrl(String anUrl) throws RSSExc, RSSFailure {
130     try {
131       InputStream inputStream = (InputStream) new URL(anUrl).getContent(new Class[] {InputStream.class});
132
133       if (inputStream==null)
134         throw new RSSExc("RSSChannel.parseUrl: Can't get url content");
135
136       return parseInputStream(inputStream);
137     }
138     catch (Throwable t) {
139       throw new RSSFailure(t);
140     }
141   }
142
143   private class RootSectionHandler extends XMLReader.AbstractSectionHandler {
144     private RSSData data;
145
146     public RootSectionHandler(RSSData aData) {
147       data = aData;
148     }
149
150     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
151       if (aTag.getLocalName().equals("RDF")) {
152         return new RDFSectionHandler(data);
153       }
154       else
155         throw new XMLReader.XMLReaderFailure(new RSSExc("'RDF' tag expected"));
156     };
157
158     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
159     };
160
161     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
162       if (aCharacters.trim().length()>0)
163         throw new XMLReader.XMLReaderExc("No character data allowed here");
164     };
165
166     public void finishSection() throws XMLReader.XMLReaderExc {
167     };
168   }
169
170   private class RDFSectionHandler extends XMLReader.AbstractSectionHandler {
171     private RSSData data;
172
173
174     public RDFSectionHandler(RSSData aData) {
175       data = aData;
176     }
177
178     public XMLReader.SectionHandler startElement(XMLReader.XMLName aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
179       String identifier = (String) anAttributes.get(RDF_ABOUT_PARAMETER);
180       String rdfClass = makeQualifiedName(aTag);
181
182       return new RDFResourceSectionHandler(rdfClass, identifier);
183     };
184
185     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
186       if (aHandler instanceof RDFResourceSectionHandler) {
187         data.addResource(((RDFResourceSectionHandler) aHandler).getResource());
188       }
189     };
190
191     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
192       if (aCharacters.trim().length()>0)
193         throw new XMLReader.XMLReaderExc("No character data allowed here");
194     };
195
196     public void finishSection() throws XMLReader.XMLReaderExc {
197     };
198   }
199
200   private XMLReader.SectionHandler makePropertyValueSectionHandler(XMLReader.XMLName aTag, Map anAttributes) {
201     RSSModule module = (RSSModule) namespaceURItoModule.get(aTag.getNamespaceURI());
202
203     if (module!=null) {
204       RSSModule.RSSModuleProperty property = module.getPropertyForName(aTag.getLocalName());
205
206       if (property!=null) {
207         switch (property.getType()) {
208           case
209             RSSModule.PCDATA_PROPERTY_TYPE:
210               return new PCDATASectionHandler();
211           case
212             RSSModule.RDFCOLLECTION_PROPERTY_TYPE:
213               return new RDFCollectionSectionHandler();
214 //          case
215 //            RSSModule.RDF_PROPERTY_TYPE:
216 //              return new RDFValueSectionHandler();
217           case
218             RSSModule.W3CDTF_PROPERTY_TYPE:
219               return new DateSectionHandler();
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       if (resource.getIdentifier()==null || resource.getIdentifier().length()==0) {
295         resource.setIdentifier(resource.get("rss:link").toString());
296       }
297
298       return resource;
299     }
300   }
301
302   private abstract class PropertyValueSectionHandler extends XMLReader.AbstractSectionHandler {
303     public abstract Object getValue();
304   }
305
306   private class FlexiblePropertyValueSectionHandler extends PropertyValueSectionHandler {
307     private StringBuffer stringData;
308     private Object structuredData;
309
310     public FlexiblePropertyValueSectionHandler() {
311       stringData = new StringBuffer();
312       structuredData=null;
313     }
314
315     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
316       if (aTag.equals(RDF_SEQUENCE_TAG))
317         return new RDFSequenceSectionHandler();
318       else
319         return new DiscardingSectionHandler();
320     };
321
322     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
323       if (aHandler instanceof RDFSequenceSectionHandler) {
324         structuredData= ((RDFSequenceSectionHandler) aHandler).getItems();
325       }
326     };
327
328     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
329       stringData.append(aCharacters);
330     };
331
332     public void finishSection() throws XMLReader.XMLReaderExc {
333     };
334
335     public String getData() {
336       return stringData.toString();
337     }
338
339     public Object getValue() {
340       if (structuredData==null)
341         return stringData.toString();
342       else
343         return structuredData;
344     }
345   }
346
347   private class RDFCollectionSectionHandler extends PropertyValueSectionHandler {
348     private List items;
349
350     public RDFCollectionSectionHandler() {
351       items = new Vector();
352     }
353
354     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
355       if (aTag.equals(RDF_SEQUENCE_TAG))
356         return new RDFSequenceSectionHandler();
357       else
358         return new DiscardingSectionHandler();
359     };
360
361     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
362       if (aHandler instanceof RDFSequenceSectionHandler) {
363         items.addAll(((RDFSequenceSectionHandler) aHandler).getItems());
364       }
365     };
366
367     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
368       if (aCharacters.trim().length()>0)
369         throw new XMLReader.XMLReaderExc("No character data allowed here");
370     };
371
372     public void finishSection() throws XMLReader.XMLReaderExc {
373     };
374
375     public List getItems() {
376       return items;
377     }
378
379     public Object getValue() {
380       return items;
381     }
382   }
383
384   private class PCDATASectionHandler extends PropertyValueSectionHandler {
385     private StringBuffer data;
386
387     public PCDATASectionHandler() {
388       data = new StringBuffer();
389     }
390
391     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
392       throw new XMLReader.XMLReaderFailure(new RSSExc("No subtags allowed here"));
393     };
394
395     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
396     };
397
398     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
399       data.append(aCharacters);
400     };
401
402     public void finishSection() throws XMLReader.XMLReaderExc {
403     };
404
405     public String getData() {
406       return data.toString();
407     }
408
409     public Object getValue() {
410       return data.toString();
411     }
412   }
413
414   private class DateSectionHandler extends PropertyValueSectionHandler {
415     private StringBuffer data;
416
417     public DateSectionHandler() {
418       data = new StringBuffer();
419     }
420
421     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
422       throw new XMLReader.XMLReaderFailure(new RSSExc("No subtags allowed here"));
423     };
424
425     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
426     };
427
428     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
429       data.append(aCharacters);
430     };
431
432     public void finishSection() throws XMLReader.XMLReaderExc {
433     };
434
435     private final static String SPACE = "[\t\n\r ]*";
436     private final static String NUMBER = "[0-9]*";
437     private final static String SIGN = "[-+]";
438
439     public Object getValue() {
440       try {
441         String expression = data.toString().trim();
442
443         return DateTimeFunctions.parseW3CDTFString(expression);
444       }
445       catch (Throwable t) {
446
447         return null;
448       }
449     }
450   }
451
452
453   private class RDFSequenceSectionHandler extends XMLReader.AbstractSectionHandler {
454     private List items;
455
456     public RDFSequenceSectionHandler() {
457       items = new Vector();
458     }
459
460     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
461       if (aTag.equals("rdf:li")) {
462         String item = (String) anAttributes.get("rdf:resource");
463
464         if (item!=null)
465           items.add(item);
466       }
467
468       return new DiscardingSectionHandler();
469     };
470
471     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
472     };
473
474     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
475     };
476
477     public void finishSection() throws XMLReader.XMLReaderExc {
478     };
479
480     public List getItems() {
481       return items;
482     }
483   }
484
485   private class RDFLiteralSectionHandler extends PropertyValueSectionHandler {
486     private StringBuffer data;
487     private String tag;
488
489     public RDFLiteralSectionHandler() {
490       data = new StringBuffer();
491     }
492
493     protected StringBuffer getData() {
494       return data;
495     }
496
497     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
498       tag=aTag;
499       data.append("<"+tag+">");
500
501       return new RDFLiteralSectionHandler();
502     };
503
504     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
505       data.append(((RDFLiteralSectionHandler) aHandler).getData());
506       data.append("</"+tag+">");
507     };
508
509     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
510       data.append(aCharacters);
511     };
512
513     public void finishSection() throws XMLReader.XMLReaderExc {
514     };
515
516     public Object getValue() {
517       return data.toString();
518     }
519   }
520
521   private class DiscardingSectionHandler extends XMLReader.AbstractSectionHandler {
522     public XMLReader.SectionHandler startElement(String aTag, Map anAttributes) throws XMLReader.XMLReaderExc {
523       return this;
524     };
525
526     public void endElement(XMLReader.SectionHandler aHandler) throws XMLReader.XMLReaderExc {
527     };
528
529     public void characters(String aCharacters) throws XMLReader.XMLReaderExc {
530     };
531
532     public void finishSection() throws XMLReader.XMLReaderExc {
533     };
534   }
535 }