001: /*
002: * Copyright 2004 Sun Microsystems, Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: */
017: package com.sun.syndication.feed.synd;
018:
019: import com.sun.syndication.feed.impl.ObjectBean;
020: import com.sun.syndication.feed.impl.CopyFromHelper;
021: import com.sun.syndication.feed.WireFeed;
022: import com.sun.syndication.feed.module.*;
023: import com.sun.syndication.feed.module.impl.ModuleUtils;
024: import com.sun.syndication.feed.synd.impl.Converters;
025: import com.sun.syndication.feed.synd.impl.URINormalizer;
026:
027: import java.util.*;
028: import java.io.Serializable;
029:
030: /**
031: * Bean for all types of feeds.
032: * <p>
033: * It handles all RSS versions and Atom 0.3, it normalizes all info, it may lose information.
034: * <p>
035: * @author Alejandro Abdelnur
036: *
037: */
038: public class SyndFeedImpl implements Serializable, SyndFeed {
039:
040: private ObjectBean _objBean;
041:
042: private String _encoding;
043: private String _uri;
044: private SyndContent _title;
045: private SyndContent _description;
046: private String _feedType;
047: private String _link;
048: private List _links;
049: private SyndImage _image;
050: private List _entries;
051: private List _modules;
052: private List _authors;
053: private List _contributors;
054: private List _foreignMarkup;
055:
056: private static final Converters CONVERTERS = new Converters();
057:
058: private static final Set IGNORE_PROPERTIES = new HashSet();
059:
060: /**
061: * Unmodifiable Set containing the convenience properties of this class.
062: * <p>
063: * Convenience properties are mapped to Modules, for cloning the convenience properties
064: * can be ignored as the will be copied as part of the module cloning.
065: */
066:
067: public static final Set CONVENIENCE_PROPERTIES = Collections
068: .unmodifiableSet(IGNORE_PROPERTIES);
069:
070: static {
071: IGNORE_PROPERTIES.add("publishedDate");
072: IGNORE_PROPERTIES.add("author");
073: IGNORE_PROPERTIES.add("copyright");
074: IGNORE_PROPERTIES.add("categories");
075: IGNORE_PROPERTIES.add("language");
076: }
077:
078: /**
079: * Returns the real feed types the SyndFeedImpl supports when converting from and to.
080: * <p>
081: * @return the real feed type supported.
082: */
083: public List getSupportedFeedTypes() {
084: return CONVERTERS.getSupportedFeedTypes();
085: }
086:
087: /**
088: * For implementations extending SyndFeedImpl to be able to use the ObjectBean functionality
089: * with extended interfaces.
090: * <p>
091: * @param beanClass
092: * @param convenienceProperties set containing the convenience properties of the SyndEntryImpl
093: * (the are ignored during cloning, check CloneableBean for details).
094: *
095: */
096: protected SyndFeedImpl(Class beanClass, Set convenienceProperties) {
097: _objBean = new ObjectBean(beanClass, this ,
098: convenienceProperties);
099: }
100:
101: /**
102: * Default constructor. All properties are set to <b>null</b>.
103: * <p>
104: *
105: */
106: public SyndFeedImpl() {
107: this (null);
108: }
109:
110: /**
111: * Creates a SyndFeedImpl and populates all its properties out of the
112: * given RSS Channel or Atom Feed properties.
113: * <p>
114: * @param feed the RSS Channel or the Atom Feed to populate the properties from.
115: *
116: */
117: public SyndFeedImpl(WireFeed feed) {
118: this (SyndFeed.class, IGNORE_PROPERTIES);
119: if (feed != null) {
120: _feedType = feed.getFeedType();
121: Converter converter = CONVERTERS.getConverter(_feedType);
122: if (converter == null) {
123: throw new IllegalArgumentException(
124: "Invalid feed type [" + _feedType + "]");
125: }
126: converter.copyInto(feed, this );
127: }
128: }
129:
130: /**
131: * Creates a deep 'bean' clone of the object.
132: * <p>
133: * @return a clone of the object.
134: * @throws CloneNotSupportedException thrown if an element of the object cannot be cloned.
135: *
136: */
137: public Object clone() throws CloneNotSupportedException {
138: return _objBean.clone();
139: }
140:
141: /**
142: * Indicates whether some other object is "equal to" this one as defined by the Object equals() method.
143: * <p>
144: * @param other he reference object with which to compare.
145: * @return <b>true</b> if 'this' object is equal to the 'other' object.
146: *
147: */
148: public boolean equals(Object other) {
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(
202: "Feed type cannot be null");
203: }
204: Converter converter = CONVERTERS.getConverter(feedType);
205: if (converter == null) {
206: throw new IllegalArgumentException("Invalid feed type ["
207: + feedType + "]");
208: }
209: return converter.createRealFeed(this );
210: }
211:
212: /**
213: * Returns the wire feed type the feed had/will-have when coverted from/to a WireFeed.
214: * <p>
215: * @return the feed type, <b>null</b> if none.
216: *
217: */
218: public String getFeedType() {
219: return _feedType;
220: }
221:
222: /**
223: * Sets the wire feed type the feed will-have when coverted to a WireFeed.
224: * <p>
225: * @param feedType the feed type to set, <b>null</b> if none.
226: *
227: */
228: public void setFeedType(String feedType) {
229: _feedType = feedType;
230: }
231:
232: /**
233: * Returns the charset encoding of a the feed. This is not set by Rome parsers.
234: * <p>
235: * @return the charset encoding of the feed.
236: *
237: */
238: public String getEncoding() {
239: return _encoding;
240: }
241:
242: /**
243: * Sets the charset encoding of a the feed. This is not set by Rome parsers.
244: * <p>
245: * @param encoding the charset encoding of the feed.
246: *
247: */
248: public void setEncoding(String encoding) {
249: _encoding = encoding;
250: }
251:
252: /**
253: * Returns the feed URI.
254: * <p>
255: * How the feed URI maps to a concrete feed type (RSS or Atom) depends on
256: * the concrete feed type. This is explained in detail in Rome documentation,
257: * <a href="http://wiki.java.net/bin/edit/Javawsxml/Rome04URIMapping">Feed and entry URI mapping</a>.
258: * <p>
259: * The returned URI is a normalized URI as specified in RFC 2396bis.
260: * <p>
261: * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
262: * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
263: * is the URL that the item is accessible under, the URI is the
264: * permanent identifier which the aggregator should use to reference
265: * this item. Often the URI will use some standardized identifier scheme
266: * such as DOI's so that items can be identified even if they appear in
267: * multiple feeds with different "links" (they might be on different
268: * hosting platforms but be the same item). Also, though rare, there
269: * could be multiple items with the same link but a different URI and
270: * associated metadata which need to be treated as distinct entities.
271: * In the RSS 1.0 case the URI must be a valid RDF URI reference.
272: * <p>
273: * @return the feed URI, <b>null</b> if none.
274: *
275: */
276: public String getUri() {
277: return _uri;
278: }
279:
280: /**
281: * Sets the feed URI.
282: * <p>
283: * How the feed URI maps to a concrete feed type (RSS or Atom) depends on
284: * the concrete feed type. This is explained in detail in Rome documentation,
285: * <a href="http://wiki.java.net/bin/edit/Javawsxml/Rome04URIMapping">Feed and entry URI mapping</a>.
286: * <p>
287: * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
288: * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
289: * is the URL that the item is accessible under, the URI is the
290: * permanent identifier which the aggregator should use to reference
291: * this item. Often the URI will use some standardized identifier scheme
292: * such as DOI's so that items can be identified even if they appear in
293: * multiple feeds with different "links" (they might be on different
294: * hosting platforms but be the same item). Also, though rare, there
295: * could be multiple items with the same link but a different URI and
296: * associated metadata which need to be treated as distinct entities.
297: * In the RSS 1.0 case the URI must be a valid RDF URI reference.
298: * <p>
299: * @param uri the feed URI to set, <b>null</b> if none.
300: *
301: */
302: public void setUri(String uri) {
303: _uri = URINormalizer.normalize(uri);
304: }
305:
306: /**
307: * Returns the feed title.
308: * <p>
309: * @return the feed title, <b>null</b> if none.
310: *
311: */
312: public String getTitle() {
313: if (_title != null)
314: return _title.getValue();
315: return null;
316: }
317:
318: /**
319: * Sets the feed title.
320: * <p>
321: * @param title the feed title to set, <b>null</b> if none.
322: *
323: */
324: public void setTitle(String title) {
325: if (_title == null)
326: _title = new SyndContentImpl();
327: _title.setValue(title);
328: }
329:
330: /**
331: * Returns the feed title as a text construct.
332: * <p>
333: * @return the feed title, <b>null</b> if none.
334: *
335: */
336: public SyndContent getTitleEx() {
337: return _title;
338: }
339:
340: /**
341: * Sets the feed title as a text construct.
342: * <p>
343: * @param title the feed title to set, <b>null</b> if none.
344: *
345: */
346: public void setTitleEx(SyndContent title) {
347: _title = title;
348: }
349:
350: /**
351: * Returns the feed link.
352: * <p>
353: * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
354: * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
355: * is the URL that the item is accessible under, the URI is the
356: * permanent identifier which the aggregator should use to reference
357: * this item. Often the URI will use some standardized identifier scheme
358: * such as DOI's so that items can be identified even if they appear in
359: * multiple feeds with different "links" (they might be on different
360: * hosting platforms but be the same item). Also, though rare, there
361: * could be multiple items with the same link but a different URI and
362: * associated metadata which need to be treated as distinct entities.
363: * In the RSS 1.0 case the URI must be a valid RDF URI reference.
364: * <p>
365: * @return the feed link, <b>null</b> if none.
366: *
367: */
368: public String getLink() {
369: return _link;
370: }
371:
372: /**
373: * Sets the feed link.
374: * <p>
375: * Note: The URI is the unique identifier, in the RSS 2.0/atom case this is
376: * the GUID, for RSS 1.0 this is the URI attribute of the item. The Link
377: * is the URL that the item is accessible under, the URI is the
378: * permanent identifier which the aggregator should use to reference
379: * this item. Often the URI will use some standardized identifier scheme
380: * such as DOI's so that items can be identified even if they appear in
381: * multiple feeds with different "links" (they might be on different
382: * hosting platforms but be the same item). Also, though rare, there
383: * could be multiple items with the same link but a different URI and
384: * associated metadata which need to be treated as distinct entities.
385: * In the RSS 1.0 case the URI must be a valid RDF URI reference.
386: * <p>
387: * @param link the feed link to set, <b>null</b> if none.
388: *
389: */
390: public void setLink(String link) {
391: _link = link;
392: }
393:
394: /**
395: * Returns the feed description.
396: * <p>
397: * @return the feed description, <b>null</b> if none.
398: *
399: */
400: public String getDescription() {
401: if (_description != null)
402: return _description.getValue();
403: return null;
404: }
405:
406: /**
407: * Sets the feed description.
408: * <p>
409: * @param description the feed description to set, <b>null</b> if none.
410: *
411: */
412: public void setDescription(String description) {
413: if (_description == null)
414: _description = new SyndContentImpl();
415: _description.setValue(description);
416: }
417:
418: /**
419: * Returns the feed description as a text construct.
420: * <p>
421: * @return the feed description, <b>null</b> if none.
422: *
423: */
424: public SyndContent getDescriptionEx() {
425: return _description;
426: }
427:
428: /**
429: * Sets the feed description as a text construct.
430: * <p>
431: * @param description the feed description to set, <b>null</b> if none.
432: *
433: */
434: public void setDescriptionEx(SyndContent description) {
435: _description = description;
436: }
437:
438: /**
439: * Returns the feed published date.
440: * <p>
441: * This method is a convenience method, it maps to the Dublin Core module date.
442: * <p>
443: * @return the feed published date, <b>null</b> if none.
444: *
445: */
446: public Date getPublishedDate() {
447: return getDCModule().getDate();
448: }
449:
450: /**
451: * Sets the feed published date.
452: * <p>
453: * This method is a convenience method, it maps to the Dublin Core module date.
454: * <p>
455: * @param publishedDate the feed published date to set, <b>null</b> if none.
456: *
457: */
458: public void setPublishedDate(Date publishedDate) {
459: getDCModule().setDate(publishedDate);
460: }
461:
462: /**
463: * Returns the feed copyright.
464: * <p>
465: * This method is a convenience method, it maps to the Dublin Core module rights.
466: * <p>
467: * @return the feed copyright, <b>null</b> if none.
468: *
469: */
470: public String getCopyright() {
471: return getDCModule().getRights();
472: }
473:
474: /**
475: * Sets the feed copyright.
476: * <p>
477: * This method is a convenience method, it maps to the Dublin Core module rights.
478: * <p>
479: * @param copyright the feed copyright to set, <b>null</b> if none.
480: *
481: */
482: public void setCopyright(String copyright) {
483: getDCModule().setRights(copyright);
484: }
485:
486: /**
487: * Returns the feed image.
488: * <p>
489: * @return the feed image, <b>null</b> if none.
490: *
491: */
492: public SyndImage getImage() {
493: return _image;
494: }
495:
496: /**
497: * Sets the feed image.
498: * <p>
499: * @param image the feed image to set, <b>null</b> if none.
500: *
501: */
502: public void setImage(SyndImage image) {
503: _image = image;
504: }
505:
506: /**
507: * Returns the feed categories.
508: * <p>
509: * This method is a convenience method, it maps to the Dublin Core module subjects.
510: * <p>
511: * @return a list of SyndCategoryImpl elements with the feed categories,
512: * an empty list if none.
513: *
514: */
515: public List getCategories() {
516: return new SyndCategoryListFacade(getDCModule().getSubjects());
517: }
518:
519: /**
520: * Sets the feed categories.
521: * <p>
522: * This method is a convenience method, it maps to the Dublin Core module subjects.
523: * <p>
524: * @param categories the list of SyndCategoryImpl elements with the feed categories to set,
525: * an empty list or <b>null</b> if none.
526: *
527: */
528: public void setCategories(List categories) {
529: getDCModule()
530: .setSubjects(
531: SyndCategoryListFacade
532: .convertElementsSyndCategoryToSubject(categories));
533: }
534:
535: /**
536: * Returns the feed entries.
537: * <p>
538: * @return a list of SyndEntryImpl elements with the feed entries,
539: * an empty list if none.
540: *
541: */
542: public List getEntries() {
543: return (_entries == null) ? (_entries = new ArrayList())
544: : _entries;
545: }
546:
547: /**
548: * Sets the feed entries.
549: * <p>
550: * @param entries the list of SyndEntryImpl elements with the feed entries to set,
551: * an empty list or <b>null</b> if none.
552: *
553: */
554: public void setEntries(List entries) {
555: _entries = entries;
556: }
557:
558: /**
559: * Returns the feed language.
560: * <p>
561: * This method is a convenience method, it maps to the Dublin Core module language.
562: * <p>
563: * @return the feed language, <b>null</b> if none.
564: *
565: */
566: public String getLanguage() {
567: return getDCModule().getLanguage();
568: }
569:
570: /**
571: * Sets the feed language.
572: * <p>
573: * This method is a convenience method, it maps to the Dublin Core module language.
574: * <p>
575: * @param language the feed language to set, <b>null</b> if none.
576: *
577: */
578: public void setLanguage(String language) {
579: getDCModule().setLanguage(language);
580: }
581:
582: /**
583: * Returns the feed modules.
584: * <p>
585: * @return a list of ModuleImpl elements with the feed modules,
586: * an empty list if none.
587: *
588: */
589: public List getModules() {
590: if (_modules == null) {
591: _modules = new ArrayList();
592: }
593: if (ModuleUtils.getModule(_modules, DCModule.URI) == null) {
594: _modules.add(new DCModuleImpl());
595: }
596: return _modules;
597: }
598:
599: /**
600: * Sets the feed modules.
601: * <p>
602: * @param modules the list of ModuleImpl elements with the feed modules to set,
603: * an empty list or <b>null</b> if none.
604: *
605: */
606: public void setModules(List modules) {
607: _modules = modules;
608: }
609:
610: /**
611: * Returns the module identified by a given URI.
612: * <p>
613: * @param uri the URI of the ModuleImpl.
614: * @return The module with the given URI, <b>null</b> if none.
615: */
616: public Module getModule(String uri) {
617: return ModuleUtils.getModule(getModules(), uri);
618: }
619:
620: /**
621: * Returns the Dublin Core module of the feed.
622: * @return the DC module, it's never <b>null</b>
623: *
624: */
625: private DCModule getDCModule() {
626: return (DCModule) getModule(DCModule.URI);
627: }
628:
629: public Class getInterface() {
630: return SyndFeed.class;
631: }
632:
633: public void copyFrom(Object obj) {
634: COPY_FROM_HELPER.copy(this , obj);
635: }
636:
637: // TODO We need to find out how to refactor this one in a nice reusable way.
638:
639: private static final CopyFromHelper COPY_FROM_HELPER;
640:
641: static {
642: Map basePropInterfaceMap = new HashMap();
643: basePropInterfaceMap.put("feedType", String.class);
644: basePropInterfaceMap.put("encoding", String.class);
645: basePropInterfaceMap.put("uri", String.class);
646: basePropInterfaceMap.put("title", String.class);
647: basePropInterfaceMap.put("link", String.class);
648: basePropInterfaceMap.put("description", String.class);
649: basePropInterfaceMap.put("image", SyndImage.class);
650: basePropInterfaceMap.put("entries", SyndEntry.class);
651: basePropInterfaceMap.put("modules", Module.class);
652:
653: Map basePropClassImplMap = new HashMap();
654: basePropClassImplMap.put(SyndEntry.class, SyndEntryImpl.class);
655: basePropClassImplMap.put(SyndImage.class, SyndImageImpl.class);
656: basePropClassImplMap.put(DCModule.class, DCModuleImpl.class);
657: basePropClassImplMap.put(SyModule.class, SyModuleImpl.class);
658:
659: COPY_FROM_HELPER = new CopyFromHelper(SyndFeed.class,
660: basePropInterfaceMap, basePropClassImplMap);
661: }
662:
663: /**
664: * Returns the links
665: * <p>
666: * @return Returns the links.
667: */
668: public List getLinks() {
669: return _links;
670: }
671:
672: /**
673: * Set the links
674: * <p>
675: * @param links The links to set.
676: */
677: public void setLinks(List links) {
678: _links = links;
679: }
680:
681: public List getAuthors() {
682: return _authors;
683: }
684:
685: public void setAuthors(List authors) {
686: this ._authors = authors;
687: }
688:
689: /**
690: * Returns the feed author.
691: * <p>
692: * This method is a convenience method, it maps to the Dublin Core module creator.
693: * <p>
694: * @return the feed author, <b>null</b> if none.
695: *
696: */
697: public String getAuthor() {
698: return getDCModule().getCreator();
699: }
700:
701: /**
702: * Sets the feed author.
703: * <p>
704: * This method is a convenience method, it maps to the Dublin Core module creator.
705: * <p>
706: * @param author the feed author to set, <b>null</b> if none.
707: *
708: */
709: public void setAuthor(String author) {
710: getDCModule().setCreator(author);
711: }
712:
713: public List getContributors() {
714: return _contributors;
715: }
716:
717: public void setContributors(List contributors) {
718: this ._contributors = contributors;
719: }
720:
721: /**
722: * Returns foreign markup found at channel level.
723: * <p>
724: * @return Opaque object to discourage use
725: *
726: */
727: public Object getForeignMarkup() {
728: return (_foreignMarkup == null) ? (_foreignMarkup = new ArrayList())
729: : _foreignMarkup;
730: }
731:
732: /**
733: * Sets foreign markup found at channel level.
734: * <p>
735: * @param foreignMarkup Opaque object to discourage use
736: *
737: */
738: public void setForeignMarkup(Object foreignMarkup) {
739: _foreignMarkup = (List) foreignMarkup;
740: }
741: }
|