Package org.rssowl.core.model.internal.types

Source Code of org.rssowl.core.model.internal.types.News

/*   **********************************************************************  **
**   Copyright notice                                                       **
**                                                                          **
**   (c) 2005-2006 RSSOwl Development Team                                  **
**   http://www.rssowl.org/                                                 **
**                                                                          **
**   All rights reserved                                                    **
**                                                                          **
**   This program and the accompanying materials are made available under   **
**   the terms of the Eclipse Public License v1.0 which accompanies this    **
**   distribution, and is available at:                                     **
**   http://www.rssowl.org/legal/epl-v10.html                               **
**                                                                          **
**   A copy is found in the file epl-v10.html and important notices to the  **
**   license from the team is found in the textfile LICENSE.txt distributed **
**   in this package.                                                       **
**                                                                          **
**   This copyright notice MUST APPEAR in all copies of the file!           **
**                                                                          **
**   Contributors:                                                          **
**     RSSOwl Development Team - initial API and implementation             **
**                                                                          **
**  **********************************************************************  */

package org.rssowl.core.model.internal.types;

import org.eclipse.core.runtime.Assert;
import org.rssowl.core.internal.persist.ComplexMergeResult;
import org.rssowl.core.internal.persist.MergeResult;
import org.rssowl.core.model.types.IAttachment;
import org.rssowl.core.model.types.ICategory;
import org.rssowl.core.model.types.IFeed;
import org.rssowl.core.model.types.ILabel;
import org.rssowl.core.model.types.INews;
import org.rssowl.core.model.types.IPerson;
import org.rssowl.core.model.types.ISource;
import org.rssowl.core.persist.IGuid;
import org.rssowl.core.persist.reference.FeedLinkReference;
import org.rssowl.core.util.MergeUtils;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

/**
* A News is a single entry inside a Feed. The attributes IsRead, IsNew and
* IsDeleted describe the lifecycle of a News:
* <ul>
* <li>IsRead: The News has been marked read</li>
* <li>IsNew: The News has not been read and was not yet looked at</li>
* <li>IsDeleted: The News has been deleted by the user or system</li>
* </ul>
*
* @author bpasero
*/
public class News extends AbstractEntity implements INews {

  private String fTitle;

  private transient URI fLink;
  private String fLinkText;

  private String fBaseUri;

  private Date fReceiveDate;
  private Date fPublishDate;
  private Date fModifiedDate;
  private String fDescription;
  private String fComments;
  private String fInReplyTo;
  private boolean fIsFlagged;
  private int fRating;

  private int fStateOrdinal = INews.State.NEW.ordinal();
  private transient State fState;

  private String fGuidValue;
  private boolean fGuidIsPermaLink;
  private transient IGuid fGuid;

  private ISource fSource;

  private ILabel fLabel;

  private String fFeedLink;
  private transient FeedLinkReference fFeedLinkReference;

  private IPerson fAuthor;

  private List<IAttachment> fAttachments;
  private List<ICategory> fCategories;



  /**
   * Constructor used by <code>DefaultModelFactory</code>
   *
   * @param feed The Feed this News is belonging to.
   */
  public News(IFeed feed) {
    super(null);
    Assert.isNotNull(feed, "The type News requires a Feed that is not NULL"); //$NON-NLS-1$
    fFeedLink = feed.getLink().toString();
    fReceiveDate = new Date();
  }

  /**
   * Creates a new Element of the Type News
   * <p>
   * TODO Consider whether feed can be null
   * </p>
   *
   * @param id The unique id of the News.
   * @param feed The Feed this News belongs to.
   * @param receiveDate The Date this News was received.
   */
  public News(Long id, IFeed feed, Date receiveDate) {
    super(id);
    Assert.isNotNull(feed, "The type News requires a Feed that is not NULL"); //$NON-NLS-1$
    fFeedLink = feed.getLink().toString();
    Assert.isNotNull(receiveDate, "The type News requires a ReceiveDate that is not NULL"); //$NON-NLS-1$
    fReceiveDate = receiveDate;
  }

  /**
   * Default constructor for deserialization
   */
  protected News() {
  // As per javadoc
  }

  private <T>Boolean isEquivalentCompare(T o1, T o2) {
    if ((o1 == null) && (o2 == null))
      return null;

    return Boolean.valueOf(equals(o1, o2));
  }

