Package net.sourceforge.pebble.domain

Source Code of net.sourceforge.pebble.domain.BlogEntry

/*
* Copyright (c) 2003-2011, Simon Brown
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.
*
*   - Neither the name of Pebble nor the names of its contributors may
*     be used to endorse or promote products derived from this software
*     without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.pebble.domain;

import net.sourceforge.pebble.api.event.blogentry.BlogEntryEvent;
import net.sourceforge.pebble.api.event.comment.CommentEvent;
import net.sourceforge.pebble.api.event.trackback.TrackBackEvent;
import net.sourceforge.pebble.comparator.ResponseByDateComparator;
import net.sourceforge.pebble.web.validation.ValidationContext;
import net.sourceforge.pebble.trackback.TrackBackTokenManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

/**
* Represents a blog entry.
*
* @author Simon Brown
*/
public class BlogEntry extends PageBasedContent {

  /**
   * the log used by this class
   */
  private static Log log = LogFactory.getLog(BlogEntry.class);

  public static final String EXCERPT_PROPERTY = "excerpt";
  public static final String COMMENTS_ENABLED_PROPERTY = "commentsEnabed";
  public static final String TRACKBACKS_ENABLED_PROPERTY = "trackBacksEnabled";
  public static final String ATTACHMENT_PROPERTY = "attachment";
  public static final String CATEGORIES_PROPERTY = "categories";

  /** the permalink */
  private String permalink;

  /**
   * the category that the blog entry falls into
   */
  private Set categories = new HashSet();

  /**
   * the excerpt of the blog entry
   */
  private String excerpt = "";

  /**
   * a flag to indicate whether comments are enabled for this entry
   */
  private boolean commentsEnabled = true;

  /**
   * a flag to indicate whether TrackBacks are enabled for this entry
   */
  private boolean trackBacksEnabled = true;

  /**
   * the collection of comments for the blog entry
   */
  private List comments = new ArrayList();

  /**
   * the collection of trackbacks for the blog entry
   */
  private List trackBacks = new ArrayList();

  /** the attachment for this blog entry, if applicable */
  private Attachment attachment;

  /** the timezone that this entry was posted in */
  private String timeZoneId;

  /**
   * Creates a new blog entry.
   *
   * @param blog    the owning Blog
   */
  public BlogEntry(Blog blog) {
    super(blog);
    setPublished(false);
  }

  /**
   * Sets the title of this blog entry.
   *
   * @param newTitle  the title as a String
   */
  public void setTitle(String newTitle) {
    super.setTitle(newTitle);

    // and cause the permalink to be re-generated
    this.permalink = null;
  }

  /**
   * Gets the category of this blog entry.
   *
   * @return the category as a String
   */
  public Set<Category> getCategories() {
    return new HashSet<Category>(categories);
  }

  /**
   * Gets a list of all tags.
   *
   * @return  a List of tags
   */
  public List<Tag> getAllTags() {
    List<Tag> list = new ArrayList<Tag>();

    if (getCategories().size() > 0) {
      Iterator it = getCategories().iterator();
      while (it.hasNext()) {
        Category category = (Category)it.next();
        List tagsForCategory = category.getAllTags();
        Collections.reverse(tagsForCategory);
        Iterator jt = tagsForCategory.iterator();
        while (jt.hasNext()) {
          Tag tag = (Tag)jt.next();
          if (!list.contains(tag)) {
            list.add(tag);
          }
        }
      }
    } else {
      List tagsForCategory = getBlog().getRootCategory().getAllTags();
      Iterator it = tagsForCategory.iterator();
      while (it.hasNext()) {
        Tag tag = (Tag)it.next();
        if (!list.contains(tag)) {
          list.add(tag);
        }
      }
    }

    Iterator it = getTagsAsList().iterator();
    while (it.hasNext()) {
      Tag tag = (Tag)it.next();
      if (!list.contains(tag)) {
        list.add(tag);
      }
    }

    Collections.sort(list);
    return list;
  }

