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