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