  private boolean equals(Object o1, Object o2) {
    return o1 == null ? o2 == null : o1.equals(o2);
  }

  public synchronized boolean isEquivalent(INews other) {
    Assert.isNotNull(other, "other cannot be null"); //$NON-NLS-1$
    String guidValue = (getGuid() == null ? null : getGuid().getValue());

    String otherGuidValue = (other.getGuid() == null ? null :
      other.getGuid().getValue());

    Boolean guidMatch = isEquivalentCompare(guidValue, otherGuidValue);
    if (guidMatch != null) {
      if (guidMatch.equals(Boolean.TRUE))
        return true;

      return false;
    }

    URI newsItemLink = other.getLink();
    Boolean linkMatch = isEquivalentCompare(getLink(), newsItemLink);
    if (linkMatch != null) {
      if (linkMatch.equals(Boolean.TRUE))
        return true;

      return false;
    }
    if (!getFeedReference().equals(other.getFeedReference()))
      return false;

    Boolean titleMatch = isEquivalentCompare(getTitle(), other.getTitle());
    if (titleMatch != null && titleMatch.equals(Boolean.TRUE))
      return true;

    return false;
  }

  /*
   * @see org.rssowl.core.model.types.INews#addAttachment(org.rssowl.core.model.types.IAttachment)
   */
  public synchronized void addAttachment(IAttachment attachment) {
    if (fAttachments == null)
      fAttachments = new ArrayList<IAttachment>();

    Assert.isNotNull(attachment, "Exception adding NULL as Attachment into News"); //$NON-NLS-1$

    /* Rule: Child needs to know about its new parent already! */
    Assert.isTrue(equals(attachment.getNews()), "The Attachment has a different News set!"); //$NON-NLS-1$
    fAttachments.add(attachment);
  }

  /*
   * @see org.rssowl.core.model.types.INews#getAttachments()
   */
  public synchronized List<IAttachment> getAttachments() {
    if (fAttachments == null)
      return Collections.emptyList();

    return Collections.unmodifiableList(fAttachments);
  }