  /**
   * Sets the category of this blog entry.
   *
   * @param category the category as a String
   */
  public synchronized void addCategory(Category category) {
    if (category != null && !categories.contains(category)) {
      Set oldCategories = new HashSet(categories);
      categories.add(category);
      Set newCategories = new HashSet(categories);
      propertyChangeSupport.firePropertyChange(CATEGORIES_PROPERTY, oldCategories, newCategories);
    }
  }

  /**
   * Removes all categories from this blog entry.
   */
  public synchronized void removeAllCategories() {
    propertyChangeSupport.firePropertyChange(CATEGORIES_PROPERTY, new HashSet(categories), new HashSet());
    categories.clear();
  }

  /**
   * Sets the categories for this blog entry.
   *
   * @param newCategories   a Collection of Category instances
   */
  public synchronized void setCategories(Collection newCategories) {
    if (newCategories != null) {
      Set oldCategories = new HashSet(categories);
      categories.clear();
      Iterator it = newCategories.iterator();
      while (it.hasNext()) {
        categories.add(it.next());
      }
      propertyChangeSupport.firePropertyChange(CATEGORIES_PROPERTY, oldCategories, new HashSet(newCategories));
    }
  }

  /**
   * Determines whether this blog entry is in the specified category.
   *
   * @param category a Category instance
   * @return true if this entry is in the specified category,
   *         false otherwise
   */
  public boolean inCategory(Category category) {
    if (category != null) {
      Iterator it = categories.iterator();
      while (it.hasNext()) {
        Category c = (Category)it.next();
        if (c.equals(category) || c.hasParent(category)) {
          return true;
        }
      }

      return false;
    } else {
      return true;
    }
  }

  /**
   * Determines whether this blog entry has the specified tag.
   *
   * @param s   a String
   * @return true if this entry has the specified tag,
   *         false otherwise
   */
  public boolean hasTag(String s) {
    if (s != null) {
      return getAllTags().contains(new Tag(s, getBlog()));
    } else {
      return false;
    }
  }

  /**
   * Gets the content of this response.
   *
   * @return a String
   */
  public String getContent() {
    if (excerpt != null && excerpt.length() > 0) {
      return excerpt;
    } else {
      return getBody();
    }
  }

  /**
   * Gets the excerpt of this blog entry.
   *
   * @return the excerpt as a String
   */
  public String getExcerpt() {
    return excerpt;
  }

  /**
   * Sets the excerpt of this blog entry.
   *
   * @param newExcerpt    the excerpt as a String
   */
  public void setExcerpt(String newExcerpt) {
    if (newExcerpt != null) {
      newExcerpt = newExcerpt.trim();
    }
    propertyChangeSupport.firePropertyChange(EXCERPT_PROPERTY, excerpt, newExcerpt);
    this.excerpt = newExcerpt;
  }

  /**
   * Gets the date that this blog entry was last updated.
   *
   * @return  a Date instance representing the time of the last comment/TrackBack
   */
  public Date getLastModified() {
    Date date = getDate();

    Iterator it = comments.iterator();
    while (it.hasNext()) {
      Comment comment = (Comment)it.next();
      if (comment.getDate().after(date)) {
        date = comment.getDate();
      }
    }

    it = trackBacks.iterator();
    while (it.hasNext()) {
      TrackBack trackBack = (TrackBack)it.next();
      if (trackBack.getDate().after(date)) {
        date = trackBack.getDate();
      }
    }

    return date;
  }

  /**
   * Sets the date that this blog entry was created.
   *
   * @param newDate a java.util.Date instance
   */
  public void setDate(Date newDate) {
    super.setDate(newDate);

    // and cause the permalink to be re-generated
    this.permalink = null;
  }

  /**
   * Gets a permalink for this blog entry that is local to the blog. In other
   * words, it doesn't take into account the original permalink for
   * aggregated content.
   *
   * @return an absolute URL as a String
   */
  public String getLocalPermalink() {
    if (this.permalink == null) {
      String s = getBlog().getPermalinkProvider().getPermalink(this);
      if (s != null && s.length() > 0) {
        this.permalink = getBlog().getUrl() + s.substring(1);
      }
    }

    return permalink;
  }

