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