View Javadoc

1   /*
2    * Copyright 2004 Sun Microsystems, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  package com.sun.syndication.feed.synd;
18  
19  import com.sun.syndication.feed.impl.ObjectBean;
20  import com.sun.syndication.feed.impl.CopyFromHelper;
21  import com.sun.syndication.feed.WireFeed;
22  import com.sun.syndication.feed.module.*;
23  import com.sun.syndication.feed.module.impl.ModuleUtils;
24  import com.sun.syndication.feed.synd.impl.Converters;
25  import com.sun.syndication.feed.synd.impl.URINormalizer;
26  
27  import java.util.*;
28  import java.io.Serializable;
29  
30  /***
31   * Bean for all types of feeds.
32   * <p>
33   * It handles all RSS versions and Atom 0.3, it normalizes all info, it may lose information.
34   * <p>
35   * @author Alejandro Abdelnur
36   *
37   */
38  public class SyndFeedImpl implements Serializable,SyndFeed {
39      
40      private ObjectBean _objBean;
41      
42      private String    _encoding;
43      private String    _uri;
44      private String    _title;
45      private String    _feedType;
46      private String    _link;
47      private List      _links;
48      private String    _description;
49      private SyndImage _image;
50      private List      _entries;
51      private List      _modules;
52      private List      _authors;
53      private List      _contributors;
54  
55      private static final Converters CONVERTERS = new Converters();
56  
57      private static final Set IGNORE_PROPERTIES = new HashSet();
58  
59      /***
60       * Unmodifiable Set containing the convenience properties of this class.
61       * <p>
62       * Convenience properties are mapped to Modules, for cloning the convenience properties
63       * can be ignored as the will be copied as part of the module cloning.
64       */
65  
66      public static final Set CONVENIENCE_PROPERTIES = Collections.unmodifiableSet(IGNORE_PROPERTIES);
67  
68      static {
69          IGNORE_PROPERTIES.add("publishedDate");
70          IGNORE_PROPERTIES.add("author");
71          IGNORE_PROPERTIES.add("copyright");
72          IGNORE_PROPERTIES.add("categories");
73          IGNORE_PROPERTIES.add("language");
74      }
75  
76      /***
77       * Returns the real feed types the SyndFeedImpl supports when converting from and to.
78       * <p>
79       * @return the real feed type supported.
80       */
81      public List getSupportedFeedTypes() {
82          return CONVERTERS.getSupportedFeedTypes();
83      }
84  
85      /***
86       * For implementations extending SyndFeedImpl to be able to use the ObjectBean functionality
87       * with extended interfaces.
88       * <p>
89       * @param beanClass
90       * @param convenienceProperties set containing the convenience properties of the SyndEntryImpl
91       * (the are ignored during cloning, check CloneableBean for details).
92       *
93       */
94      protected SyndFeedImpl(Class beanClass,Set convenienceProperties) {
95          _objBean = new ObjectBean(beanClass,this,convenienceProperties);
96      }
97  
98      /***
99       * Default constructor. All properties are set to <b>null</b>.
100      * <p>
101      *
102      */
103     public SyndFeedImpl() {
104         this(null);
105     }
106 
107     /***
108      * Creates a SyndFeedImpl and populates all its properties out of the
109      * given RSS Channel or Atom Feed properties.
110      * <p>
111      * @param feed the RSS Channel or the Atom Feed to populate the properties from.
112      *
113      */
114     public SyndFeedImpl(WireFeed feed) {
115         this(SyndFeed.class,IGNORE_PROPERTIES);
116         if (feed!=null) {
117             _feedType = feed.getFeedType();
118             Converter converter = CONVERTERS.getConverter(_feedType);
119             if (converter==null) {
120                 throw new IllegalArgumentException("Invalid feed type ["+_feedType+"]");
121             }
122             converter.copyInto(feed,this);
123         }
124     }
125 
126     /***
127      * Creates a deep 'bean' clone of the object.
128      * <p>
129      * @return a clone of the object.
130      * @throws CloneNotSupportedException thrown if an element of the object cannot be cloned.
131      *
132      */
133     public Object clone() throws CloneNotSupportedException {
134         return _objBean.clone();
135     }
136 
137     /***
138      * Indicates whether some other object is "equal to" this one as defined by the Object equals() method.
139      * <p>
140      * @param other he reference object with which to compare.
141      * @return <b>true</b> if 'this' object is equal to the 'other' object.
142      *
143      */
144     public boolean equals(Object other) {
145         return _objBean.equals(other);
146     }
147 
148     /***
149      * Returns a hashcode value for the object.
150      * <p>
151      * It follows the contract defined by the Object hashCode() method.
152      * <p>
153      * @return the hashcode of the bean object.
154      *
155      */
156     public int hashCode() {
157         return _objBean.hashCode();
158     }
159 
160     /***
161      * Returns the String representation for the object.
162      * <p>
163      * @return String representation for the object.
164      *
165      */
166     public String toString() {
167         return _objBean.toString();
168     }
169 
170     /***
171      * Creates a real feed containing the information of the SyndFeedImpl.
172      * <p>
173      * The feed type of the created WireFeed is taken from the SyndFeedImpl feedType property.
174      * <p>
175      * @return the real feed.
176      *
177      */
178     public WireFeed createWireFeed() {
179         return createWireFeed(_feedType);
180     }
181 
182     /***
183      * Creates a real feed containing the information of the SyndFeedImpl.
184      * <p>
185      * @param feedType the feed type for the WireFeed to be created.
186      * @return the real feed.
187      *
188      */
189     public WireFeed createWireFeed(String feedType) {
190         if (feedType==null) {
191             throw new IllegalArgumentException("Feed type cannot be null");
192         }
193         Converter converter = CONVERTERS.getConverter(feedType);
194         if (converter==null) {
195             throw new IllegalArgumentException("Invalid feed type ["+feedType+"]");
196         }
197         return converter.createRealFeed(this);
198     }
199 
200     /***
201      * Returns the wire feed type the feed had/will-have when coverted from/to a WireFeed.
202      * <p>
203      * @return the feed type, <b>null</b> if none.
204      *
205      */
206     public String getFeedType() {
207         return _feedType;
208     }
209 
210     /***
211      * Sets the wire feed type the feed will-have when coverted to a WireFeed.
212      * <p>
213      * @param feedType the feed type to set, <b>null</b> if none.
214      *
215      */
216     public void setFeedType(String feedType) {
217         _feedType = feedType;
218     }
219 
220     /***
221      * Returns the charset encoding of a the feed. This is not set by Rome parsers.
222      * <p>
223      * @return the charset encoding of the feed.
224      *
225      */
226     public String getEncoding() {
227         return _encoding;
228     }
229 
230     /***
231      * Sets the charset encoding of a the feed. This is not set by Rome parsers.
232      * <p>
233      * @param encoding the charset encoding of the feed.
234      *
235      */
236     public void setEncoding(String encoding) {
237         _encoding = encoding;
238     }
239 
240     /***
241      * Returns the feed URI.
242      * <p>
243      * How the feed URI maps to a concrete feed type (RSS or Atom) depends on
244      * the concrete feed type. This is explained in detail in Rome documentation,
245      * <a href="http://wiki.java.net/bin/edit/Javawsxml/Rome04URIMapping">Feed and entry URI mapping</a>.
246      * <p>
247      * The returned URI is a normalized URI as specified in RFC 2396bis.
248      * <p>
249      * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
250      * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
251      * is the URL that the item is accessible under, the URI is the
252      * permanent identifier which the aggregator should use to reference
253      * this item. Often the URI will use some standardized identifier scheme
254      * such as DOI's so that items can be identified even if they appear in
255      * multiple feeds with different "links" (they might be on different
256      * hosting platforms but be the same item). Also, though rare, there
257      * could be multiple items with the same link but a different URI and
258      * associated metadata which need to be treated as distinct entities.
259      * In the RSS 1.0 case the URI must be a valid RDF URI reference.
260      * <p>
261      * @return the feed URI, <b>null</b> if none.
262      *
263      */
264     public String getUri() {
265         return _uri;
266     }
267 
268     /***
269      * Sets the feed URI.
270      * <p>
271      * How the feed URI maps to a concrete feed type (RSS or Atom) depends on
272      * the concrete feed type. This is explained in detail in Rome documentation,
273      * <a href="http://wiki.java.net/bin/edit/Javawsxml/Rome04URIMapping">Feed and entry URI mapping</a>.
274      * <p>
275      * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
276      * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
277      * is the URL that the item is accessible under, the URI is the
278      * permanent identifier which the aggregator should use to reference
279      * this item. Often the URI will use some standardized identifier scheme
280      * such as DOI's so that items can be identified even if they appear in
281      * multiple feeds with different "links" (they might be on different
282      * hosting platforms but be the same item). Also, though rare, there
283      * could be multiple items with the same link but a different URI and
284      * associated metadata which need to be treated as distinct entities.
285      * In the RSS 1.0 case the URI must be a valid RDF URI reference.
286      * <p>
287      * @param uri the feed URI to set, <b>null</b> if none.
288      *
289      */
290     public void setUri(String uri) {
291         _uri = URINormalizer.normalize(uri);
292     }
293 
294     /***
295      * Returns the feed title.
296      * <p>
297      * @return the feed title, <b>null</b> if none.
298      *
299      */
300     public String getTitle() {
301         return _title;
302     }
303 
304     /***
305      * Sets the feed title.
306      * <p>
307      * @param title the feed title to set, <b>null</b> if none.
308      *
309      */
310     public void setTitle(String title) {
311         _title = title;
312     }
313 
314     /***
315      * Returns the feed link.
316      * <p>
317      * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
318      * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
319      * is the URL that the item is accessible under, the URI is the
320      * permanent identifier which the aggregator should use to reference
321      * this item. Often the URI will use some standardized identifier scheme
322      * such as DOI's so that items can be identified even if they appear in
323      * multiple feeds with different "links" (they might be on different
324      * hosting platforms but be the same item). Also, though rare, there
325      * could be multiple items with the same link but a different URI and
326      * associated metadata which need to be treated as distinct entities.
327      * In the RSS 1.0 case the URI must be a valid RDF URI reference.
328      * <p>
329      * @return the feed link, <b>null</b> if none.
330      *
331      */
332     public String getLink() {
333         return _link;
334     }
335 
336     /***
337      * Sets the feed link.
338      * <p>
339      * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
340      * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
341      * is the URL that the item is accessible under, the URI is the
342      * permanent identifier which the aggregator should use to reference
343      * this item. Often the URI will use some standardized identifier scheme
344      * such as DOI's so that items can be identified even if they appear in
345      * multiple feeds with different "links" (they might be on different
346      * hosting platforms but be the same item). Also, though rare, there
347      * could be multiple items with the same link but a different URI and
348      * associated metadata which need to be treated as distinct entities.
349      * In the RSS 1.0 case the URI must be a valid RDF URI reference.
350      * <p>
351      * @param link the feed link to set, <b>null</b> if none.
352      *
353      */
354     public void setLink(String link) {
355         _link = link;
356     }
357 
358     /***
359      * Returns the feed description.
360      * <p>
361      * @return the feed description, <b>null</b> if none.
362      *
363      */
364     public String getDescription() {
365         return _description;
366     }
367 
368     /***
369      * Sets the feed description.
370      * <p>
371      * @param description the feed description to set, <b>null</b> if none.
372      *
373      */
374     public void setDescription(String description) {
375         _description = description;
376     }
377 
378     /***
379      * Returns the feed published date.
380      * <p>
381      * This method is a convenience method, it maps to the Dublin Core module date.
382      * <p>
383      * @return the feed published date, <b>null</b> if none.
384      *
385      */
386     public Date getPublishedDate() {
387         return getDCModule().getDate();
388     }
389 
390     /***
391      * Sets the feed published date.
392      * <p>
393      * This method is a convenience method, it maps to the Dublin Core module date.
394      * <p>
395      * @param publishedDate the feed published date to set, <b>null</b> if none.
396      *
397      */
398     public void setPublishedDate(Date publishedDate) {
399         getDCModule().setDate(publishedDate);
400     }
401 
402     /***
403      * Returns the feed copyright.
404      * <p>
405      * This method is a convenience method, it maps to the Dublin Core module rights.
406      * <p>
407      * @return the feed copyright, <b>null</b> if none.
408      *
409      */
410     public String getCopyright() {
411         return getDCModule().getRights();
412     }
413 
414     /***
415      * Sets the feed copyright.
416      * <p>
417      * This method is a convenience method, it maps to the Dublin Core module rights.
418      * <p>
419      * @param copyright the feed copyright to set, <b>null</b> if none.
420      *
421      */
422     public void setCopyright(String copyright) {
423         getDCModule().setRights(copyright);
424     }
425 
426     /***
427      * Returns the feed image.
428      * <p>
429      * @return the feed image, <b>null</b> if none.
430      *
431      */
432     public SyndImage getImage() {
433         return _image;
434     }
435 
436     /***
437      * Sets the feed image.
438      * <p>
439      * @param image the feed image to set, <b>null</b> if none.
440      *
441      */
442     public void setImage(SyndImage image) {
443         _image = image;
444     }
445 
446     /***
447      * Returns the feed categories.
448      * <p>
449      * This method is a convenience method, it maps to the Dublin Core module subjects.
450      * <p>
451      * @return a list of SyndCategoryImpl elements with the feed categories,
452      *         an empty list if none.
453      *
454      */
455     public List getCategories() {
456         return new SyndCategoryListFacade(getDCModule().getSubjects());
457     }
458 
459     /***
460      * Sets the feed categories.
461      * <p>
462      * This method is a convenience method, it maps to the Dublin Core module subjects.
463      * <p>
464      * @param categories the list of SyndCategoryImpl elements with the feed categories to set,
465      *        an empty list or <b>null</b> if none.
466      *
467      */
468     public void setCategories(List categories) {
469         getDCModule().setSubjects(SyndCategoryListFacade.convertElementsSyndCategoryToSubject(categories));
470     }
471 
472     /***
473      * Returns the feed entries.
474      * <p>
475      * @return a list of SyndEntryImpl elements with the feed entries,
476      *         an empty list if none.
477      *
478      */
479     public List getEntries() {
480         return (_entries==null) ? (_entries=new ArrayList()) : _entries;
481     }
482 
483     /***
484      * Sets the feed entries.
485      * <p>
486      * @param entries the list of SyndEntryImpl elements with the feed entries to set,
487      *        an empty list or <b>null</b> if none.
488      *
489      */
490     public void setEntries(List entries) {
491         _entries = entries;
492     }
493 
494     /***
495      * Returns the feed language.
496      * <p>
497      * This method is a convenience method, it maps to the Dublin Core module language.
498      * <p>
499      * @return the feed language, <b>null</b> if none.
500      *
501      */
502     public String getLanguage() {
503         return getDCModule().getLanguage();
504     }
505 
506     /***
507      * Sets the feed language.
508      * <p>
509      * This method is a convenience method, it maps to the Dublin Core module language.
510      * <p>
511      * @param language the feed language to set, <b>null</b> if none.
512      *
513      */
514     public void setLanguage(String language) {
515         getDCModule().setLanguage(language);
516     }
517 
518     /***
519      * Returns the feed modules.
520      * <p>
521      * @return a list of ModuleImpl elements with the feed modules,
522      *         an empty list if none.
523      *
524      */
525     public List getModules() {
526         if  (_modules==null) {
527             _modules=new ArrayList();
528         }
529         if (ModuleUtils.getModule(_modules,DCModule.URI)==null) {
530             _modules.add(new DCModuleImpl());
531         }
532         return _modules;
533     }
534 
535 
536     /***
537      * Sets the feed modules.
538      * <p>
539      * @param modules the list of ModuleImpl elements with the feed modules to set,
540      *        an empty list or <b>null</b> if none.
541      *
542      */
543     public void setModules(List modules) {
544         _modules = modules;
545     }
546 
547     /***
548      * Returns the module identified by a given URI.
549      * <p>
550      * @param uri the URI of the ModuleImpl.
551      * @return The module with the given URI, <b>null</b> if none.
552      */
553     public Module getModule(String uri) {
554         return ModuleUtils.getModule(getModules(),uri);
555     }
556 
557     /***
558      * Returns the Dublin Core module of the feed.
559      * @return the DC module, it's never <b>null</b>
560      *
561      */
562     private DCModule getDCModule() {
563         return (DCModule) getModule(DCModule.URI);
564     }
565 
566     public Class getInterface() {
567         return SyndFeed.class;
568     }
569 
570     public void copyFrom(Object obj) {
571         COPY_FROM_HELPER.copy(this,obj);
572     }
573 
574 
575     // TODO We need to find out how to refactor this one in a nice reusable way.
576 
577     private static final CopyFromHelper COPY_FROM_HELPER;
578 
579     static {
580         Map basePropInterfaceMap = new HashMap();
581         basePropInterfaceMap.put("feedType",String.class);
582         basePropInterfaceMap.put("encoding",String.class);
583         basePropInterfaceMap.put("uri",String.class);
584         basePropInterfaceMap.put("title",String.class);
585         basePropInterfaceMap.put("link",String.class);
586         basePropInterfaceMap.put("description",String.class);
587         basePropInterfaceMap.put("image",SyndImage.class);
588         basePropInterfaceMap.put("entries",SyndEntry.class);
589         basePropInterfaceMap.put("modules",Module.class);
590 
591         Map basePropClassImplMap = new HashMap();
592         basePropClassImplMap.put(SyndEntry.class,SyndEntryImpl.class);
593         basePropClassImplMap.put(SyndImage.class,SyndImageImpl.class);
594         basePropClassImplMap.put(DCModule.class,DCModuleImpl.class);
595         basePropClassImplMap.put(SyModule.class,SyModuleImpl.class);
596 
597         COPY_FROM_HELPER = new CopyFromHelper(SyndFeed.class,basePropInterfaceMap,basePropClassImplMap);
598     }
599 
600     /***
601      * Returns the links
602      * <p>
603      * @return Returns the links.
604      */
605     public List getLinks() {
606         return _links;
607     }
608     
609     /***
610      * Set the links
611      * <p>
612      * @param links The links to set.
613      */
614     public void setLinks(List links) {
615         _links = links;
616     }
617 
618     public List getAuthors() {
619         return _authors;
620     }
621 
622     public void setAuthors(List authors) {
623         this._authors = authors;
624     }
625 
626     /***
627      * Returns the feed author.
628      * <p>
629      * This method is a convenience method, it maps to the Dublin Core module creator.
630      * <p>
631      * @return the feed author, <b>null</b> if none.
632      *
633      */
634     public String getAuthor() {
635         return getDCModule().getCreator();
636     }
637 
638     /***
639      * Sets the feed author.
640      * <p>
641      * This method is a convenience method, it maps to the Dublin Core module creator.
642      * <p>
643      * @param author the feed author to set, <b>null</b> if none.
644      *
645      */
646     public void setAuthor(String author) {
647         getDCModule().setCreator(author);
648     }    
649     
650     public List getContributors() {
651         return _contributors;
652     }
653 
654     public void setContributors(List contributors) {
655         this._contributors = contributors;
656     }
657 }