  /**
   * Gets the attachment associated with this blog entry.
   *
   * @return  an Attachment instance, or null if one doesn't exist
   */
  public Attachment getAttachment() {
    return attachment;
  }

  /**
   * Sets the attachment associated with thie blog entry.
   *
   * @param newAttachment    an Attachment instance
   */
  public void setAttachment(Attachment newAttachment) {
    propertyChangeSupport.firePropertyChange(ATTACHMENT_PROPERTY, attachment, newAttachment);
    this.attachment = newAttachment;
  }

  /**
   * Determines whether comments are enabled for this blog entry.
   *
   * @return true if comments are enabled, false otherwise
   */
  public boolean isCommentsEnabled() {
    return this.commentsEnabled;
  }

  /**
   * Sets whether comments are enabled for this blog entry.
   *
   * @param newCommentsEnabled true if comments should be enabled,
   *                        false otherwise
   */
  public void setCommentsEnabled(boolean newCommentsEnabled) {
    propertyChangeSupport.firePropertyChange(COMMENTS_ENABLED_PROPERTY, commentsEnabled, newCommentsEnabled);
    this.commentsEnabled = newCommentsEnabled;
  }

  /**
   * Gets a link to the comments for this blog entry.
   *
   * @return an absolute URL as a String
   */
  public String getCommentsLink() {
    return getLocalPermalink() + "#comments";
  }

  /**
   * Determines whether TrackBacks are enabled for this blog entry.
   *
   * @return true if TrackBacks are enabled, false otherwise
   */
  public boolean isTrackBacksEnabled() {
    return this.trackBacksEnabled;
  }

  /**
   * Sets whether TrackBacks are enabled for this blog entry.
   *
   * @param newTrackBacksEnabled true if TrackBacks should be enabled,
   *                          false otherwise
   */
  public void setTrackBacksEnabled(boolean newTrackBacksEnabled) {
    propertyChangeSupport.firePropertyChange(TRACKBACKS_ENABLED_PROPERTY, trackBacksEnabled, newTrackBacksEnabled);
    this.trackBacksEnabled = newTrackBacksEnabled;
  }

  /**
   * Gets a link to the trackbacks for this blog entry.
   *
   * @return an absolute URL as a String
   */
  public String getTrackBacksLink() {
    return getLocalPermalink() + "#trackbacks";
  }

  /**
   * Gets the link that blogs can send TrackBacks too.
    */
  public String getTrackBackLink() {
    StringBuffer link = new StringBuffer();
    link.append(getBlog().getUrl());
    link.append("addTrackBack.action?entry=");
    link.append(getId());
    link.append("&token=");
    link.append(TrackBackTokenManager.getInstance().generateToken());

    return link.toString();
  }

  /**
   * Gets a list of all comments and TrackBacks.
   *
   * @return  a List of all Response instances
   */
  public List<Response> getResponses() {
    List<Response> responses = new ArrayList();
    responses.addAll(getComments());
    responses.addAll(getTrackBacks());
    Collections.sort(responses, new ResponseByDateComparator());
    return responses;
  }

  /**
   * Gets a collection of all comments.
   *
   * @return a List of Comment instances
   */
  public List<Comment> getComments() {
    List<Comment> allComments = new ArrayList();
    Iterator it = comments.iterator();
    while (it.hasNext()) {
      allComments.addAll(getComments((Comment)it.next()));
    }

    return allComments;
  }

  private List<Comment> getComments(Comment comment) {
    List<Comment> allComments = new ArrayList();
    allComments.add(comment);
    Iterator it = comment.getComments().iterator();
    while (it.hasNext()) {
      allComments.addAll(getComments((Comment)it.next()));
    }

    return allComments;
  }

