001/**
002 * Copyright 2011 Bill Brown
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 */
016package com.colorfulsoftware.rss;
017
018import java.io.Serializable;
019import java.util.LinkedList;
020import java.util.List;
021
022/**
023 * <p>
024 * The &lt;item> element.
025 * </p>
026 * <p>
027 * From the <a href="http://cyber.law.harvard.edu/rss/rss.html">RSS 2.0
028 * specification</a>...
029 * </p>
030 * <p>
031 * A channel may contain any number of &lt;item>s. An item may represent a
032 * "story" -- much like a story in a newspaper or magazine; if so its
033 * description is a synopsis of the story, and the link points to the full
034 * story. An item may also be complete in itself, if so, the description
035 * contains the text (entity-encoded HTML is allowed; see <a
036 * href="http://cyber.law.harvard.edu/rss/encodingDescriptions.html"
037 * >examples</a>), and the link and title may be omitted. All elements of an
038 * item are optional, however at least one of title or description must be
039 * present.
040 * </p>
041 * 
042 * @author Bill Brown
043 * 
044 */
045public class Item implements Serializable {
046
047        /**
048         * 
049         */
050        private static final long serialVersionUID = 6856122030764773780L;
051        /* one of the two of these is required */
052        private final Title title;
053
054        private final Description description;
055
056        /* optional elements */
057        private final Link link;
058
059        private final Author author;
060
061        private final List<Category> categories;
062
063        private final Comments comments;
064
065        private final Enclosure enclosure;
066
067        private final GUID guid;
068
069        private final PubDate pubDate;
070
071        private final Source source;
072
073        private final List<Extension> extensions;
074
075        private List<String> unboundPrefixes;
076
077        Item(Title title, Link link, Description description, Author author,
078                        List<Category> categories, Comments comments, Enclosure enclosure,
079                        GUID guid, PubDate pubDate, Source source,
080                        List<Extension> extensions) throws RSSpectException {
081
082                // make sure title or description is present
083                if (title == null && description == null) {
084                        throw new RSSpectException(
085                                        "item elements MUST contain either a title or description element.");
086                }
087                this.title = (title == null) ? null : new Title(title.getTitle());
088
089                this.link = (link == null) ? null : new Link(link);
090
091                this.description = (description == null) ? null : new Description(
092                                description.getDescription());
093
094                this.author = (author == null) ? null : new Author(author.getAuthor());
095
096                if (categories == null) {
097                        this.categories = null;
098                } else {
099                        this.categories = new LinkedList<Category>();
100                        for (Category category : categories) {
101                                this.categories.add(new Category(category));
102                        }
103                }
104
105                this.comments = (comments == null) ? null : new Comments(comments
106                                .getComments());
107
108                this.enclosure = (enclosure == null) ? null : new Enclosure(enclosure);
109
110                this.guid = (guid == null) ? null : new GUID(guid.getIsPermaLink(),
111                                guid.getGuid());
112
113                this.pubDate = (pubDate == null) ? null : new PubDate(pubDate
114                                .getDateTime());
115
116                this.source = (source == null) ? null : new Source(source);
117
118                // check that the extension prefixes are bound to a namespace
119                this.unboundPrefixes = new LinkedList<String>();
120
121                if (extensions == null) {
122                        this.extensions = null;
123                } else {
124                        this.extensions = new LinkedList<Extension>();
125
126                        for (Extension extension : extensions) {
127                                // check that the extension prefix is bound to a namespace
128                                String namespacePrefix = extension.getNamespacePrefix();
129                                if (namespacePrefix != null) {
130                                        this.unboundPrefixes.add(namespacePrefix);
131                                }
132                                this.extensions.add(new Extension(extension));
133                        }
134                }
135
136                this.unboundPrefixes = (this.unboundPrefixes.size() == 0) ? null
137                                : this.unboundPrefixes;
138        }
139
140        Item(Item item) {
141                this.title = item.getTitle();
142                this.description = item.getDescription();
143                this.link = item.getLink();
144                this.author = item.getAuthor();
145                this.categories = item.getCategories();
146                this.comments = item.getComments();
147                this.enclosure = item.getEnclosure();
148                this.guid = item.getGuid();
149                this.pubDate = item.getPubDate();
150                this.source = item.getSource();
151                this.extensions = item.getExtensions();
152        }
153
154        /**
155         * @return the title object.
156         */
157        public Title getTitle() {
158                return (title == null) ? null : new Title(title);
159        }
160
161        /**
162         * @return the description object.
163         */
164        public Description getDescription() {
165                return (description == null) ? null : new Description(description
166                                .getDescription());
167        }
168
169        /**
170         * @return the link object.
171         */
172        public Link getLink() {
173                return (link == null) ? null : new Link(link);
174        }
175
176        /**
177         * @return the author object.
178         */
179        public Author getAuthor() {
180                return (author == null) ? null : new Author(author);
181        }
182
183        /**
184         * @return the list of categories.
185         */
186        public List<Category> getCategories() {
187                if (categories == null) {
188                        return null;
189                } else {
190                        List<Category> catsCopy = new LinkedList<Category>();
191                        for (Category category : this.categories) {
192                                catsCopy.add(new Category(category));
193                        }
194                        return catsCopy;
195                }
196        }
197
198        /**
199         * @return the comments object.
200         */
201        public Comments getComments() {
202                return (comments == null) ? null : new Comments(comments);
203        }
204
205        /**
206         * @return the enclosure object.
207         */
208        public Enclosure getEnclosure() {
209                return (enclosure == null) ? null : new Enclosure(enclosure);
210        }
211
212        /**
213         * @return the guid object.
214         */
215        public GUID getGuid() {
216                return (guid == null) ? null : new GUID(guid);
217        }
218
219        /**
220         * @return the published date object.
221         */
222        public PubDate getPubDate() {
223                return (pubDate == null) ? null : new PubDate(pubDate.getDateTime());
224        }
225
226        /**
227         * @return the source object.
228         */
229        public Source getSource() {
230                return (source == null) ? null : new Source(source);
231        }
232
233        /**
234         * 
235         * @return the list of extensions for this entry.
236         */
237        public List<Extension> getExtensions() {
238                if (extensions == null) {
239                        return null;
240                }
241                List<Extension> extsCopy = new LinkedList<Extension>();
242                for (Extension extension : this.extensions) {
243                        extsCopy.add(new Extension(extension));
244                }
245                return extsCopy;
246        }
247
248        /**
249         * @param catValue
250         *            the value for the category.
251         * @return the category object if found otherwise null.
252         */
253        public Category getCategory(String catValue) {
254                if (this.categories != null) {
255                        for (Category category : this.categories) {
256                                if (category.getCategory() != null
257                                                && category.getCategory().equals(catValue)) {
258                                        return new Category(category);
259                                }
260                        }
261                }
262                return null;
263        }
264
265        /**
266         * @param extName
267         *            the element name of the extension. eg. "atom:link" or
268         *            "someExtension"
269         * @return the extension matching the element or null if not found.
270         */
271        public Extension getExtension(String extName) {
272                if (this.extensions != null) {
273                        for (Extension extension : this.extensions) {
274                                if (extension.getElementName().equals(extName)) {
275                                        return new Extension(extension);
276                                }
277                        }
278                }
279                return null;
280        }
281
282        /**
283         * Shows the contents of the &lt;item> element.
284         */
285        @Override
286        public String toString() {
287                StringBuilder sb = new StringBuilder("<item>");
288
289                if (title != null) {
290                        sb.append(title);
291                }
292
293                if (description != null) {
294                        sb.append(description);
295                }
296
297                if (link != null) {
298                        sb.append(link);
299                }
300
301                if (author != null) {
302                        sb.append(author);
303                }
304
305                if (categories != null) {
306                        for (Category category : categories) {
307                                sb.append(category);
308                        }
309                }
310
311                if (comments != null) {
312                        sb.append(comments);
313                }
314
315                if (enclosure != null) {
316                        sb.append(enclosure);
317                }
318
319                if (guid != null) {
320                        sb.append(guid);
321                }
322
323                if (pubDate != null) {
324                        sb.append(pubDate);
325                }
326
327                if (source != null) {
328                        sb.append(source);
329                }
330
331                if (extensions != null) {
332                        for (Extension extension : extensions) {
333                                sb.append(extension);
334                        }
335                }
336
337                sb.append("</item>");
338                return sb.toString();
339        }
340
341        List<String> getUnboundPrefixes() {
342                return unboundPrefixes;
343        }
344        
345        @Override
346        public boolean equals(Object obj) {
347                if (obj == this) {
348                        return true;
349                }
350                if (!(obj instanceof Item)) {
351                        return false;
352                }
353                return this.toString().equals(obj.toString());
354        }
355        
356        @Override public int hashCode() {
357                return toString().hashCode();
358        }
359}