Coverage Report - net.sourceforge.pebble.domain.BlogEntry
 
Classes in this File Line Coverage Branch Coverage Complexity
BlogEntry
94%
272/288
82%
109/132
2.386
 
 1  
 /*
 2  
  * Copyright (c) 2003-2011, Simon Brown
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions are met:
 7  
  *
 8  
  *   - Redistributions of source code must retain the above copyright
 9  
  *     notice, this list of conditions and the following disclaimer.
 10  
  *
 11  
  *   - Redistributions in binary form must reproduce the above copyright
 12  
  *     notice, this list of conditions and the following disclaimer in
 13  
  *     the documentation and/or other materials provided with the
 14  
  *     distribution.
 15  
  *
 16  
  *   - Neither the name of Pebble nor the names of its contributors may
 17  
  *     be used to endorse or promote products derived from this software
 18  
  *     without specific prior written permission.
 19  
  *
 20  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 21  
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23  
  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 24  
  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 25  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 26  
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 27  
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 28  
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 29  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 30  
  * POSSIBILITY OF SUCH DAMAGE.
 31  
  */
 32  
 package net.sourceforge.pebble.domain;
 33  
 
 34  
 import net.sourceforge.pebble.api.event.blogentry.BlogEntryEvent;
 35  
 import net.sourceforge.pebble.api.event.comment.CommentEvent;
 36  
 import net.sourceforge.pebble.api.event.trackback.TrackBackEvent;
 37  
 import net.sourceforge.pebble.comparator.ResponseByDateComparator;
 38  
 import net.sourceforge.pebble.web.validation.ValidationContext;
 39  
 import net.sourceforge.pebble.trackback.TrackBackTokenManager;
 40  
 import org.apache.commons.logging.Log;
 41  
 import org.apache.commons.logging.LogFactory;
 42  
 
 43  
 import java.util.*;
 44  
 
 45  
 /**
 46  
  * Represents a blog entry.
 47  
  *
 48  
  * @author Simon Brown
 49  
  */
 50  
 public class BlogEntry extends PageBasedContent {
 51  
 
 52  
   /**
 53  
    * the log used by this class
 54  
    */
 55  4
   private static Log log = LogFactory.getLog(BlogEntry.class);
 56  
 
 57  
   public static final String EXCERPT_PROPERTY = "excerpt";
 58  
   public static final String COMMENTS_ENABLED_PROPERTY = "commentsEnabed";
 59  
   public static final String TRACKBACKS_ENABLED_PROPERTY = "trackBacksEnabled";
 60  
   public static final String ATTACHMENT_PROPERTY = "attachment";
 61  
   public static final String CATEGORIES_PROPERTY = "categories";
 62  
 
 63  
   /** the permalink */
 64  
   private String permalink;
 65  
 
 66  
   /**
 67  
    * the category that the blog entry falls into
 68  
    */
 69  3076
   private Set categories = new HashSet();
 70  
 
 71  
   /**
 72  
    * the excerpt of the blog entry
 73  
    */
 74  3076
   private String excerpt = "";
 75  
 
 76  
   /**
 77  
    * a flag to indicate whether comments are enabled for this entry
 78  
    */
 79  3076
   private boolean commentsEnabled = true;
 80  
 
 81  
   /**
 82  
    * a flag to indicate whether TrackBacks are enabled for this entry
 83  
    */
 84  3076
   private boolean trackBacksEnabled = true;
 85  
 
 86  
   /**
 87  
    * the collection of comments for the blog entry
 88  
    */
 89  3076
   private List comments = new ArrayList();
 90  
 
 91  
   /**
 92  
    * the collection of trackbacks for the blog entry
 93  
    */
 94  3076
   private List trackBacks = new ArrayList();
 95  
 
 96  
   /** the attachment for this blog entry, if applicable */
 97  
   private Attachment attachment;
 98  
 
 99  
   /** the timezone that this entry was posted in */
 100  
   private String timeZoneId;
 101  
 
 102  
   /**
 103  
    * Creates a new blog entry.
 104  
    *
 105  
    * @param blog    the owning Blog
 106  
    */
 107  
   public BlogEntry(Blog blog) {
 108  3076
     super(blog);
 109  3076
     setPublished(false);
 110  3076
   }
 111  
 
 112  
   /**
 113  
    * Sets the title of this blog entry.
 114  
    *
 115  
    * @param newTitle  the title as a String
 116  
    */
 117  
   public void setTitle(String newTitle) {
 118  2520
     super.setTitle(newTitle);
 119  
 
 120  
     // and cause the permalink to be re-generated
 121  2520
     this.permalink = null;
 122  2520
   }
 123  
 
 124  
   /**
 125  
    * Gets the category of this blog entry.
 126  
    *
 127  
    * @return the category as a String
 128  
    */
 129  
   public Set<Category> getCategories() {
 130  3028
     return new HashSet<Category>(categories);
 131  
   }
 132  
 
 133  
   /**
 134  
    * Gets a list of all tags.
 135  
    *
 136  
    * @return  a List of tags
 137  
    */
 138  
   public List<Tag> getAllTags() {
 139  2220
     List<Tag> list = new ArrayList<Tag>();
 140  
 
 141  2220
     if (getCategories().size() > 0) {
 142  256
       Iterator it = getCategories().iterator();
 143  552
       while (it.hasNext()) {
 144  296
         Category category = (Category)it.next();
 145  296
         List tagsForCategory = category.getAllTags();
 146  296
         Collections.reverse(tagsForCategory);
 147  296
         Iterator jt = tagsForCategory.iterator();
 148  400
         while (jt.hasNext()) {
 149  104
           Tag tag = (Tag)jt.next();
 150  104
           if (!list.contains(tag)) {
 151  92
             list.add(tag);
 152  
           }
 153  104
         }
 154  296
       }
 155  256
     } else {
 156  1964
       List tagsForCategory = getBlog().getRootCategory().getAllTags();
 157  1964
       Iterator it = tagsForCategory.iterator();
 158  1968
       while (it.hasNext()) {
 159  4
         Tag tag = (Tag)it.next();
 160  4
         if (!list.contains(tag)) {
 161  4
           list.add(tag);
 162  
         }
 163  4
       }
 164  
     }
 165  
 
 166  2220
     Iterator it = getTagsAsList().iterator();
 167  3294
     while (it.hasNext()) {
 168  1074
       Tag tag = (Tag)it.next();
 169  1074
       if (!list.contains(tag)) {
 170  1074
         list.add(tag);
 171  
       }
 172  1074
     }
 173  
 
 174  2220
     Collections.sort(list);
 175  2220
     return list;
 176  
   }
 177  
 
 178  
   /**
 179  
    * Sets the category of this blog entry.
 180  
    *
 181  
    * @param category the category as a String
 182  
    */
 183  
   public synchronized void addCategory(Category category) {
 184  400
     if (category != null && !categories.contains(category)) {
 185  304
       Set oldCategories = new HashSet(categories);
 186  304
       categories.add(category);
 187  304
       Set newCategories = new HashSet(categories);
 188  304
       propertyChangeSupport.firePropertyChange(CATEGORIES_PROPERTY, oldCategories, newCategories);
 189  
     }
 190  400
   }
 191  
 
 192  
   /**
 193  
    * Removes all categories from this blog entry.
 194  
    */
 195  
   public synchronized void removeAllCategories() {
 196  16
     propertyChangeSupport.firePropertyChange(CATEGORIES_PROPERTY, new HashSet(categories), new HashSet());
 197  16
     categories.clear();
 198  16
   }
 199  
 
 200  
   /**
 201  
    * Sets the categories for this blog entry.
 202  
    *
 203  
    * @param newCategories   a Collection of Category instances
 204  
    */
 205  
   public synchronized void setCategories(Collection newCategories) {
 206  12
     if (newCategories != null) {
 207  12
       Set oldCategories = new HashSet(categories);
 208  12
       categories.clear();
 209  12
       Iterator it = newCategories.iterator();
 210  28
       while (it.hasNext()) {
 211  16
         categories.add(it.next());
 212  
       }
 213  12
       propertyChangeSupport.firePropertyChange(CATEGORIES_PROPERTY, oldCategories, new HashSet(newCategories));
 214  
     }
 215  12
   }
 216  
 
 217  
   /**
 218  
    * Determines whether this blog entry is in the specified category.
 219  
    *
 220  
    * @param category a Category instance
 221  
    * @return true if this entry is in the specified category,
 222  
    *         false otherwise
 223  
    */
 224  
   public boolean inCategory(Category category) {
 225  104
     if (category != null) {
 226  52
       Iterator it = categories.iterator();
 227  68
       while (it.hasNext()) {
 228  60
         Category c = (Category)it.next();
 229  60
         if (c.equals(category) || c.hasParent(category)) {
 230  44
           return true;
 231  
         }
 232  16
       }
 233  
 
 234  8
       return false;
 235  
     } else {
 236  52
       return true;
 237  
     }
 238  
   }
 239  
 
 240  
   /**
 241  
    * Determines whether this blog entry has the specified tag.
 242  
    *
 243  
    * @param s   a String
 244  
    * @return true if this entry has the specified tag,
 245  
    *         false otherwise
 246  
    */
 247  
   public boolean hasTag(String s) {
 248  72
     if (s != null) {
 249  68
       return getAllTags().contains(new Tag(s, getBlog()));
 250  
     } else {
 251  4
       return false;
 252  
     }
 253  
   }
 254  
 
 255  
   /**
 256  
    * Gets the content of this response.
 257  
    *
 258  
    * @return a String
 259  
    */
 260  
   public String getContent() {
 261  152
     if (excerpt != null && excerpt.length() > 0) {
 262  76
       return excerpt;
 263  
     } else {
 264  76
       return getBody();
 265  
     }
 266  
   }
 267  
 
 268  
   /**
 269  
    * Gets the excerpt of this blog entry.
 270  
    *
 271  
    * @return the excerpt as a String
 272  
    */
 273  
   public String getExcerpt() {
 274  2288
     return excerpt;
 275  
   }
 276  
 
 277  
   /**
 278  
    * Sets the excerpt of this blog entry.
 279  
    *
 280  
    * @param newExcerpt    the excerpt as a String
 281  
    */
 282  
   public void setExcerpt(String newExcerpt) {
 283  2336
     if (newExcerpt != null) {
 284  2328
       newExcerpt = newExcerpt.trim();
 285  
     }
 286  2336
     propertyChangeSupport.firePropertyChange(EXCERPT_PROPERTY, excerpt, newExcerpt);
 287  2336
     this.excerpt = newExcerpt;
 288  2336
   }
 289  
 
 290  
   /**
 291  
    * Gets the date that this blog entry was last updated.
 292  
    *
 293  
    * @return  a Date instance representing the time of the last comment/TrackBack
 294  
    */
 295  
   public Date getLastModified() {
 296  16
     Date date = getDate();
 297  
 
 298  16
     Iterator it = comments.iterator();
 299  40
     while (it.hasNext()) {
 300  24
       Comment comment = (Comment)it.next();
 301  24
       if (comment.getDate().after(date)) {
 302  24
         date = comment.getDate();
 303  
       }
 304  24
     }
 305  
 
 306  16
     it = trackBacks.iterator();
 307  28
     while (it.hasNext()) {
 308  12
       TrackBack trackBack = (TrackBack)it.next();
 309  12
       if (trackBack.getDate().after(date)) {
 310  4
         date = trackBack.getDate();
 311  
       }
 312  12
     }
 313  
 
 314  16
     return date;
 315  
   }
 316  
 
 317  
   /**
 318  
    * Sets the date that this blog entry was created.
 319  
    *
 320  
    * @param newDate a java.util.Date instance
 321  
    */
 322  
   public void setDate(Date newDate) {
 323  5408
     super.setDate(newDate);
 324  
 
 325  
     // and cause the permalink to be re-generated
 326  5408
     this.permalink = null;
 327  5408
   }
 328  
 
 329  
   /**
 330  
    * Gets a permalink for this blog entry that is local to the blog. In other
 331  
    * words, it doesn't take into account the original permalink for
 332  
    * aggregated content.
 333  
    *
 334  
    * @return an absolute URL as a String
 335  
    */
 336  
   public String getLocalPermalink() {
 337  408
     if (this.permalink == null) {
 338  308
       String s = getBlog().getPermalinkProvider().getPermalink(this);
 339  308
       if (s != null && s.length() > 0) {
 340  308
         this.permalink = getBlog().getUrl() + s.substring(1);
 341  
       }
 342  
     }
 343  
 
 344  408
     return permalink;
 345  
   }
 346  
 
 347  
   /**
 348  
    * Gets the attachment associated with this blog entry.
 349  
    *
 350  
    * @return  an Attachment instance, or null if one doesn't exist
 351  
    */
 352  
   public Attachment getAttachment() {
 353  224
     return attachment;
 354  
   }
 355  
 
 356  
   /**
 357  
    * Sets the attachment associated with thie blog entry.
 358  
    *
 359  
    * @param newAttachment    an Attachment instance
 360  
    */
 361  
   public void setAttachment(Attachment newAttachment) {
 362  32
     propertyChangeSupport.firePropertyChange(ATTACHMENT_PROPERTY, attachment, newAttachment);
 363  32
     this.attachment = newAttachment;
 364  32
   }
 365  
 
 366  
   /**
 367  
    * Determines whether comments are enabled for this blog entry.
 368  
    *
 369  
    * @return true if comments are enabled, false otherwise
 370  
    */
 371  
   public boolean isCommentsEnabled() {
 372  184
     return this.commentsEnabled;
 373  
   }
 374  
 
 375  
   /**
 376  
    * Sets whether comments are enabled for this blog entry.
 377  
    *
 378  
    * @param newCommentsEnabled true if comments should be enabled,
 379  
    *                        false otherwise
 380  
    */
 381  
   public void setCommentsEnabled(boolean newCommentsEnabled) {
 382  1700
     propertyChangeSupport.firePropertyChange(COMMENTS_ENABLED_PROPERTY, commentsEnabled, newCommentsEnabled);
 383  1700
     this.commentsEnabled = newCommentsEnabled;
 384  1700
   }
 385  
 
 386  
   /**
 387  
    * Gets a link to the comments for this blog entry.
 388  
    *
 389  
    * @return an absolute URL as a String
 390  
    */
 391  
   public String getCommentsLink() {
 392  4
     return getLocalPermalink() + "#comments";
 393  
   }
 394  
 
 395  
   /**
 396  
    * Determines whether TrackBacks are enabled for this blog entry.
 397  
    *
 398  
    * @return true if TrackBacks are enabled, false otherwise
 399  
    */
 400  
   public boolean isTrackBacksEnabled() {
 401  152
     return this.trackBacksEnabled;
 402  
   }
 403  
 
 404  
   /**
 405  
    * Sets whether TrackBacks are enabled for this blog entry.
 406  
    *
 407  
    * @param newTrackBacksEnabled true if TrackBacks should be enabled,
 408  
    *                          false otherwise
 409  
    */
 410  
   public void setTrackBacksEnabled(boolean newTrackBacksEnabled) {
 411  1692
     propertyChangeSupport.firePropertyChange(TRACKBACKS_ENABLED_PROPERTY, trackBacksEnabled, newTrackBacksEnabled);
 412  1692
     this.trackBacksEnabled = newTrackBacksEnabled;
 413  1692
   }
 414  
 
 415  
   /**
 416  
    * Gets a link to the trackbacks for this blog entry.
 417  
    *
 418  
    * @return an absolute URL as a String
 419  
    */
 420  
   public String getTrackBacksLink() {
 421  4
     return getLocalPermalink() + "#trackbacks";
 422  
   }
 423  
 
 424  
   /**
 425  
    * Gets the link that blogs can send TrackBacks too.
 426  
     */
 427  
   public String getTrackBackLink() {
 428  4
     StringBuffer link = new StringBuffer();
 429  4
     link.append(getBlog().getUrl());
 430  4
     link.append("addTrackBack.action?entry=");
 431  4
     link.append(getId());
 432  4
     link.append("&token=");
 433  4
     link.append(TrackBackTokenManager.getInstance().generateToken());
 434  
 
 435  4
     return link.toString();
 436  
   }
 437  
 
 438  
   /**
 439  
    * Gets a list of all comments and TrackBacks.
 440  
    *
 441  
    * @return  a List of all Response instances
 442  
    */
 443  
   public List<Response> getResponses() {
 444  5292
     List<Response> responses = new ArrayList();
 445  5292
     responses.addAll(getComments());
 446  5292
     responses.addAll(getTrackBacks());
 447  5292
     Collections.sort(responses, new ResponseByDateComparator());
 448  5292
     return responses;
 449  
   }
 450  
 
 451  
   /**
 452  
    * Gets a collection of all comments.
 453  
    *
 454  
    * @return a List of Comment instances
 455  
    */
 456  
   public List<Comment> getComments() {
 457  9036
     List<Comment> allComments = new ArrayList();
 458  9036
     Iterator it = comments.iterator();
 459  15862
     while (it.hasNext()) {
 460  6826
       allComments.addAll(getComments((Comment)it.next()));
 461  
     }
 462  
 
 463  9036
     return allComments;
 464  
   }
 465  
 
 466  
   private List<Comment> getComments(Comment comment) {
 467  7004
     List<Comment> allComments = new ArrayList();
 468  7004
     allComments.add(comment);
 469  7004
     Iterator it = comment.getComments().iterator();
 470  7182
     while (it.hasNext()) {
 471  178
       allComments.addAll(getComments((Comment)it.next()));
 472  
     }
 473  
 
 474  7004
     return allComments;
 475  
   }
 476  
 
 477  
   /**
 478  
    * Gets the number of comments that have been left for this blog entry.
 479  
    *
 480  
    * @return the number of comments as a int
 481  
    */
 482  
   public int getNumberOfComments() {
 483  60
     return getComments().size();
 484  
   }
 485  
 
 486  
   /**
 487  
    * Gets a collection of all trackbacks.
 488  
    *
 489  
    * @return a List of TrackBack instances
 490  
    */
 491  
   public List<TrackBack> getTrackBacks() {
 492  7764
     return new ArrayList<TrackBack>(trackBacks);
 493  
   }
 494  
 
 495  
   /**
 496  
    * Gets the number of trackbacks that have been left for this blog entry.
 497  
    *
 498  
    * @return the number of trackbacks as a int
 499  
    */
 500  
   public int getNumberOfTrackBacks() {
 501  12
     return trackBacks.size();
 502  
   }
 503  
 
 504  
   /**
 505  
    * Gets the number of responses that have been left for this blog entry.
 506  
    *
 507  
    * @return the number of responses as a int
 508  
    */
 509  
   public int getNumberOfResponses() {
 510  0
     return getResponses().size();
 511  
   }
 512  
 
 513  
   /**
 514  
    * Creates a new comment for this blog entry. This method doesn't actually
 515  
    * <b>add</b> the comment too.
 516  
    *
 517  
    * @param title     the title of the comment
 518  
    * @param body      the body of the comment
 519  
    * @param author    the author of the comment
 520  
    * @param email     the author's e-mail address
 521  
    * @param website   the author's website
 522  
    * @param ipAddress the IP address of the author
 523  
    * @param date      the date that the comment was created
 524  
    * @param state     the state of the comment
 525  
    * @return a new Comment instance with the specified properties
 526  
    */
 527  
   public Comment createComment(String title, String body, String author, String email, String website, String avatar, String ipAddress, Date date, State state) {
 528  608
     return new Comment(title, body, author, email, website, avatar, ipAddress, date, state, this);
 529  
   }
 530  
 
 531  
   /**
 532  
    * Creates a new comment for this blog entry, with a creation date of now.
 533  
    * This method doesn't actually <b>add</b> the comment too.
 534  
    *
 535  
    * @param title   the title of the comment
 536  
    * @param body    the body of the comment
 537  
    * @param author  the author of the comment
 538  
    * @param email   the author's e-mail address
 539  
    * @param website the author's website
 540  
    * @param ipAddress the IP address of the author
 541  
    * @return a new Comment instance with the specified properties
 542  
    */
 543  
   public Comment createComment(String title, String body, String author, String email, String website, String avatar, String ipAddress) {
 544  500
     Calendar cal = getBlog().getCalendar();
 545  500
     return createComment(title, body, author, email, website, avatar, ipAddress, cal.getTime(), State.APPROVED);
 546  
   }
 547  
 
 548  
   /**
 549  
    * Adds the specified comment.
 550  
    *
 551  
    * @param comment a Comment instance
 552  
    */
 553  
   public synchronized void addComment(Comment comment) {
 554  924
     if (comment == null) {
 555  0
       return;
 556  
     }
 557  
 
 558  924
     Comment existingComment = getComment(comment.getId());
 559  924
     if (existingComment != null && existingComment != comment) {
 560  
       // there is an existing comment with the same ID, but it's
 561  
       // not the same instance
 562  364
       comment.setDate(new Date(comment.getDate().getTime() + 1));
 563  364
       addComment(comment);
 564  560
     } else if (existingComment != null) {
 565  4
       return;
 566  
     } else {
 567  556
       if (comment.getParent() != null) {
 568  68
         Comment parent = getComment(comment.getParent().getId());
 569  68
         if (parent != null) {
 570  68
           parent.addComment(comment);
 571  
         } else {
 572  0
           comments.add(comment);
 573  
         }
 574  68
       } else {
 575  488
         comments.add(comment);
 576  
       }
 577  556
       comment.setBlogEntry(this);
 578  
 
 579  556
       if (areEventsEnabled()) {
 580  212
         addEvent(new CommentEvent(comment, CommentEvent.COMMENT_ADDED));
 581  212
         comment.setEventsEnabled(true);
 582  
       }
 583  
     }
 584  920
   }
 585  
 
 586  
   /**
 587  
    * Creates a new trackback for this blog entry. This method doesn't actually
 588  
    * <b>add</b> the trackback too.
 589  
    *
 590  
    * @param title    the title of the entry
 591  
    * @param excerpt  the excerpt of the entry
 592  
    * @param url      the url (permalink) of the entry
 593  
    * @param blogName the name of the blog
 594  
    * @param ipAddress   the IP address of the author
 595  
    * @param date     the date the trackback was received
 596  
    * @return a new TrackBack instance with the specified properties
 597  
    */
 598  
   public TrackBack createTrackBack(String title, String excerpt, String url, String blogName, String ipAddress, Date date, State state) {
 599  288
     return new TrackBack(title, excerpt, url, blogName, ipAddress, date, state, this);
 600  
   }
 601  
 
 602  
   /**
 603  
    * Creates a new trackback for this blog entry with a date of now.
 604  
    * This method doesn't actually <b>add</b> the trackback too.
 605  
    *
 606  
    * @param title       the title of the entry
 607  
    * @param excerpt     the excerpt of the entry
 608  
    * @param url         the url (permalink) of the entry
 609  
    * @param blogName    the name of the blog
 610  
    * @param ipAddress   the IP address of the author
 611  
    * @return a new Comment instance with the specified properties
 612  
    */
 613  
   public TrackBack createTrackBack(String title, String excerpt, String url, String blogName, String ipAddress) {
 614  264
     Calendar cal = getBlog().getCalendar();
 615  264
     return createTrackBack(title, excerpt, url, blogName, ipAddress, cal.getTime(), State.APPROVED);
 616  
   }
 617  
 
 618  
   /**
 619  
    * Adds the specified trackback.
 620  
    *
 621  
    * @param trackBack a TrackBack instance
 622  
    */
 623  
   public synchronized void addTrackBack(TrackBack trackBack) {
 624  96
     if (trackBack == null || trackBacks.contains(trackBack)) {
 625  0
       return;
 626  
     }
 627  
 
 628  96
     trackBacks.add(trackBack);
 629  
 
 630  96
     if (areEventsEnabled()) {
 631  20
       addEvent(new TrackBackEvent(trackBack, TrackBackEvent.TRACKBACK_ADDED));
 632  20
       trackBack.setEventsEnabled(true);
 633  
     }
 634  96
   }
 635  
 
 636  
   /**
 637  
    * Removes the specified comment.
 638  
    *
 639  
    * @param id    the id of the comment to be removed
 640  
    */
 641  
   public synchronized void removeComment(long id) {
 642  48
     Comment comment = getComment(id);
 643  48
     if (comment != null) {
 644  
 
 645  
       // get all children and delete them
 646  48
       for (Comment child : comment.getComments()) {
 647  12
         comment.removeComment(child);
 648  
       }
 649  
 
 650  48
       if (comment.getParent() != null) {
 651  8
         comment.getParent().removeComment(comment);
 652  
       } else {
 653  40
         comments.remove(comment);
 654  
       }
 655  
 
 656  48
       if (areEventsEnabled()) {
 657  28
         addEvent(new CommentEvent(comment, CommentEvent.COMMENT_REMOVED));
 658  
       }
 659  
     } else {
 660  0
       log.warn("A comment with id=" + id + " could not be found - " +
 661  
         "perhaps it has been removed already.");
 662  
     }
 663  48
   }
 664  
 
 665  
   /**
 666  
    * Gets the specified comment.
 667  
    *
 668  
    * @param id    the id of the comment
 669  
    */
 670  
   public Comment getComment(long id) {
 671  1060
     Iterator it = getComments().iterator();
 672  5352
     while (it.hasNext()) {
 673  4788
       Comment comment = (Comment) it.next();
 674  4788
       if (comment.getId() == id) {
 675  496
         return comment;
 676  
       }
 677  4292
     }
 678  
 
 679  564
     return null;
 680  
   }
 681  
 
 682  
   /**
 683  
    * Gets the specified TrackBack.
 684  
    *
 685  
    * @param id    the id of the TrackBack
 686  
    */
 687  
   public TrackBack getTrackBack(long id) {
 688  24
     Iterator it = getTrackBacks().iterator();
 689  24
     while (it.hasNext()) {
 690  24
       TrackBack trackBack = (TrackBack)it.next();
 691  24
       if (trackBack.getId() == id) {
 692  24
         return trackBack;
 693  
       }
 694  0
     }
 695  
 
 696  0
     return null;
 697  
   }
 698  
 
 699  
   /**
 700  
    * Gets the response specified by the guid.
 701  
    *
 702  
    * @param guid    the response guid
 703  
    * @return  a Response object, or null if no response exists
 704  
    */
 705  
   public Response getResponse(String guid) {
 706  0
     long id = Long.parseLong(guid.substring(guid.lastIndexOf("/")+1));
 707  0
     if (guid.startsWith("c")) {
 708  0
       return getComment(id);
 709  
     } else {
 710  0
       return getTrackBack(id);
 711  
     }
 712  
   }
 713  
 
 714  
   /**
 715  
    * Removes the specified TrackBack.
 716  
    *
 717  
    * @param id    the id of the TrackBack to be removed
 718  
    */
 719  
   public synchronized void removeTrackBack(long id) {
 720  24
     TrackBack trackBack = getTrackBack(id);
 721  24
     if (trackBack != null) {
 722  24
       trackBacks.remove(trackBack);
 723  
 
 724  24
       if (areEventsEnabled()) {
 725  8
         addEvent(new TrackBackEvent(trackBack, TrackBackEvent.TRACKBACK_REMOVED));
 726  
       }
 727  
     } else {
 728  0
       log.warn("A TrackBack with id=" + id + " could not be found - " +
 729  
           "perhaps it has been removed already.");
 730  
     }
 731  24
   }
 732  
 
 733  
   /**
 734  
    * Removes the specified comment or TrackBack.
 735  
    *
 736  
    * @param response    the Response to be removed
 737  
    */
 738  
   public void removeResponse(Response response) {
 739  16
     if (response instanceof Comment) {
 740  8
       removeComment(response.getId());
 741  8
     } else if (response instanceof TrackBack) {
 742  8
       removeTrackBack(response.getId());
 743  
     }
 744  16
   }
 745  
 
 746  
   /**
 747  
    * Returns the blog entry that was posted before this one.
 748  
    *
 749  
    * @return  a BlogEntry instance, or null if this is the first entry
 750  
    */
 751  
   public BlogEntry getPreviousBlogEntry() {
 752  12
     return getBlog().getPreviousBlogEntry(this);
 753  
   }
 754  
 
 755  
   /**
 756  
    * Returns the blog entry that was posted after this one.
 757  
    *
 758  
    * @return  a BlogEntry instance, or null is this is the last entry
 759  
    */
 760  
   public BlogEntry getNextBlogEntry() {
 761  12
     return getBlog().getNextBlogEntry(this);
 762  
   }
 763  
 
 764  
   public void validate(ValidationContext context) {
 765  0
   }
 766  
 
 767  
   /**
 768  
    * Indicates whether some other object is "equal to" this one.
 769  
    *
 770  
    * @param o   the reference object with which to compare.
 771  
    * @return <code>true</code> if this object is the same as the obj
 772  
    *         argument; <code>false</code> otherwise.
 773  
    * @see #hashCode()
 774  
    * @see java.util.Hashtable
 775  
    */
 776  
   public boolean equals(Object o) {
 777  168
     if (this == o) {
 778  80
       return true;
 779  
     }
 780  
 
 781  88
     if (!(o instanceof BlogEntry)) {
 782  0
       return false;
 783  
     }
 784  
 
 785  88
     BlogEntry blogEntry = (BlogEntry)o;
 786  88
     return getGuid().equals(blogEntry.getGuid());
 787  
   }
 788  
 
 789  
   public String getGuid() {
 790  896
     return "blogEntry/" + getBlog().getId() + "/" + getId();
 791  
   }
 792  
 
 793  
   public int hashCode() {
 794  32
     return getGuid().hashCode();
 795  
   }
 796  
 
 797  
   /**
 798  
    * Creates and returns a copy of this object.
 799  
    *
 800  
    * @return a clone of this instance.
 801  
    * @see Cloneable
 802  
    */
 803  
   public Object clone() {
 804  1520
     BlogEntry entry = new BlogEntry(getBlog());
 805  1520
     entry.setEventsEnabled(false);
 806  1520
     entry.setPersistent(isPersistent());
 807  1520
     entry.setPublished(isPublished());
 808  1520
     entry.setTitle(getTitle());
 809  1520
     entry.setSubtitle(getSubtitle());
 810  1520
     entry.setExcerpt(getExcerpt());
 811  1520
     entry.setBody(getBody());
 812  1520
     entry.setDate(getDate());
 813  1520
     entry.setTimeZoneId(timeZoneId);
 814  1520
     entry.setState(getState());
 815  1520
     entry.setAuthor(getAuthor());
 816  1520
     entry.setOriginalPermalink(getOriginalPermalink());
 817  1520
     entry.setCommentsEnabled(commentsEnabled);
 818  1520
     entry.setTrackBacksEnabled(trackBacksEnabled);
 819  
 
 820  1520
     if (attachment != null) {
 821  4
       entry.setAttachment((Attachment)attachment.clone());
 822  
     }
 823  
 
 824  
     // copy the categories
 825  1520
     Iterator it = categories.iterator();
 826  1620
     while (it.hasNext()) {
 827  100
       entry.addCategory((Category)it.next());
 828  
     }
 829  
 
 830  1520
     entry.setTags(getTags());
 831  
 
 832  
     // also copy the comments
 833  1520
     it = getComments().iterator();
 834  1760
     while (it.hasNext()) {
 835  240
       Comment comment = (Comment)it.next();
 836  240
       Comment clonedComment = (Comment)comment.clone();
 837  240
       entry.addComment(clonedComment);
 838  240
     }
 839  
 
 840  
     // and TrackBacks
 841  1520
     it = getTrackBacks().iterator();
 842  1532
     while (it.hasNext()) {
 843  12
       TrackBack trackBack = (TrackBack)it.next();
 844  12
       TrackBack clonedTrackBack = (TrackBack)trackBack.clone();
 845  12
       clonedTrackBack.setBlogEntry(entry);
 846  12
       entry.addTrackBack(clonedTrackBack);
 847  12
     }
 848  
 
 849  1520
     return entry;
 850  
   }
 851  
 
 852  
   /**
 853  
    * Sets whether events are enabled.
 854  
    *
 855  
    * @param b   true to enable events, false otherwise
 856  
    */
 857  
   void setEventsEnabled(boolean b) {
 858  4224
     super.setEventsEnabled(b);
 859  
 
 860  
     // and cascade
 861  4224
     for (Response response : getResponses()) {
 862  524
       response.setEventsEnabled(b);
 863  
     }
 864  4224
   }
 865  
 
 866  
   public void clearEvents() {
 867  1032
     super.clearEvents();
 868  
 
 869  1032
     for (Response response : getResponses()) {
 870  300
       response.clearEvents();
 871  
     }
 872  1032
   }
 873  
 
 874  
   /**
 875  
    * Sets the state of this blog entry.
 876  
    */
 877  
   void setState(State state) {
 878  6532
     State previousState = getState();
 879  6532
     super.setState(state);
 880  
 
 881  6532
     if (areEventsEnabled()) {
 882  36
       if (isPublished() && previousState == State.UNPUBLISHED) {
 883  28
         addEvent(new BlogEntryEvent(this, BlogEntryEvent.BLOG_ENTRY_PUBLISHED));
 884  8
       } else if (isUnpublished() && previousState == State.PUBLISHED) {
 885  8
         addEvent(new BlogEntryEvent(this, BlogEntryEvent.BLOG_ENTRY_UNPUBLISHED));
 886  
       }
 887  
     }
 888  6532
   }
 889  
 
 890  
   public String toString() {
 891  0
     return getGuid() + ":" + super.hashCode();
 892  
   }
 893  
 
 894  
   public TimeZone getTimeZone() {
 895  0
     return TimeZone.getTimeZone(getTimeZoneId());
 896  
   }
 897  
 
 898  
   public String getTimeZoneId() {
 899  144
     if (this.timeZoneId != null) {
 900  12
       return timeZoneId;
 901  
     } else {
 902  132
       return getBlog().getTimeZoneId();
 903  
     }
 904  
   }
 905  
 
 906  
   public void setTimeZoneId(String timeZoneId) {
 907  1632
     this.timeZoneId = timeZoneId;
 908  1632
   }
 909  
 
 910  
 }