001/** 002 * Copyright 2011 Bill Brown 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.colorfulsoftware.rss; 017 018import java.io.Serializable; 019import java.util.LinkedList; 020import java.util.List; 021 022/** 023 * <p> 024 * The <channel> element. 025 * </p> 026 * <p> 027 * From the <a href="http://cyber.law.harvard.edu/rss/rss.html">RSS 2.0 028 * specification</a>... 029 * </p> 030 * <p> 031 * A channel may contain any number of <item>s. An item may represent a 032 * "story" -- much like a story in a newspaper or magazine; if so its 033 * description is a synopsis of the story, and the link points to the full 034 * story. An item may also be complete in itself, if so, the description 035 * contains the text (entity-encoded HTML is allowed; see examples), and the 036 * link and title may be omitted. All elements of an item are optional, however 037 * at least one of title or description must be present. 038 * </p> 039 * 040 * @author Bill Brown 041 * 042 */ 043public class Channel implements Serializable { 044 045 /** 046 * 047 */ 048 private static final long serialVersionUID = -6193654133883709775L; 049 050 /* required fields */ 051 private final Title title; 052 053 private final Link link; 054 055 private final Description description; 056 057 /* optional fields */ 058 private final Language language; 059 060 private final Copyright copyright; 061 062 private final ManagingEditor managingEditor; 063 064 private final WebMaster webMaster; 065 066 private final PubDate pubDate; 067 068 private final LastBuildDate lastBuildDate; 069 070 private final List<Category> categories; 071 072 private final Generator generator; 073 074 private final Docs docs; 075 076 private final Cloud cloud; 077 078 private final TTL ttl; 079 080 private final Image image; 081 082 private final Rating rating; 083 084 private final TextInput textInput; 085 086 private final SkipHours skipHours; 087 088 private final SkipDays skipDays; 089 090 private final List<Item> items; 091 092 private final List<Extension> extensions; 093 094 private List<String> unboundPrefixes; 095 096 Channel(Title title, Link link, Description description, Language language, 097 Copyright copyright, ManagingEditor managingEditor, 098 WebMaster webMaster, PubDate pubDate, LastBuildDate lastBuildDate, 099 List<Category> categories, Generator generator, Docs docs, 100 Cloud cloud, TTL ttl, Image image, Rating rating, 101 TextInput textInput, SkipHours skipHours, SkipDays skipDays, 102 List<Extension> extensions, List<Item> items) 103 throws RSSpectException { 104 105 // make sure title is present 106 if (title == null) { 107 throw new RSSpectException( 108 "channel elements MUST contain a title element."); 109 } 110 this.title = new Title(title); 111 112 // make sure link is present 113 if (link == null) { 114 throw new RSSpectException( 115 "channel elements MUST contain a link element."); 116 } 117 this.link = new Link(link); 118 119 // make sure description is present 120 if (description == null) { 121 throw new RSSpectException( 122 "channel elements MUST contain a description element."); 123 } 124 this.description = new Description(description); 125 126 this.language = (language == null) ? null : new Language(language); 127 this.copyright = (copyright == null) ? null : new Copyright(copyright); 128 this.managingEditor = (managingEditor == null) ? null 129 : new ManagingEditor(managingEditor); 130 this.webMaster = (webMaster == null) ? null : new WebMaster(webMaster); 131 this.pubDate = (pubDate == null) ? null : new PubDate(pubDate 132 .getDateTime()); 133 this.lastBuildDate = (lastBuildDate == null) ? null 134 : new LastBuildDate(lastBuildDate.getDateTime()); 135 if (categories == null) { 136 this.categories = null; 137 } else { 138 this.categories = new LinkedList<Category>(); 139 for (Category category : categories) { 140 this.categories.add(new Category(category)); 141 } 142 } 143 this.generator = (generator == null) ? null : new Generator(generator 144 .getGenerator()); 145 this.docs = (docs == null) ? null : new Docs(docs.getDocs()); 146 this.cloud = (cloud == null) ? null : new Cloud(cloud); 147 this.ttl = (ttl == null) ? null : new TTL(ttl.getTtl()); 148 this.image = (image == null) ? null : new Image(image); 149 this.rating = (rating == null) ? null : new Rating(rating.getRating()); 150 this.textInput = (textInput == null) ? null : new TextInput(textInput); 151 this.skipHours = (skipHours == null) ? null : new SkipHours(skipHours); 152 this.skipDays = (skipDays == null) ? null : new SkipDays(skipDays); 153 154 // check that the extension prefixes are bound to a namespace 155 this.unboundPrefixes = new LinkedList<String>(); 156 157 if (items == null) { 158 this.items = null; 159 } else { 160 this.items = new LinkedList<Item>(); 161 for (Item item : items) { 162 // add any unbound prefixes to test. 163 if (item.getUnboundPrefixes() != null) { 164 this.unboundPrefixes.addAll(item.getUnboundPrefixes()); 165 } 166 this.items.add(new Item(item)); 167 } 168 } 169 170 if (extensions == null) { 171 this.extensions = null; 172 } else { 173 this.extensions = new LinkedList<Extension>(); 174 175 for (Extension extension : extensions) { 176 // check that the extension prefix is bound to a namespace 177 String namespacePrefix = extension.getNamespacePrefix(); 178 if (namespacePrefix != null) { 179 this.unboundPrefixes.add(namespacePrefix); 180 } 181 this.extensions.add(new Extension(extension)); 182 } 183 } 184 185 this.unboundPrefixes = (this.unboundPrefixes.size() == 0) ? null 186 : this.unboundPrefixes; 187 } 188 189 Channel(Channel channel) { 190 this.title = channel.getTitle(); 191 this.link = channel.getLink(); 192 this.description = channel.getDescription(); 193 this.language = channel.getLanguage(); 194 this.copyright = channel.getCopyright(); 195 this.managingEditor = channel.getManagingEditor(); 196 this.webMaster = channel.getWebMaster(); 197 this.pubDate = channel.getPubDate(); 198 this.lastBuildDate = channel.getLastBuildDate(); 199 this.categories = channel.getCategories(); 200 this.generator = channel.getGenerator(); 201 this.docs = channel.getDocs(); 202 this.cloud = channel.getCloud(); 203 this.ttl = channel.getTtl(); 204 this.image = channel.getImage(); 205 this.rating = channel.getRating(); 206 this.textInput = channel.getTextInput(); 207 this.skipHours = channel.getSkipHours(); 208 this.skipDays = channel.getSkipDays(); 209 this.items = channel.getItems(); 210 this.extensions = channel.getExtensions(); 211 this.unboundPrefixes = channel.getUnboundPrefixes(); 212 } 213 214 /** 215 * @return the title. 216 */ 217 public Title getTitle() { 218 return new Title(title); 219 } 220 221 /** 222 * @return the link element. 223 */ 224 public Link getLink() { 225 return new Link(link); 226 } 227 228 /** 229 * @return the description element. 230 */ 231 public Description getDescription() { 232 return new Description(description.getDescription()); 233 } 234 235 /** 236 * @return the language element. 237 */ 238 public Language getLanguage() { 239 return (language == null) ? null : new Language(language); 240 } 241 242 /** 243 * @return the copyright element 244 */ 245 public Copyright getCopyright() { 246 return (copyright == null) ? null : new Copyright(copyright); 247 } 248 249 /** 250 * @return the managing editor element. 251 */ 252 public ManagingEditor getManagingEditor() { 253 return (managingEditor == null) ? null : new ManagingEditor( 254 managingEditor); 255 } 256 257 /** 258 * @return the webmaster element. 259 */ 260 public WebMaster getWebMaster() { 261 return (webMaster == null) ? null : new WebMaster(webMaster); 262 } 263 264 /** 265 * @return the pubDate element. 266 */ 267 public PubDate getPubDate() { 268 return (pubDate == null) ? null : new PubDate(pubDate.getDateTime()); 269 } 270 271 /** 272 * @return the last build date element. 273 */ 274 public LastBuildDate getLastBuildDate() { 275 return (lastBuildDate == null) ? null : new LastBuildDate(lastBuildDate 276 .getDateTime()); 277 } 278 279 /** 280 * @return the list of categories. 281 */ 282 public List<Category> getCategories() { 283 if (categories == null) { 284 return null; 285 } else { 286 List<Category> catsCopy = new LinkedList<Category>(); 287 for (Category category : this.categories) { 288 catsCopy.add(new Category(category)); 289 } 290 return catsCopy; 291 } 292 } 293 294 /** 295 * @return the generator element. 296 */ 297 public Generator getGenerator() { 298 return (generator == null) ? null : new Generator(generator); 299 } 300 301 /** 302 * @return the docs element. 303 */ 304 public Docs getDocs() { 305 return (docs == null) ? null : new Docs(docs); 306 } 307 308 /** 309 * @return the cloud element. 310 */ 311 public Cloud getCloud() { 312 return (cloud == null) ? null : new Cloud(cloud); 313 } 314 315 /** 316 * @return the ttl element. 317 */ 318 public TTL getTtl() { 319 return (ttl == null) ? null : new TTL(ttl); 320 } 321 322 /** 323 * @return the image element. 324 */ 325 public Image getImage() { 326 return (image == null) ? null : new Image(image); 327 } 328 329 /** 330 * @return the rating element. 331 */ 332 public Rating getRating() { 333 return (rating == null) ? null : new Rating(rating); 334 } 335 336 /** 337 * @return the textInput element. 338 */ 339 public TextInput getTextInput() { 340 return (textInput == null) ? null : new TextInput(textInput); 341 } 342 343 /** 344 * @return the skipHours element. 345 */ 346 public SkipHours getSkipHours() { 347 return (skipHours == null) ? null : new SkipHours(skipHours); 348 } 349 350 /** 351 * @return the skipDays element. 352 */ 353 public SkipDays getSkipDays() { 354 return (skipDays == null) ? null : new SkipDays(skipDays); 355 } 356 357 /** 358 * @return the list of items. 359 */ 360 public List<Item> getItems() { 361 if (items == null) { 362 return null; 363 } else { 364 List<Item> itemsCopy = new LinkedList<Item>(); 365 for (Item item : this.items) { 366 itemsCopy.add(new Item(item)); 367 } 368 return itemsCopy; 369 } 370 } 371 372 /** 373 * 374 * @return the extensions for this entry. 375 */ 376 public List<Extension> getExtensions() { 377 if (extensions == null) { 378 return null; 379 } 380 List<Extension> extsCopy = new LinkedList<Extension>(); 381 for (Extension extension : this.extensions) { 382 extsCopy.add(new Extension(extension)); 383 } 384 return extsCopy; 385 } 386 387 /** 388 * @param catValue 389 * the value of the category. 390 * @return the category name matching this item or null if not found. 391 */ 392 public Category getCategory(String catValue) { 393 if (this.categories != null) { 394 for (Category category : this.categories) { 395 if (category.getCategory() != null 396 && category.getCategory().equals(catValue)) { 397 return new Category(category); 398 } 399 } 400 } 401 return null; 402 } 403 404 /** 405 * @param titleOrDescription 406 * the title or description data. 407 * @return the item with this title or description. returns null if not 408 * found. 409 */ 410 public Item getItem(String titleOrDescription) { 411 if (this.items != null) { 412 for (Item item : this.items) { 413 if ((item.getTitle() != null 414 && item.getTitle().getTitle() != null && item 415 .getTitle().getTitle().equals(titleOrDescription)) 416 || (item.getDescription() != null && item 417 .getDescription().getDescription().equals( 418 titleOrDescription))) { 419 return new Item(item); 420 } 421 } 422 } 423 return null; 424 } 425 426 /** 427 * @param extName 428 * the element name of the extension. eg. "atom:link" or 429 * "someExtension" 430 * @return the extension matching the element or null if not found. 431 */ 432 public Extension getExtension(String extName) { 433 if (this.extensions != null) { 434 for (Extension extension : this.extensions) { 435 if (extension.getElementName().equals(extName)) { 436 return new Extension(extension); 437 } 438 } 439 } 440 return null; 441 } 442 443 /** 444 * Shows the contents of the <channel> element. 445 */ 446 @Override 447 public String toString() { 448 StringBuilder sb = new StringBuilder("<channel>"); 449 450 sb.append(title); 451 452 sb.append(link); 453 454 sb.append(description); 455 456 if (language != null) { 457 sb.append(language); 458 } 459 460 if (copyright != null) { 461 sb.append(copyright); 462 } 463 464 if (managingEditor != null) { 465 sb.append(managingEditor); 466 } 467 468 if (webMaster != null) { 469 sb.append(webMaster); 470 } 471 472 if (pubDate != null) { 473 sb.append(pubDate); 474 } 475 476 if (lastBuildDate != null) { 477 sb.append(lastBuildDate); 478 } 479 480 if (categories != null) { 481 for (Category category : categories) { 482 sb.append(category); 483 } 484 } 485 486 if (generator != null) { 487 sb.append(generator); 488 } 489 490 if (docs != null) { 491 sb.append(docs); 492 } 493 494 if (cloud != null) { 495 sb.append(cloud); 496 } 497 498 if (ttl != null) { 499 sb.append(ttl); 500 } 501 502 if (image != null) { 503 sb.append(image); 504 } 505 506 if (rating != null) { 507 sb.append(rating); 508 } 509 510 if (textInput != null) { 511 sb.append(textInput); 512 } 513 514 if (skipHours != null) { 515 sb.append(skipHours); 516 } 517 518 if (skipDays != null) { 519 sb.append(skipDays); 520 } 521 522 if (items != null) { 523 for (Item item : items) { 524 sb.append(item); 525 } 526 } 527 528 if (extensions != null) { 529 for (Extension extension : extensions) { 530 sb.append(extension); 531 } 532 } 533 534 sb.append("</channel>"); 535 return sb.toString(); 536 } 537 538 List<String> getUnboundPrefixes() { 539 return unboundPrefixes; 540 } 541 542 @Override 543 public boolean equals(Object obj) { 544 if (obj == this) { 545 return true; 546 } 547 if (!(obj instanceof Channel)) { 548 return false; 549 } 550 return this.toString().equals(obj.toString()); 551 } 552 553 @Override public int hashCode() { 554 return toString().hashCode(); 555 } 556}