  /**
   * Gets the number of comments that have been left for this blog entry.
   *
   * @return the number of comments as a int
   */
  public int getNumberOfComments() {
    return getComments().size();
  }

  /**
   * Gets a collection of all trackbacks.
   *
   * @return a List of TrackBack instances
   */
  public List<TrackBack> getTrackBacks() {
    return new ArrayList<TrackBack>(trackBacks);
  }

  /**
   * Gets the number of trackbacks that have been left for this blog entry.
   *
   * @return the number of trackbacks as a int
   */
  public int getNumberOfTrackBacks() {
    return trackBacks.size();
  }

  /**
   * Gets the number of responses that have been left for this blog entry.
   *
   * @return the number of responses as a int
   */
  public int getNumberOfResponses() {
    return getResponses().size();
  }

  /**
   * Creates a new comment for this blog entry. This method doesn't actually
   * <b>add</b> the comment too.
   *
   * @param title     the title of the comment
   * @param body      the body of the comment
   * @param author    the author of the comment
   * @param email     the author's e-mail address
   * @param website   the author's website
   * @param ipAddress the IP address of the author
   * @param date      the date that the comment was created
   * @param state     the state of the comment
   * @return a new Comment instance with the specified properties
   */
  public Comment createComment(String title, String body, String author, String email, String website, String avatar, String ipAddress, Date date, State state) {
    return new Comment(title, body, author, email, website, avatar, ipAddress, date, state, this);
  }

  /**
   * Creates a new comment for this blog entry, with a creation date of now.
   * This method doesn't actually <b>add</b> the comment too.
   *
   * @param title   the title of the comment
   * @param body    the body of the comment
   * @param author  the author of the comment
   * @param email   the author's e-mail address
   * @param website the author's website
   * @param ipAddress the IP address of the author
   * @return a new Comment instance with the specified properties
   */
  public Comment createComment(String title, String body, String author, String email, String website, String avatar, String ipAddress) {
    Calendar cal = getBlog().getCalendar();
    return createComment(title, body, author, email, website, avatar, ipAddress, cal.getTime(), State.APPROVED);
  }

  /**
   * Adds the specified comment.
   *
   * @param comment a Comment instance
   */
  public synchronized void addComment(Comment comment) {
    if (comment == null) {
      return;
    }

    Comment existingComment = getComment(comment.getId());
    if (existingComment != null && existingComment != comment) {
      // there is an existing comment with the same ID, but it's
      // not the same instance
      comment.setDate(new Date(comment.getDate().getTime() + 1));
      addComment(comment);
    } else if (existingComment != null) {
      return;
    } else {
      if (comment.getParent() != null) {
        Comment parent = getComment(comment.getParent().getId());
        if (parent != null) {
          parent.addComment(comment);
        } else {
          comments.add(comment);
        }
      } else {
        comments.add(comment);
      }
      comment.setBlogEntry(this);

      if (areEventsEnabled()) {
        addEvent(new CommentEvent(comment, CommentEvent.COMMENT_ADDED));
        comment.setEventsEnabled(true);
      }
    }
  }

  /**
   * Creates a new trackback for this blog entry. This method doesn't actually
   * <b>add</b> the trackback too.
   *
   * @param title    the title of the entry
   * @param excerpt  the excerpt of the entry
   * @param url      the url (permalink) of the entry
   * @param blogName the name of the blog
   * @param ipAddress   the IP address of the author
   * @param date     the date the trackback was received
   * @return a new TrackBack instance with the specified properties
   */
  public TrackBack createTrackBack(String title, String excerpt, String url, String blogName, String ipAddress, Date date, State state) {
    return new TrackBack(title, excerpt, url, blogName, ipAddress, date, state, this);
  }

  /**
   * Creates a new trackback for this blog entry with a date of now.
   * This method doesn't actually <b>add</b> the trackback too.
   *
   * @param title       the title of the entry
   * @param excerpt     the excerpt of the entry
   * @param url         the url (permalink) of the entry
   * @param blogName    the name of the blog
   * @param ipAddress   the IP address of the author
   * @return a new Comment instance with the specified properties
   */
  public TrackBack createTrackBack(String title, String excerpt, String url, String blogName, String ipAddress) {
    Calendar cal = getBlog().getCalendar();
    return createTrackBack(title, excerpt, url, blogName, ipAddress, cal.getTime(), State.APPROVED);
  }

