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 * & atomCategory* 045 * & atomContent? 046 * & atomContributor* 047 * & atomId 048 * & atomLink* 049 * & atomPublished? 050 * & atomRights? 051 * & atomSource? 052 * & atomSummary? 053 * & atomTitle 054 * & atomUpdated 055 * & 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>"<p>This is my first blog post!</p>"</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>"<p>This is <strong>HTML</strong> content</p>"</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}