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