  /**
   * Adds the specified trackback.
   *
   * @param trackBack a TrackBack instance
   */
  public synchronized void addTrackBack(TrackBack trackBack) {
    if (trackBack == null || trackBacks.contains(trackBack)) {
      return;
    }

    trackBacks.add(trackBack);

    if (areEventsEnabled()) {
      addEvent(new TrackBackEvent(trackBack, TrackBackEvent.TRACKBACK_ADDED));
      trackBack.setEventsEnabled(true);
    }
  }

  /**
   * Removes the specified comment.
   *
   * @param id    the id of the comment to be removed
   */
  public synchronized void removeComment(long id) {
    Comment comment = getComment(id);
    if (comment != null) {

      // get all children and delete them
      for (Comment child : comment.getComments()) {
        comment.removeComment(child);
      }

      if (comment.getParent() != null) {
        comment.getParent().removeComment(comment);
      } else {
        comments.remove(comment);
      }

      if (areEventsEnabled()) {
        addEvent(new CommentEvent(comment, CommentEvent.COMMENT_REMOVED));
      }
    } else {
      log.warn("A comment with id=" + id + " could not be found - " +
        "perhaps it has been removed already.");
    }
  }

  /**
   * Gets the specified comment.
   *
   * @param id    the id of the comment
   */
  public Comment getComment(long id) {
    Iterator it = getComments().iterator();
    while (it.hasNext()) {
      Comment comment = (Comment) it.next();
      if (comment.getId() == id) {
        return comment;
      }
    }

    return null;
  }

  /**
   * Gets the specified TrackBack.
   *
   * @param id    the id of the TrackBack
   */
  public TrackBack getTrackBack(long id) {
    Iterator it = getTrackBacks().iterator();
    while (it.hasNext()) {
      TrackBack trackBack = (TrackBack)it.next();
      if (trackBack.getId() == id) {
        return trackBack;
      }
    }

    return null;
  }

  /**
   * Gets the response specified by the guid.
   *
   * @param guid    the response guid
   * @return  a Response object, or null if no response exists
   */
  public Response getResponse(String guid) {
    long id = Long.parseLong(guid.substring(guid.lastIndexOf("/")+1));
    if (guid.startsWith("c")) {
      return getComment(id);
    } else {
      return getTrackBack(id);
    }
  }

  /**
   * Removes the specified TrackBack.
   *
   * @param id    the id of the TrackBack to be removed
   */
  public synchronized void removeTrackBack(long id) {
    TrackBack trackBack = getTrackBack(id);
    if (trackBack != null) {
      trackBacks.remove(trackBack);

      if (areEventsEnabled()) {
        addEvent(new TrackBackEvent(trackBack, TrackBackEvent.TRACKBACK_REMOVED));
      }
    } else {
      log.warn("A TrackBack with id=" + id + " could not be found - " +
          "perhaps it has been removed already.");
    }
  }

  /**
   * Removes the specified comment or TrackBack.
   *
   * @param response    the Response to be removed
   */
  public void removeResponse(Response response) {
    if (response instanceof Comment) {
      removeComment(response.getId());
    } else if (response instanceof TrackBack) {
      removeTrackBack(response.getId());
    }
  }

  /**
   * Returns the blog entry that was posted before this one.
   *
   * @return  a BlogEntry instance, or null if this is the first entry
   */
  public BlogEntry getPreviousBlogEntry() {
    return getBlog().getPreviousBlogEntry(this);
  }

  /**
   * Returns the blog entry that was posted after this one.
   *
   * @return  a BlogEntry instance, or null is this is the last entry
   */
  public BlogEntry getNextBlogEntry() {
    return getBlog().getNextBlogEntry(this);
  }

