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.Shorts.*;
020import static org.apache.juneau.marshall.xml.XmlFormat.*;
021
022import java.util.*;
023
024import org.apache.juneau.marshall.*;
025import org.apache.juneau.marshall.xml.*;
026
027/**
028 * Represents a top-level Atom feed document.
029 *
030 * <p>
031 * An Atom feed is a Web resource that contains metadata and optionally a set of entries.
032 * Feeds are the top-level container element in Atom documents and act as a manifest of metadata
033 * and data associated with a collection of related resources.
034 *
035 * <p>
036 * The feed is the fundamental unit of syndication in Atom and is used to aggregate entries that
037 * share a common purpose, such as a blog, podcast channel, or news source.
038 *
039 * <h5 class='figure'>Schema</h5>
040 * <p class='bschema'>
041 *    atomFeed =
042 *       element atom:feed {
043 *          atomCommonAttributes,
044 *          (atomAuthor*
045 *           &amp; atomCategory*
046 *           &amp; atomContributor*
047 *           &amp; atomGenerator?
048 *           &amp; atomIcon?
049 *           &amp; atomId
050 *           &amp; atomLink*
051 *           &amp; atomLogo?
052 *           &amp; atomRights?
053 *           &amp; atomSubtitle?
054 *           &amp; atomTitle
055 *           &amp; atomUpdated
056 *           &amp; extensionElement*),
057 *          atomEntry*
058 *       }
059 * </p>
060 *
061 * <h5 class='section'>Required Elements:</h5>
062 * <p>
063 * Per RFC 4287, the following elements are required in a feed:
064 * <ul class='spaced-list'>
065 *    <li><c>atom:id</c> - A permanent, universally unique identifier for the feed.
066 *    <li><c>atom:title</c> - A human-readable title for the feed.
067 *    <li><c>atom:updated</c> - The most recent instant in time when the feed was modified.
068 * </ul>
069 *
070 * <h5 class='section'>Recommended Elements:</h5>
071 * <p>
072 * The following elements are recommended but not required:
073 * <ul class='spaced-list'>
074 *    <li><c>atom:author</c> - Authors of the feed (required if entries don't have authors).
075 *    <li><c>atom:link</c> - Links associated with the feed (should include a "self" link).
076 * </ul>
077 *
078 * <h5 class='section'>Example:</h5>
079 * <p class='bjava'>
080 *    <jc>// Create a feed using fluent-style setters</jc>
081 *    Feed <jv>feed</jv> = <jk>new</jk> Feed(
082 *       <js>"tag:example.org,2024:feed"</js>,
083 *       <js>"Example Feed"</js>,
084 *       <js>"2024-01-15T12:00:00Z"</js>
085 *    )
086 *    .setSubtitle(<js>"A sample Atom feed"</js>)
087 *    .setLinks(
088 *       <jk>new</jk> Link(<js>"self"</js>, <js>"application/atom+xml"</js>, <js>"http://example.org/feed.atom"</js>),
089 *       <jk>new</jk> Link(<js>"alternate"</js>, <js>"text/html"</js>, <js>"http://example.org"</js>)
090 *    )
091 *    .setAuthors(
092 *       <jk>new</jk> Person(<js>"John Doe"</js>).setEmail(<js>"john@example.org"</js>)
093 *    )
094 *    .setEntries(
095 *       <jk>new</jk> Entry(<js>"tag:example.org,2024:entry1"</js>, <js>"First Post"</js>, <js>"2024-01-15T12:00:00Z"</js>)
096 *          .setSummary(<js>"This is the first post"</js>)
097 *    );
098 *
099 *    <jc>// Serialize to ATOM/XML</jc>
100 *    String <jv>atomXml</jv> = XmlSerializer.<jsf>DEFAULT_SQ_READABLE</jsf>.write(<jv>feed</jv>);
101 * </p>
102 *
103 * <h5 class='section'>Specification:</h5>
104 * <p>
105 * Represents an <c>atomFeed</c> construct in the
106 * <a class="doclink" href="https://tools.ietf.org/html/rfc4287#section-4.1.1">RFC 4287 - Section 4.1.1</a> specification.
107 *
108 * <h5 class='section'>See Also:</h5><ul>
109 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanAtom">juneau-bean-atom</a>
110 *    <li class='extlink'><a class="doclink" href="https://tools.ietf.org/html/rfc4287">RFC 4287 - The Atom Syndication Format</a>
111 * </ul>
112 */
113@Marshalled(typeName = "feed")
114public class Feed extends CommonEntry<Feed> {
115
116   // @formatter:off
117   private Generator generator;  // atomGenerator?
118   private Icon icon;            // atomIcon?
119   private Logo logo;            // atomLogo?
120   private Text subtitle;        // atomSubtitle?
121   private Entry[] entries;      // atomEntry*
122   // @formatter:on
123
124   /** Bean constructor. */
125   public Feed() {}
126
127   /**
128    * Normal constructor.
129    *
130    * @param id The feed identifier.  Can be <jk>null</jk> to leave the property unset.
131    * @param title The feed title.  Can be <jk>null</jk> to leave the property unset.
132    * @param updated The feed updated timestamp.  Can be <jk>null</jk> to leave the property unset.
133    */
134   public Feed(Id id, Text title, Calendar updated) {
135      super(id, title, updated);
136   }
137
138   /**
139    * Normal constructor.
140    *
141    * @param id The feed identifier.  Can be <jk>null</jk>, in which case the property is set to an {@link Id} with no text (not left unset).
142    * @param title The feed title.  Can be <jk>null</jk>, in which case the property is set to a {@link Text} with no text (not left unset).
143    * @param updated The feed updated timestamp.  Can be <jk>null</jk> or blank to leave the property unset.
144    */
145   public Feed(String id, String title, String updated) {
146      super(id, title, updated);
147   }
148
149   /**
150    * Bean property getter:  <property>entries</property>.
151    *
152    * <p>
153    * Returns the individual entries contained within this feed.
154    *
155    * <p>
156    * Each entry represents a single item in the feed, such as a blog post, news article,
157    * podcast episode, or other discrete piece of content. Entries contain their own metadata
158    * including title, content, links, and timestamps.
159    *
160    * @return The property value, or <jk>null</jk> if it is not set.
161    */
162   @Xml(format = COLLAPSED)
163   public Entry[] getEntries() { return cp(entries); }
164
165   /**
166    * Bean property getter:  <property>generator</property>.
167    *
168    * <p>
169    * Identifies the software agent used to generate the feed.
170    *
171    * <p>
172    * This is useful for debugging and analytics purposes, allowing consumers to identify the
173    * software responsible for producing the feed.
174    *
175    * @return The property value, or <jk>null</jk> if it is not set.
176    */
177   public Generator getGenerator() { return generator; }
178
179   /**
180    * Bean property getter:  <property>icon</property>.
181    *
182    * <p>
183    * Identifies a small image that provides iconic visual identification for the feed.
184    *
185    * <p>
186    * Icons should be square and small (typically 16x16 or similar). The image should have an
187    * aspect ratio of 1 (horizontal) to 1 (vertical).
188    *
189    * @return The property value, or <jk>null</jk> if it is not set.
190    */
191   public Icon getIcon() { return icon; }
192
193   /**
194    * Bean property getter:  <property>logo</property>.
195    *
196    * <p>
197    * Identifies a larger image that provides visual identification for the feed.
198    *
199    * <p>
200    * Logos should be twice as wide as they are tall (aspect ratio of 2:1).
201    *
202    * @return The property value, or <jk>null</jk> if it is not set.
203    */
204   public Logo getLogo() { return logo; }
205
206   /**
207    * Bean property getter:  <property>subtitle</property>.
208    *
209    * <p>
210    * Returns a human-readable description or subtitle for the feed.
211    *
212    * <p>
213    * The subtitle provides additional context about the feed's purpose or content beyond
214    * what is conveyed in the title.
215    *
216    * @return The property value, or <jk>null</jk> if it is not set.
217    */
218   public Text getSubtitle() { return subtitle; }
219
220   /**
221    * Bean property setter:  <property>entries</property>.
222    *
223    * <p>
224    * Sets the individual entries contained within this feed.
225    *
226    * <h5 class='section'>Example:</h5>
227    * <p class='bjava'>
228    *    Feed <jv>feed</jv> = <jk>new</jk> Feed(...)
229    *       .setEntries(
230    *          <jk>new</jk> Entry(
231    *             <js>"tag:example.org,2024:entry1"</js>,
232    *             <js>"First Post"</js>,
233    *             <js>"2024-01-15T12:00:00Z"</js>
234    *          )
235    *          .setContent(
236    *             <jk>new</jk> Content(<js>"html"</js>)
237    *                .setText(<js>"&lt;p&gt;This is the content&lt;/p&gt;"</js>)
238    *          ),
239    *          <jk>new</jk> Entry(
240    *             <js>"tag:example.org,2024:entry2"</js>,
241    *             <js>"Second Post"</js>,
242    *             <js>"2024-01-16T12:00:00Z"</js>
243    *          )
244    *       );
245    * </p>
246    *
247    * @param value
248    *    The new value for this property.
249    *    <br>Can be <jk>null</jk> to unset the property.
250    * @return This object.
251    */
252   public Feed setEntries(Entry...value) {
253      entries = cp(value);
254      return this;
255   }
256
257   /**
258    * Bean property setter:  <property>generator</property>.
259    *
260    * <p>
261    * Identifies the software agent used to generate the feed.
262    *
263    * <h5 class='section'>Example:</h5>
264    * <p class='bjava'>
265    *    Feed <jv>feed</jv> = <jk>new</jk> Feed(...)
266    *       .setGenerator(
267    *          <jk>new</jk> Generator(<js>"My Blog Software"</js>)
268    *             .setUri(<js>"http://www.example.com/software"</js>)
269    *             .setVersion(<js>"2.0"</js>)
270    *       );
271    * </p>
272    *
273    * @param value
274    *    The new value for this property.
275    *    <br>Can be <jk>null</jk> to unset the property.
276    * @return This object.
277    */
278   public Feed setGenerator(Generator value) {
279      generator = value;
280      return this;
281   }
282
283   /**
284    * Bean property setter:  <property>icon</property>.
285    *
286    * <p>
287    * Identifies a small image that provides iconic visual identification for the feed.
288    *
289    * <h5 class='section'>Example:</h5>
290    * <p class='bjava'>
291    *    Feed <jv>feed</jv> = <jk>new</jk> Feed(...)
292    *       .setIcon(<jk>new</jk> Icon(<js>"http://example.org/icon.png"</js>));
293    * </p>
294    *
295    * @param value
296    *    The new value for this property.
297    *    <br>Can be <jk>null</jk> to unset the property.
298    * @return This object.
299    */
300   public Feed setIcon(Icon value) {
301      icon = value;
302      return this;
303   }
304
305   /**
306    * Bean property setter:  <property>logo</property>.
307    *
308    * <p>
309    * Identifies a larger image that provides visual identification for the feed.
310    *
311    * <h5 class='section'>Example:</h5>
312    * <p class='bjava'>
313    *    Feed <jv>feed</jv> = <jk>new</jk> Feed(...)
314    *       .setLogo(<jk>new</jk> Logo(<js>"http://example.org/logo.png"</js>));
315    * </p>
316    *
317    * @param value
318    *    The new value for this property.
319    *    <br>Can be <jk>null</jk> to unset the property.
320    * @return This object.
321    */
322   public Feed setLogo(Logo value) {
323      logo = value;
324      return this;
325   }
326
327   /**
328    * Bean property fluent setter:  <property>subtitle</property>.
329    *
330    * <p>
331    * Sets a human-readable description or subtitle for the feed as plain text.
332    *
333    * @param value
334    *    The new value for this property.
335    *    <br>Can be <jk>null</jk> to unset the property.
336    * @return This object.
337    */
338   public Feed setSubtitle(String value) {
339      setSubtitle(new Text(value));
340      return this;
341   }
342
343   /**
344    * Bean property setter:  <property>subtitle</property>.
345    *
346    * <p>
347    * Sets a human-readable description or subtitle for the feed.
348    *
349    * <h5 class='section'>Example:</h5>
350    * <p class='bjava'>
351    *    Feed <jv>feed</jv> = <jk>new</jk> Feed(...)
352    *       .setSubtitle(
353    *          <jk>new</jk> Text(<js>"html"</js>)
354    *             .setText(<js>"A &lt;em&gt;comprehensive&lt;/em&gt; guide to Atom feeds"</js>)
355    *       );
356    * </p>
357    *
358    * @param value
359    *    The new value for this property.
360    *    <br>Can be <jk>null</jk> to unset the property.
361    * @return This object.
362    */
363   public Feed setSubtitle(Text value) {
364      subtitle = value;
365      return this;
366   }
367}