001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.atom;
018
019import static org.apache.juneau.bean.atom.Utils.*;
020
021import java.util.*;
022
023import org.apache.juneau.annotation.*;
024
025/**
026 * Represents an individual entry within an Atom feed or as a standalone Atom document.
027 *
028 * <p>
029 * An Atom entry is a discrete item of content within a feed, such as a blog post, news article, 
030 * podcast episode, or other individual piece of content. Entries can exist within a feed or be 
031 * published as standalone Atom documents.
032 *
033 * <p>
034 * Each entry contains metadata about the content (title, authors, timestamps) and optionally the 
035 * content itself or links to it. Entries are designed to be independently meaningful and may be 
036 * consumed separately from their containing feed.
037 *
038 * <h5 class='figure'>Schema</h5>
039 * <p class='bschema'>
040 *    atomEntry =
041 *       element atom:entry {
042 *          atomCommonAttributes,
043 *          (atomAuthor*
044 *          &amp; atomCategory*
045 *          &amp; atomContent?
046 *          &amp; atomContributor*
047 *          &amp; atomId
048 *          &amp; atomLink*
049 *          &amp; atomPublished?
050 *          &amp; atomRights?
051 *          &amp; atomSource?
052 *          &amp; atomSummary?
053 *          &amp; atomTitle
054 *          &amp; atomUpdated
055 *          &amp; extensionElement*)
056 *       }
057 * </p>
058 *
059 * <h5 class='section'>Required Elements:</h5>
060 * <p>
061 * Per RFC 4287, the following elements are required in an entry:
062 * <ul class='spaced-list'>
063 *    <li><c>atom:id</c> - A permanent, universally unique identifier for the entry.
064 *    <li><c>atom:title</c> - A human-readable title for the entry.
065 *    <li><c>atom:updated</c> - The most recent instant in time when the entry was modified.
066 * </ul>
067 *
068 * <h5 class='section'>Recommended Elements:</h5>
069 * <p>
070 * The following elements are recommended:
071 * <ul class='spaced-list'>
072 *    <li><c>atom:author</c> - Authors of the entry (required if feed doesn't have authors).
073 *    <li><c>atom:content</c> or <c>atom:link[@rel="alternate"]</c> - Either content or alternate link.
074 *    <li><c>atom:summary</c> - Brief summary of the entry (required if content is not inline).
075 * </ul>
076 *
077 * <h5 class='section'>Example:</h5>
078 * <p class='bjava'>
079 *    <jc>// Create an entry</jc>
080 *    Entry <jv>entry</jv> = <jk>new</jk> Entry(
081 *       <js>"tag:example.org,2024:entry1"</js>,
082 *       <js>"My First Blog Post"</js>,
083 *       <js>"2024-01-15T12:00:00Z"</js>
084 *    )
085 *    .setAuthors(
086 *       <jk>new</jk> Person(<js>"Jane Doe"</js>)
087 *          .setEmail(<js>"jane@example.org"</js>)
088 *    )
089 *    .setContent(
090 *       <jk>new</jk> Content(<js>"html"</js>)
091 *          .setText(<js>"&lt;p&gt;This is my first blog post!&lt;/p&gt;"</js>)
092 *    )
093 *    .setSummary(<js>"An introduction to my new blog"</js>)
094 *    .setPublished(<js>"2024-01-15T10:00:00Z"</js>)
095 *    .setLinks(
096 *       <jk>new</jk> Link(<js>"alternate"</js>, <js>"text/html"</js>, <js>"http://example.org/posts/1"</js>)
097 *    );
098 * </p>
099 *
100 * <h5 class='section'>Specification:</h5>
101 * <p>
102 * Represents an <c>atomEntry</c> construct in the 
103 * <a class="doclink" href="https://tools.ietf.org/html/rfc4287#section-4.1.2">RFC 4287 - Section 4.1.2</a> specification.
104 *
105 * <h5 class='section'>See Also:</h5><ul>
106 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanAtom">juneau-bean-atom</a>
107 *    <li class='extlink'><a class="doclink" href="https://tools.ietf.org/html/rfc4287">RFC 4287 - The Atom Syndication Format</a>
108 * </ul>
109 */
110@Bean(typeName="entry")
111public class Entry extends CommonEntry {
112
113   private Content content;
114   private Calendar published;
115   private Source source;
116   private Text summary;
117
118   /**
119    * Normal constructor.
120    *
121    * @param id The ID of this entry.
122    * @param title The title of this entry.
123    * @param updated The updated timestamp of this entry.
124    */
125   public Entry(Id id, Text title, Calendar updated) {
126      super(id, title, updated);
127   }
128
129   /**
130    * Normal constructor.
131    *
132    * @param id The ID of this entry.
133    * @param title The title of this entry.
134    * @param updated The updated timestamp of this entry.
135    */
136   public Entry(String id, String title, String updated) {
137      super(id, title, updated);
138   }
139
140   /** Bean constructor. */
141   public Entry() {}
142
143
144   //-----------------------------------------------------------------------------------------------------------------
145   // Bean properties
146   //-----------------------------------------------------------------------------------------------------------------
147
148   /**
149    * Bean property getter:  <property>content</property>.
150    *
151    * <p>
152    * Returns the content of this entry, or a link to it.
153    *
154    * <p>
155    * The content element contains or links to the complete content of the entry. It can contain 
156    * text, HTML, XHTML, or other media types. When not present, the entry must have an alternate 
157    * link pointing to the content.
158    *
159    * @return The property value, or <jk>null</jk> if it is not set.
160    */
161   public Content getContent() {
162      return content;
163   }
164
165   /**
166    * Bean property setter:  <property>content</property>.
167    *
168    * <p>
169    * Sets the content of this entry.
170    *
171    * <h5 class='section'>Examples:</h5>
172    * <p class='bjava'>
173    *    <jc>// Plain text content</jc>
174    *    Entry <jv>entry</jv> = <jk>new</jk> Entry(...)
175    *       .setContent(
176    *          <jk>new</jk> Content(<js>"text"</js>)
177    *             .setText(<js>"This is plain text content"</js>)
178    *       );
179    *
180    *    <jc>// HTML content</jc>
181    *    Entry <jv>entry2</jv> = <jk>new</jk> Entry(...)
182    *       .setContent(
183    *          <jk>new</jk> Content(<js>"html"</js>)
184    *             .setText(<js>"&lt;p&gt;This is &lt;strong&gt;HTML&lt;/strong&gt; content&lt;/p&gt;"</js>)
185    *       );
186    *
187    *    <jc>// Link to external content</jc>
188    *    Entry <jv>entry3</jv> = <jk>new</jk> Entry(...)
189    *       .setContent(
190    *          <jk>new</jk> Content()
191    *             .setType(<js>"video/mp4"</js>)
192    *             .setSrc(<js>"http://example.org/video.mp4"</js>)
193    *       );
194    * </p>
195    *
196    * @param value
197    *    The new value for this property.
198    *    <br>Can be <jk>null</jk> to unset the property.
199    * @return This object.
200    */
201   public Entry setContent(Content value) {
202      this.content = value;
203      return this;
204   }
205
206   /**
207    * Bean property getter:  <property>published</property>.
208    *
209    * <p>
210    * Returns the time when this entry was first published or made available.
211    *
212    * <p>
213    * This differs from the updated time in that it represents the original publication date, 
214    * which typically doesn't change even when the entry is modified. The updated timestamp 
215    * reflects the last modification time.
216    *
217    * @return The property value, or <jk>null</jk> if it is not set.
218    */
219   public Calendar getPublished() {
220      return published;
221   }
222
223   /**
224    * Bean property setter:  <property>published</property>.
225    *
226    * <p>
227    * Sets the time when this entry was first published or made available.
228    *
229    * <h5 class='section'>Example:</h5>
230    * <p class='bjava'>
231    *    Entry <jv>entry</jv> = <jk>new</jk> Entry(...)
232    *       .setPublished(Calendar.<jsm>getInstance</jsm>());
233    * </p>
234    *
235    * @param value
236    *    The new value for this property.
237    *    <br>Can be <jk>null</jk> to unset the property.
238    * @return This object.
239    */
240   public Entry setPublished(Calendar value) {
241      this.published = value;
242      return this;
243   }
244
245   /**
246    * Bean property fluent setter:  <property>published</property>.
247    *
248    * <p>
249    * Sets the time when this entry was first published using an ISO-8601 date string.
250    *
251    * <h5 class='section'>Example:</h5>
252    * <p class='bjava'>
253    *    Entry <jv>entry</jv> = <jk>new</jk> Entry(...)
254    *       .setPublished(<js>"2024-01-15T10:00:00Z"</js>);
255    * </p>
256    *
257    * @param value
258    *    The new value for this property in ISO-8601 format.
259    *    <br>Can be <jk>null</jk> to unset the property.
260    * @return This object.
261    */
262   public Entry setPublished(String value) {
263      setPublished(parseDateTime(value));
264      return this;
265   }
266
267   /**
268    * Bean property getter:  <property>source</property>.
269    *
270    * <p>
271    * Returns metadata about the source feed if this entry was copied from another feed.
272    *
273    * <p>
274    * When an entry is copied or aggregated from another feed, the source element preserves 
275    * metadata from the original feed. This is useful for attribution and tracking the origin 
276    * of syndicated content.
277    *
278    * @return The property value, or <jk>null</jk> if it is not set.
279    */
280   public Source getSource() {
281      return source;
282   }
283
284   /**
285    * Bean property setter:  <property>source</property>.
286    *
287    * <p>
288    * Sets metadata about the source feed if this entry was copied from another feed.
289    *
290    * <h5 class='section'>Example:</h5>
291    * <p class='bjava'>
292    *    Entry <jv>entry</jv> = <jk>new</jk> Entry(...)
293    *       .setSource(
294    *          <jk>new</jk> Source()
295    *             .setId(<js>"tag:originalblog.example.com,2024:feed"</js>)
296    *             .setTitle(<js>"Original Blog"</js>)
297    *             .setUpdated(<js>"2024-01-15T12:00:00Z"</js>)
298    *       );
299    * </p>
300    *
301    * @param value
302    *    The new value for this property.
303    *    <br>Can be <jk>null</jk> to unset the property.
304    * @return This object.
305    */
306   public Entry setSource(Source value) {
307      this.source = value;
308      return this;
309   }
310
311   /**
312    * Bean property getter:  <property>summary</property>.
313    *
314    * <p>
315    * Returns a short summary, abstract, or excerpt of the entry.
316    *
317    * <p>
318    * The summary is typically used in feed readers to give users a preview of the entry's 
319    * content without loading the full content. It's especially useful when content is not 
320    * inline or is very long.
321    *
322    * @return The property value, or <jk>null</jk> if it is not set.
323    */
324   public Text getSummary() {
325      return summary;
326   }
327
328   /**
329    * Bean property setter:  <property>summary</property>.
330    *
331    * <p>
332    * Sets a short summary, abstract, or excerpt of the entry.
333    *
334    * <h5 class='section'>Example:</h5>
335    * <p class='bjava'>
336    *    Entry <jv>entry</jv> = <jk>new</jk> Entry(...)
337    *       .setSummary(
338    *          <jk>new</jk> Text(<js>"text"</js>)
339    *             .setText(<js>"This entry discusses the benefits of Atom feeds..."</js>)
340    *       );
341    * </p>
342    *
343    * @param value
344    *    The new value for this property.
345    *    <br>Can be <jk>null</jk> to unset the property.
346    * @return This object.
347    */
348   public Entry setSummary(Text value) {
349      this.summary = value;
350      return this;
351   }
352
353   /**
354    * Bean property fluent setter:  <property>summary</property>.
355    *
356    * <p>
357    * Sets a short summary, abstract, or excerpt of the entry as plain text.
358    *
359    * @param value
360    *    The new value for this property.
361    *    <br>Can be <jk>null</jk> to unset the property.
362    * @return This object.
363    */
364   public Entry setSummary(String value) {
365      setSummary(new Text(value));
366      return this;
367   }
368
369
370   //-----------------------------------------------------------------------------------------------------------------
371   // Overridden setters (to simplify method chaining)
372   //-----------------------------------------------------------------------------------------------------------------
373
374   @Override /* Overridden from Common */
375   public Entry setBase(Object value) {
376      super.setBase(value);
377      return this;
378   }
379
380   @Override /* Overridden from Common */
381   public Entry setLang(String value) {
382      super.setLang(value);
383      return this;
384   }
385
386   @Override /* Overridden from CommonEntry */
387   public Entry setAuthors(Person...value) {
388      super.setAuthors(value);
389      return this;
390   }
391
392   @Override /* Overridden from CommonEntry */
393   public Entry setCategories(Category...value) {
394      super.setCategories(value);
395      return this;
396   }
397
398   @Override /* Overridden from CommonEntry */
399   public Entry setContributors(Person...value) {
400      super.setContributors(value);
401      return this;
402   }
403
404   @Override /* Overridden from CommonEntry */
405   public Entry setId(String value) {
406      super.setId(value);
407      return this;
408   }
409
410   @Override /* Overridden from CommonEntry */
411   public Entry setId(Id value) {
412      super.setId(value);
413      return this;
414   }
415
416   @Override /* Overridden from CommonEntry */
417   public Entry setLinks(Link...value) {
418      super.setLinks(value);
419      return this;
420   }
421
422   @Override /* Overridden from CommonEntry */
423   public Entry setRights(String value) {
424      super.setRights(value);
425      return this;
426   }
427
428   @Override /* Overridden from CommonEntry */
429   public Entry setRights(Text value) {
430      super.setRights(value);
431      return this;
432   }
433
434   @Override /* Overridden from CommonEntry */
435   public Entry setTitle(String value) {
436      super.setTitle(value);
437      return this;
438   }
439
440   @Override /* Overridden from CommonEntry */
441   public Entry setTitle(Text value) {
442      super.setTitle(value);
443      return this;
444   }
445
446   @Override /* Overridden from CommonEntry */
447   public Entry setUpdated(String value) {
448      super.setUpdated(value);
449      return this;
450   }
451
452   @Override /* Overridden from CommonEntry */
453   public Entry setUpdated(Calendar value) {
454      super.setUpdated(value);
455      return this;
456   }
457}