  public void validate(ValidationContext context) {
  }

  /**
   * Indicates whether some other object is "equal to" this one.
   *
   * @param o   the reference object with which to compare.
   * @return <code>true</code> if this object is the same as the obj
   *         argument; <code>false</code> otherwise.
   * @see #hashCode()
   * @see java.util.Hashtable
   */
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (!(o instanceof BlogEntry)) {
      return false;
    }

    BlogEntry blogEntry = (BlogEntry)o;
    return getGuid().equals(blogEntry.getGuid());
  }

  public String getGuid() {
    return "blogEntry/" + getBlog().getId() + "/" + getId();
  }

  public int hashCode() {
    return getGuid().hashCode();
  }

  /**
   * Creates and returns a copy of this object.
   *
   * @return a clone of this instance.
   * @see Cloneable
   */
  public Object clone() {
    BlogEntry entry = new BlogEntry(getBlog());
    entry.setEventsEnabled(false);
    entry.setPersistent(isPersistent());
    entry.setPublished(isPublished());
    entry.setTitle(getTitle());
    entry.setSubtitle(getSubtitle());
    entry.setExcerpt(getExcerpt());
    entry.setBody(getBody());
    entry.setDate(getDate());
    entry.setTimeZoneId(timeZoneId);
    entry.setState(getState());
    entry.setAuthor(getAuthor());
    entry.setOriginalPermalink(getOriginalPermalink());
    entry.setCommentsEnabled(commentsEnabled);
    entry.setTrackBacksEnabled(trackBacksEnabled);

    if (attachment != null) {
      entry.setAttachment((Attachment)attachment.clone());
    }

    // copy the categories
    Iterator it = categories.iterator();
    while (it.hasNext()) {
      entry.addCategory((Category)it.next());
    }

    entry.setTags(getTags());

    // also copy the comments
    it = getComments().iterator();
    while (it.hasNext()) {
      Comment comment = (Comment)it.next();
      Comment clonedComment = (Comment)comment.clone();
      entry.addComment(clonedComment);
    }

    // and TrackBacks
    it = getTrackBacks().iterator();
    while (it.hasNext()) {
      TrackBack trackBack = (TrackBack)it.next();
      TrackBack clonedTrackBack = (TrackBack)trackBack.clone();
      clonedTrackBack.setBlogEntry(entry);
      entry.addTrackBack(clonedTrackBack);
    }

    return entry;
  }

  /**
   * Sets whether events are enabled.
   *
   * @param b   true to enable events, false otherwise
   */
  void setEventsEnabled(boolean b) {
    super.setEventsEnabled(b);

    // and cascade
    for (Response response : getResponses()) {
      response.setEventsEnabled(b);
    }
  }

  public void clearEvents() {
    super.clearEvents();

    for (Response response : getResponses()) {
      response.clearEvents();
    }
  }

  /**
   * Sets the state of this blog entry.
   */
  void setState(State state) {
    State previousState = getState();
    super.setState(state);

    if (areEventsEnabled()) {
      if (isPublished() && previousState == State.UNPUBLISHED) {
        addEvent(new BlogEntryEvent(this, BlogEntryEvent.BLOG_ENTRY_PUBLISHED));
      } else if (isUnpublished() && previousState == State.PUBLISHED) {
        addEvent(new BlogEntryEvent(this, BlogEntryEvent.BLOG_ENTRY_UNPUBLISHED));
      }
    }
  }

  public String toString() {
    return getGuid() + ":" + super.hashCode();
  }

  public TimeZone getTimeZone() {
    return TimeZone.getTimeZone(getTimeZoneId());
  }

  public String getTimeZoneId() {
    if (this.timeZoneId != null) {
      return timeZoneId;
    } else {
      return getBlog().getTimeZoneId();
    }
  }

  public void setTimeZoneId(String timeZoneId) {
    this.timeZoneId = timeZoneId;
  }

}
TOP

Related Classes of net.sourceforge.pebble.domain.BlogEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.