  /*
   * @see org.rssowl.core.model.types.INews#getAuthor()
   */
  public synchronized IPerson getAuthor() {
    return fAuthor;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setAuthor(org.rssowl.core.model.types.IPerson)
   */
  public synchronized void setAuthor(IPerson author) {
    fAuthor = author;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getDescription()
   */
  public synchronized String getDescription() {
    return fDescription;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setDescription(java.lang.String)
   */
  public synchronized void setDescription(String description) {
    fDescription = description;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getLink()
   */
  public synchronized URI getLink() {
    if (fLink == null && fLinkText != null) {
      try {
        fLink = new URI(fLinkText);
      } catch (URISyntaxException e) {
        throw new IllegalStateException("Somehow an illegal URI was stored.", e); //$NON-NLS-1$
      }
    }
    return fLink;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setLink(java.lang.String)
   */
  public synchronized void setLink(URI link) {
    fLinkText = link == null ? null : link.toString();
    fLink = link;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getPublishDate()
   */
  public synchronized Date getPublishDate() {
    return fPublishDate;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setPublishDate(java.util.Date)
   */
  public synchronized void setPublishDate(Date publishDate) {
    fPublishDate = publishDate;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getTitle()
   */
  public synchronized String getTitle() {
    return fTitle;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setTitle(java.lang.String)
   */
  public synchronized void setTitle(String title) {
    fTitle = title;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getFeed()
   */
  public synchronized FeedLinkReference getFeedReference() {
    if (fFeedLinkReference == null) {
      try {
        fFeedLinkReference = new FeedLinkReference(new URI(fFeedLink));
      } catch (URISyntaxException e) {
        throw new IllegalStateException("A Malformed URI was stored somehow", e); //$NON-NLS-1$
      }
    }
    return fFeedLinkReference;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setReceiveDate(java.util.Date)
   */
  public synchronized void setReceiveDate(Date receiveDate) {
    fReceiveDate = receiveDate;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getReceiveDate()
   */
  public synchronized Date getReceiveDate() {
    return fReceiveDate;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setComments(java.lang.String)
   */
  public synchronized void setComments(String comments) {
    fComments = comments;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setGuid(org.rssowl.core.model.types.IGuid)
   */
  public synchronized void setGuid(IGuid guid) {
    fGuid = guid;
    fGuidValue = (guid == null ? null : guid.getValue());
    fGuidIsPermaLink = (guid == null ? false : guid.isPermaLink());
  }

  /*
   * @see org.rssowl.core.model.types.INews#setSource(org.rssowl.core.model.types.ISource)
   */
  public synchronized void setSource(ISource source) {
    fSource = source;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setInReplyTo(java.lang.String)
   */
  public void setInReplyTo(String guid) {
    fInReplyTo = guid;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setModifiedDate(java.util.Date)
   */
  public synchronized void setModifiedDate(Date modifiedDate) {
    fModifiedDate = modifiedDate;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getModifiedDate()
   */
  public synchronized Date getModifiedDate() {
    return fModifiedDate;
  }

  /*
   * @see org.rssowl.core.model.types.INews#addCategory(org.rssowl.core.model.types.ICategory)
   */
  public synchronized void addCategory(ICategory category) {
    if (fCategories == null)
      fCategories = new ArrayList<ICategory>();

    fCategories.add(category);
  }

  /*
   * @see org.rssowl.core.model.types.INews#getComments()
   */
  public synchronized String getComments() {
    return fComments;
  }

  /*
   * @see org.rssowl.core.model.types.INews#isFlagged()
   */
  public synchronized boolean isFlagged() {
    return fIsFlagged;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setFlagged(boolean)
   */
  public synchronized void setFlagged(boolean isFlagged) {
    fIsFlagged = isFlagged;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getLabel()
   */
  public synchronized ILabel getLabel() {
    return fLabel;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setLabel(org.rssowl.core.model.types.impl.Label)
   */
  public synchronized void setLabel(ILabel label) {
    fLabel = label;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getGuid()
   */
  public synchronized IGuid getGuid() {
    if (fGuid == null && fGuidValue != null) {
      fGuid = new Guid(fGuidValue);
      fGuid.setPermaLink(fGuidIsPermaLink);
    }
    return fGuid;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setBase(java.net.URI)
   */
  public synchronized void setBase(URI baseUri) {
    fBaseUri = getURIText(baseUri);
  }

  /*
   * @see org.rssowl.core.model.types.INews#getBase()
   */
  public synchronized URI getBase() {
    return createURI(fBaseUri);
  }

  /*
   * @see org.rssowl.core.model.types.INews#getCategories()
   */
  public synchronized List<ICategory> getCategories() {
    if (fCategories == null)
      return Collections.emptyList();

    return Collections.unmodifiableList(fCategories);
  }

  /*
   * @see org.rssowl.core.model.types.INews#setState(org.rssowl.core.model.types.INews.State)
   */
  public synchronized void setState(State state) {
    Assert.isNotNull(state, "state cannot be null"); //$NON-NLS-1$
    fStateOrdinal = state.ordinal();
    fState = state;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getState()
   */
  public synchronized State getState() {
    if (fState == null)
      fState = INews.State.values()[fStateOrdinal];

    return fState;
  }

  /*
   * @see org.rssowl.core.model.types.INews#setRating(int)
   */
  public synchronized void setRating(int rating) {
    fRating = rating;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getRating()
   */
  public synchronized int getRating() {
    return fRating;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getSource()
   */
  public synchronized ISource getSource() {
    return fSource;
  }

  /*
   * @see org.rssowl.core.model.types.INews#getInReplyTo()
   */
  public String getInReplyTo() {
    return fInReplyTo;
  }

  /*
   * @see org.rssowl.core.model.types.INews#isVisible()
   */
  public synchronized boolean isVisible() {
    INews.State state = getState();
    return state == State.NEW || state == State.UPDATED || state == State.UNREAD || state == State.READ;
  }

  @SuppressWarnings("nls")
  @Override
  public synchronized String toString() {
    StringBuilder str = new StringBuilder();
    str.append("\n\n****************************** News ******************************\n");
    str.append("\nNews ID: ").append(getId());
    if (getTitle() != null)
      str.append("\nTitle: ").append(getTitle());
    if (getLink() != null)
      str.append("\nLink: ").append(getLink());

    return str.toString();
  }

  /**
   * Returns a String describing the state of this Entity.
   *
   * @return A String describing the state of this Entity.
   */
  @SuppressWarnings("nls")
  public synchronized String toLongString() {
    StringBuilder str = new StringBuilder();

    str.append("\n\n****************************** News ******************************\n");
    str.append("\nNews ID: ").append(getId());
    if (fFeedLink != null)
      str.append("\nFeed Link: ").append(fFeedLink);
    str.append("\nState: ").append(getState());
    if (getTitle() != null)
      str.append("\nTitle: ").append(getTitle());
    if (getLink() != null)
      str.append("\nLink: ").append(getLink());
    if (getBase() != null)
      str.append("\nBase URI: ").append(getBase());
    if (getDescription() != null)
      str.append("\nDescription: ").append(getDescription());
    str.append("\nRating: ").append(getRating());
    if (getPublishDate() != null)
      str.append("\nPublish Date: ").append(DateFormat.getDateTimeInstance().format(getPublishDate()));
    if (getReceiveDate() != null)
      str.append("\nReceive Date: ").append(DateFormat.getDateTimeInstance().format(getReceiveDate()));
    if (getModifiedDate() != null)
      str.append("\nModified Date: ").append(DateFormat.getDateTimeInstance().format(getModifiedDate()));
    if (getAuthor() != null)
      str.append("\nAuthor: ").append(getAuthor());
    if (getComments() != null)
      str.append("\nComments: ").append(getComments());
    if (getGuid() != null)
      str.append("\nGUID: ").append(getGuid());
    if (getSource() != null)
      str.append("\nSource: ").append(getSource());
    if (getInReplyTo() != null)
      str.append("\nIn Reply To: ").append(getInReplyTo());
    if (getLabel() != null)
      str.append("\nLabel: ").append(getLabel());
    str.append("\nAttachments: ").append(getAttachments());
    str.append("\nCategories: ").append(getCategories());
    str.append("\nIs Flagged: ").append(fIsFlagged);
    str.append("\nProperties: ").append(getProperties());

    return str.toString();
  }

  /**
   * @param news
   * @return whether <code>news</code> is identical to this object.
   */
  //TODO Consider not casting to News since this is now part of INews.
  public synchronized boolean isIdentical(INews news) {
    if (news == null)
      return false;

    synchronized (news) {
      if (this == news)
        return true;

      if (news instanceof News == false)
        return false;

      News n = (News) news;

      return getId().equals(n.getId()) &&
          fFeedLink.equals(n.fFeedLink) &&
          simpleFieldsEqual(news) &&
          (fReceiveDate == null ? n.fReceiveDate == null : fReceiveDate.equals(n.fReceiveDate)) &&
          (getGuid() == null ? n.getGuid() == null : getGuid().equals(n.getGuid())) &&
          (fSource == null ? n.fSource == null : fSource.equals(n.fSource)) &&
          (fInReplyTo == null ? n.fInReplyTo == null : fInReplyTo.equals(n.fInReplyTo)) &&
          (fLabel == null ? n.fLabel == null : fLabel.equals(n.fLabel)) &&
          (getAuthor() == null ? n.getAuthor() == null : getAuthor().equals(n.getAuthor())) &&
          getAttachments().equals(n.getAttachments()) &&
          getCategories().equals(n.getCategories()) &&
          getState() == n.getState() && fIsFlagged == n.fIsFlagged && fRating == n.fRating &&
          (getProperties() == null ? n.getProperties() == null : getProperties().equals(n.getProperties()));
    }
  }

  private boolean simpleFieldsEqual(INews news) {
    return MergeUtils.equals(getBase(), news.getBase()) &&
        MergeUtils.equals(fComments, news.getComments()) &&
        MergeUtils.equals(getDescription(), news.getDescription()) &&
        MergeUtils.equals(getLink(), news.getLink()) &&
        MergeUtils.equals(fModifiedDate, news.getModifiedDate()) &&
        MergeUtils.equals(fPublishDate, news.getPublishDate()) &&
        MergeUtils.equals(fInReplyTo, news.getInReplyTo()) &&
        MergeUtils.equals(fTitle, news.getTitle());
  }

  public synchronized MergeResult merge(INews news) {
    Assert.isNotNull(news, "news cannot be null"); //$NON-NLS-1$
    synchronized (news) {
      boolean updated = mergeState(news);

      MergeResult result = new MergeResult();
      /* Merge all items except for feed and id */
      updated |= processListMergeResult(result, mergeAttachments(news.getAttachments()));
      updated |= processListMergeResult(result, mergeCategories(news.getCategories()));
      updated |= processListMergeResult(result, mergeAuthor(news.getAuthor()));
      updated |= processListMergeResult(result, mergeGuid(news.getGuid()));
      updated |= processListMergeResult(result, mergeSource(news.getSource()));
      updated |= !simpleFieldsEqual(news);

      setBase(news.getBase());
      fComments = news.getComments();
      setDescription(news.getDescription());
      setLink(news.getLink());
      fModifiedDate = news.getModifiedDate();
      fPublishDate = news.getPublishDate();
      fTitle = news.getTitle();
      fInReplyTo = news.getInReplyTo();

      ComplexMergeResult<?> propertiesResult = MergeUtils.mergeProperties(this, news);
      if (updated || propertiesResult.isStructuralChange()) {
        result.addUpdatedObject(this);
        result.addAll(propertiesResult);
      }
      return result;
    }
  }

  private boolean mergeState(INews news) {
    State thisState = getState();
    State otherState = news.getState();
    if (thisState != otherState && otherState != State.NEW) {
      setState(otherState);
      return true;
    }
    if (isUpdated(news)) {
      setState(State.UPDATED);
      return true;
    }
    return false;
  }

  //FIXME Need to consolidate description comparison so that we only do it once
  //per merge.
  private boolean isUpdated(INews news) {
    State thisState = getState();
    if (thisState != State.READ && thisState != State.UNREAD)
      return false;

    String title = news.getTitle();
    if (!(fTitle == null ? title == null : fTitle.equals(title)))
      return true;

    return false;
  }

  private ComplexMergeResult<IGuid> mergeGuid(IGuid guid) {
    ComplexMergeResult<IGuid> mergeResult = MergeUtils.merge(getGuid(), guid);
    fGuid = mergeResult.getMergedObject();
    return mergeResult;
  }

  private ComplexMergeResult<ISource> mergeSource(ISource source) {
    ComplexMergeResult<ISource> mergeResult = MergeUtils.merge(getSource(), source);
    fSource = mergeResult.getMergedObject();
    return mergeResult;
  }

  private ComplexMergeResult<IPerson> mergeAuthor(IPerson author) {
    ComplexMergeResult<IPerson> mergeResult = MergeUtils.merge(getAuthor(), author);
    fAuthor = mergeResult.getMergedObject();
    return mergeResult;
  }

  private ComplexMergeResult<List<ICategory>> mergeCategories(List<ICategory> categories) {
    Comparator<ICategory> comparator = new Comparator<ICategory>() {

      public int compare(ICategory o1, ICategory o2) {
        if (o1.getName() == null ? o2.getName() == null : o1.getName().equals(o2.getName())) {
          return 0;
        }
        return -1;
      }

    };
    ComplexMergeResult<List<ICategory>> mergeResult = MergeUtils.merge(fCategories, categories, comparator, null);
    fCategories = mergeResult.getMergedObject();
    return mergeResult;
  }

  private ComplexMergeResult<List<IAttachment>> mergeAttachments(List<IAttachment> attachments) {
    Comparator<IAttachment> comparator = new Comparator<IAttachment>() {
      public int compare(IAttachment o1, IAttachment o2) {
        if (o1.getLink().equals(o2.getLink())) {
          return 0;
        }
        return -1;
      }
    };
    ComplexMergeResult<List<IAttachment>> mergeResult = MergeUtils.merge(fAttachments, attachments, comparator, this);
    fAttachments = mergeResult.getMergedObject();
    return mergeResult;
  }

  public synchronized void setParent(IFeed feed) {
    Assert.isNotNull(feed, "feed"); //$NON-NLS-1$
    this.fFeedLink = feed.getLink().toString();

    /*
     * Current value is not valid anymore, but don't create a new one until
     * getFeedReference is called.
     */
    this.fFeedLinkReference = null;
  }

  public synchronized void removeAttachment(IAttachment attachment) {
    if (fAttachments != null)
      fAttachments.remove(attachment);
  }
}
TOP

Related Classes of org.rssowl.core.model.internal.types.News

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.