Package org.rssowl.core.tests.model

Source Code of org.rssowl.core.tests.model.ModelTest

/*   **********************************************************************  **
**   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.tests.model;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.junit.Before;
import org.junit.Test;
import org.rssowl.core.model.NewsModel;
import org.rssowl.core.model.dao.IApplicationLayer;
import org.rssowl.core.model.dao.IModelDAO;
import org.rssowl.core.model.dao.PersistenceException;
import org.rssowl.core.model.events.AttachmentAdapter;
import org.rssowl.core.model.events.AttachmentEvent;
import org.rssowl.core.model.events.AttachmentListener;
import org.rssowl.core.model.events.BookMarkAdapter;
import org.rssowl.core.model.events.BookMarkEvent;
import org.rssowl.core.model.events.BookMarkListener;
import org.rssowl.core.model.events.CategoryAdapter;
import org.rssowl.core.model.events.CategoryEvent;
import org.rssowl.core.model.events.CategoryListener;
import org.rssowl.core.model.events.FeedAdapter;
import org.rssowl.core.model.events.FeedEvent;
import org.rssowl.core.model.events.FeedListener;
import org.rssowl.core.model.events.FolderAdapter;
import org.rssowl.core.model.events.FolderEvent;
import org.rssowl.core.model.events.FolderListener;
import org.rssowl.core.model.events.LabelEvent;
import org.rssowl.core.model.events.LabelListener;
import org.rssowl.core.model.events.NewsAdapter;
import org.rssowl.core.model.events.NewsEvent;
import org.rssowl.core.model.events.NewsListener;
import org.rssowl.core.model.events.PersonAdapter;
import org.rssowl.core.model.events.PersonEvent;
import org.rssowl.core.model.events.PersonListener;
import org.rssowl.core.model.events.SearchConditionAdapter;
import org.rssowl.core.model.events.SearchConditionEvent;
import org.rssowl.core.model.events.SearchConditionListener;
import org.rssowl.core.model.events.SearchMarkAdapter;
import org.rssowl.core.model.events.SearchMarkEvent;
import org.rssowl.core.model.events.SearchMarkListener;
import org.rssowl.core.model.internal.search.SearchValueType;
import org.rssowl.core.model.internal.types.BookMark;
import org.rssowl.core.model.internal.types.Feed;
import org.rssowl.core.model.internal.types.Folder;
import org.rssowl.core.model.internal.types.SearchMark;
import org.rssowl.core.model.reference.AttachmentReference;
import org.rssowl.core.model.reference.BookMarkReference;
import org.rssowl.core.model.reference.CategoryReference;
import org.rssowl.core.model.reference.FeedLinkReference;
import org.rssowl.core.model.reference.FeedReference;
import org.rssowl.core.model.reference.FolderReference;
import org.rssowl.core.model.reference.LabelReference;
import org.rssowl.core.model.reference.NewsReference;
import org.rssowl.core.model.reference.PersonReference;
import org.rssowl.core.model.reference.SearchConditionReference;
import org.rssowl.core.model.reference.SearchMarkReference;
import org.rssowl.core.model.search.ISearchCondition;
import org.rssowl.core.model.search.ISearchField;
import org.rssowl.core.model.search.ISearchValueType;
import org.rssowl.core.model.search.SearchSpecifier;
import org.rssowl.core.model.types.IAttachment;
import org.rssowl.core.model.types.IBookMark;
import org.rssowl.core.model.types.ICategory;
import org.rssowl.core.model.types.IExtendableType;
import org.rssowl.core.model.types.IFeed;
import org.rssowl.core.model.types.IFolder;
import org.rssowl.core.model.types.ILabel;
import org.rssowl.core.model.types.IModelTypesFactory;
import org.rssowl.core.model.types.INews;
import org.rssowl.core.model.types.IPerson;
import org.rssowl.core.model.types.ISearchMark;
import org.rssowl.core.model.types.INews.State;
import org.rssowl.core.tests.TestUtils;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

/**
* This TestCase is for testing the Model Plugin.
*
* @author bpasero
*/
@SuppressWarnings("nls")
public class ModelTest {
  private IModelTypesFactory fFactory;
  private IModelDAO fDao;
  private NewsModel fModel;
  private IApplicationLayer fAppLayer;
 
  /**
   * @throws Exception
   */
  @Before
  public void setUp() throws Exception {
    NewsModel.getDefault().getPersistenceLayer().getModelSearch().stopIndexer();
    NewsModel.getDefault().getPersistenceLayer().recreateSchema();
    fFactory = NewsModel.getDefault().getTypesFactory();
    fDao = NewsModel.getDefault().getPersistenceLayer().getModelDAO();
    fModel = NewsModel.getDefault();
    fAppLayer = NewsModel.getDefault().getPersistenceLayer().getApplicationLayer();
  }

  /**
   * Tests that no UPDATE event is issued for a type that has been deleted (and
   * as such there is also a REMOVE event). See bug #189 for more information.
   *
   * @throws Exception
   */
  @Test
  public void testNoUpdateEventWithRemoveEvent() throws Exception {
    IFolder folder = fFactory.createFolder(null, null, "Folder");
    fFactory.createFolder(null, folder, "Child folder #1");
    fFactory.createFolder(null, folder, "Child folder #2");
    fFactory.createFolder(null, folder, "Child folder #3");
    final IFolder savedFolder = fDao.saveFolder(folder);
    final IFolder savedChildFolder1 = savedFolder.getFolders().get(0);
    final IFolder savedChildFolder2 = savedFolder.getFolders().get(1);
    final IFolder savedChildFolder3 = savedFolder.getFolders().get(2);
    List<IFolder> foldersToRemove = new ArrayList<IFolder>();
    foldersToRemove.add(savedChildFolder1);
    foldersToRemove.add(savedChildFolder2);

    final boolean[] folderDeletedCalled = new boolean[1];
    final boolean[] folderUpdatedCalled = new boolean[1];
    FolderListener listener = new FolderAdapter() {
      @Override
      public void folderAdded(Set<FolderEvent> events) {
        fail("Unexpected folder added event");
      }

      @Override
      public void folderDeleted(Set<FolderEvent> events) {
        assertEquals(2, events.size());
        for (FolderEvent event : events) {
          IFolder folder = event.getEntity();
          if (!folder.equals(savedChildFolder1) && (!folder.equals(savedChildFolder2)))
            fail("No delete event expected for folder: " + folder.getId());

          folderDeletedCalled[0] = true;
        }
      }

      @Override
      public void folderUpdated(Set<FolderEvent> events) {
        assertEquals(2, events.size());
        for (FolderEvent event : events) {
          Long id = event.getEntity().getId();
          if (!id.equals(savedChildFolder3.getId()) && (!id.equals(savedFolder.getId())))
            fail("No update event expected for folder: " + id);

        }
        folderUpdatedCalled[0] = true;
      }
    };
    NewsModel.getDefault().addFolderListener(listener);
    try {
      fAppLayer.deleteFolders(foldersToRemove);
      assertEquals(true, folderDeletedCalled[0]);
      assertEquals(true, folderUpdatedCalled[0]);
    } finally {
      NewsModel.getDefault().removeFolderListener(listener);
    }
  }
 
  /**
   * Tests that merging a news with categories doesn't throw any exception.
   * @throws Exception
   */
  @Test
  public void testNewsWithCategoriesMerge() throws Exception    {
    IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
    INews news = fFactory.createNews(null, feed, new Date());
    news.setLink(new URI("http://news.com"));
    fFactory.createCategory(null, news);
    INews anotherNews = fFactory.createNews(null, feed, new Date());
    anotherNews.setLink(new URI("http://anothernews.com"));
    fFactory.createCategory(null, anotherNews);
    ICategory category = fFactory.createCategory(null, anotherNews);
    category.setName("name");
    news.merge(anotherNews);
  }
 
  /**
   * Tests that merging a feed with categories doesn't throw any exception.
   * @throws Exception
   */
  @Test
  public void testFeedWithCategoriesMerge() throws Exception    {
    IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
    fFactory.createCategory(null, feed);
    IFeed anotherFeed = fFactory.createFeed(null, new URL("http://www.feed.com"));
    fFactory.createCategory(null, anotherFeed);
    ICategory category = fFactory.createCategory(null, anotherFeed);
    category.setName("name");
    feed.merge(anotherFeed);
  }

  /**
   * Tests that {@link IModelDAO#saveFeed(IFeed)} sets the current and old state
   * correctly in the news when firing a newsUpdated event.
   *
   * @throws Exception
   */
  @Test
  public void testSaveFeedSetsCurrentAndOldStateInNews() throws Exception {
    IFeed feed = new Feed(new URL("http://www.feed.com"));
    feed = fDao.saveFeed(feed);

    INews news = fFactory.createNews(null, feed, new Date());
    news.setTitle("News Title #1");
    news.setLink(new URI("http://www.link.com"));
    news.setState(INews.State.UNREAD);

    feed = fDao.saveFeed(feed);

    final INews savedNews = feed.getNews().get(0);
    savedNews.setTitle("News Title Updated #1");

    NewsListener newsListener = new NewsAdapter() {
      @Override
      public void newsUpdated(Set<NewsEvent> events) {
        assertEquals(1, events.size());
        NewsEvent event = events.iterator().next();
        assertEquals(true, event.getEntity().equals(savedNews));
        assertEquals(State.UNREAD, event.getOldNews().getState());
        assertEquals(State.UNREAD, event.getEntity().getState());
      }
    };
    fModel.addNewsListener(newsListener);
    try {
      feed = fDao.saveFeed(feed);
    } finally {
      fModel.removeNewsListener(newsListener);
    }
    newsListener = new NewsAdapter() {
      @Override
      public void newsUpdated(Set<NewsEvent> events) {
        assertEquals(1, events.size());
        NewsEvent event = events.iterator().next();
        assertEquals(savedNews.getId(), event.getEntity().getId());
        assertEquals(State.UNREAD, event.getOldNews().getState());
        assertEquals(State.UPDATED, event.getEntity().getState());
      }
    };
    fModel.addNewsListener(newsListener);
    feed.getNews().get(0).setState(State.UPDATED);
    try {
      feed = fDao.saveFeed(feed);
    } finally {
      fModel.removeNewsListener(newsListener);
    }
  }

  /**
   * Tests that {@link IModelDAO#saveNews(INews)} sets the current and old state
   * correctly when firing a newsUpdated event.
   *
   * @throws Exception
   */
  @Test
  public void testSaveNewsSetsCurrentAndOldState() throws Exception {
    IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
    feed = fDao.saveFeed(feed);

    INews news = fFactory.createNews(null, feed, new Date());
    news.setTitle("News Title #1");
    news.setLink(new URI("http://www.link.com"));
    news.setState(INews.State.UNREAD);

    feed = fDao.saveFeed(feed);

    INews savedNews = feed.getNews().get(0);
    final Long savedNewsId = savedNews.getId();
    savedNews.setTitle("News Title Updated #1");

    NewsListener newsListener = new NewsAdapter() {
      @Override
      public void newsUpdated(Set<NewsEvent> events) {
        assertEquals(1, events.size());
        NewsEvent event = events.iterator().next();
        assertEquals(savedNewsId, event.getEntity().getId());
        assertEquals(State.UNREAD, event.getOldNews().getState());
        assertEquals(State.UNREAD, event.getEntity().getState());
      }
    };
    fModel.addNewsListener(newsListener);
    try {
      savedNews = fDao.saveNews(savedNews);
    } finally {
      fModel.removeNewsListener(newsListener);
    }
    newsListener = new NewsAdapter() {
      @Override
      public void newsUpdated(Set<NewsEvent> events) {
        assertEquals(1, events.size());
        NewsEvent event = events.iterator().next();
        assertEquals(savedNewsId, event.getEntity().getId());
        assertEquals(State.UNREAD, event.getOldNews().getState());
        assertEquals(State.UPDATED, event.getEntity().getState());
      }
    };
    fModel.addNewsListener(newsListener);
    savedNews.setState(State.UPDATED);
    try {
      fDao.saveNews(savedNews);
    } finally {
      fModel.removeNewsListener(newsListener);
    }
  }
 
  /**
   * Tests equals and hashCode for FeedLinkReference.
   * @throws Exception
   */
  @Test
  public void testFeedLinkReferenceEqualsAndHashCode() throws Exception {
    String url1 = "http://url1.com";
    String url3 = "http://url3.com";
   
    FeedLinkReference feedRef1 = new FeedLinkReference(new URL(url1));
    FeedLinkReference feedRef2 = new FeedLinkReference(new URL(url1));
   
    assertEquals(feedRef1, feedRef2);
    assertEquals(feedRef1.hashCode(), feedRef2.hashCode());
   
    FeedLinkReference feedRef3 = new FeedLinkReference(new URL(url3));
    assertFalse(feedRef1.equals(feedRef3));
    assertFalse(feedRef1.hashCode() == feedRef3.hashCode());
  }

  /**
   * Tests {@link IFeed#getVisibleNews()} and
   * {@link IFeed#getNewsByStates(EnumSet)}
   *
   * @throws Exception
   */
  @Test
  public void testGetNewsByStatesAndGetVisibleNews() throws Exception {
    IFeed feed = createFeed("http://feed1.com");
    feed = fDao.saveFeed(feed);

    int newNewsCount = 4;
    List<INews> newNews = new ArrayList<INews>();
    for (int i = 0; i < newNewsCount; ++i) {
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("new News: " + i);
      news.setState(State.NEW);
      newNews.add(news);
    }

    int readNewsCount = 2;
    List<INews> readNews = new ArrayList<INews>();
    for (int i = 0; i < readNewsCount; ++i) {
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("read News: " + i);
      news.setState(State.READ);
      readNews.add(news);
    }

    int unreadNewsCount = 3;
    List<INews> unreadNews = new ArrayList<INews>();
    for (int i = 0; i < unreadNewsCount; ++i) {
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("unread News: " + i);
      news.setState(State.UNREAD);
      unreadNews.add(news);
    }

    int updatedNewsCount = 6;
    List<INews> updatedNews = new ArrayList<INews>();
    for (int i = 0; i < updatedNewsCount; ++i) {
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("updated News: " + i);
      news.setState(State.UPDATED);
      updatedNews.add(news);
    }

    int hiddenNewsCount = 8;
    List<INews> hiddenNews = new ArrayList<INews>();
    for (int i = 0; i < hiddenNewsCount; ++i) {
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("hidden News: " + i);
      news.setState(State.HIDDEN);
      hiddenNews.add(news);
    }

    int deletedNewsCount = 7;
    List<INews> deletedNews = new ArrayList<INews>();
    for (int i = 0; i < deletedNewsCount; ++i) {
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("deleted News: " + i);
      news.setState(State.DELETED);
      deletedNews.add(news);
    }

    assertEquals(newNewsCount, feed.getNewsByStates(EnumSet.of(State.NEW)).size());
    int counter = 0;
    for (INews news : feed.getNewsByStates(EnumSet.of(State.NEW))) {
      INews newsItem = newNews.get(counter++);
      assertEquals(newsItem.getTitle(), news.getTitle());
    }

    assertEquals(readNewsCount, feed.getNewsByStates(EnumSet.of(State.READ)).size());
    counter = 0;
    for (INews news : feed.getNewsByStates(EnumSet.of(State.READ))) {
      INews newsItem = readNews.get(counter++);
      assertEquals(newsItem.getTitle(), news.getTitle());
    }

    assertEquals(unreadNewsCount, feed.getNewsByStates(EnumSet.of(State.UNREAD)).size());
    counter = 0;
    for (INews news : feed.getNewsByStates(EnumSet.of(State.UNREAD))) {
      INews newsItem = unreadNews.get(counter++);
      assertEquals(newsItem.getTitle(), news.getTitle());
    }

    assertEquals(updatedNewsCount, feed.getNewsByStates(EnumSet.of(State.UPDATED)).size());
    counter = 0;
    for (INews news : feed.getNewsByStates(EnumSet.of(State.UPDATED))) {
      INews newsItem = updatedNews.get(counter++);
      assertEquals(newsItem.getTitle(), news.getTitle());
    }

    assertEquals(hiddenNewsCount, feed.getNewsByStates(EnumSet.of(State.HIDDEN)).size());
    counter = 0;
    for (INews news : feed.getNewsByStates(EnumSet.of(State.HIDDEN))) {
      INews newsItem = hiddenNews.get(counter++);
      assertEquals(newsItem.getTitle(), news.getTitle());
    }

    assertEquals(deletedNewsCount, feed.getNewsByStates(EnumSet.of(State.DELETED)).size());
    counter = 0;
    for (INews news : feed.getNewsByStates(EnumSet.of(State.DELETED))) {
      INews newsItem = deletedNews.get(counter++);
      assertEquals(newsItem.getTitle(), news.getTitle());
    }

    int visibleNewsCount = newNewsCount + readNewsCount + unreadNewsCount + updatedNewsCount;
    assertEquals(visibleNewsCount, feed.getVisibleNews().size());

    for (INews news : feed.getVisibleNews()) {
      boolean matchFound = false;
      for (INews newsItem : newNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;

      for (INews newsItem : readNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;

      for (INews newsItem : unreadNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;

      for (INews newsItem : updatedNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;
      fail("No match was found. A news that had the wrong state was returned");
    }

    for (INews news : feed.getNewsByStates(EnumSet.of(State.NEW, State.HIDDEN, State.DELETED))) {
      boolean matchFound = false;
      for (INews newsItem : newNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;

      for (INews newsItem : hiddenNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;

      for (INews newsItem : deletedNews) {
        if (news.getTitle().equals(newsItem.getTitle())) {
          matchFound = true;
          break;
        }
      }
      if (matchFound)
        continue;
      fail("No match was found. A news that had the wrong state was returned");
    }

  }

  /**
   * Tests that removing a INews doesn't also remove its parent feed.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveNewsWithoutFeed() throws Exception {
    IFeed feed = createFeed("http://www.rssowl.org");
    fFactory.createNews(null, feed, new Date());
    IFeed savedFeed = fDao.saveFeed(feed);
    fDao.deleteNews(new NewsReference(feed.getNews().get(0).getId()));
    savedFeed = fDao.loadFeed(savedFeed.getId());
    assertNotNull(savedFeed);
  }

  /**
   * Tests that removing a ISearchCondition doesn't also remove its parent
   * ISearchMark.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveSearchConditionWithoutSearchMark() throws Exception {
    IFolder folder = fFactory.createFolder(null, null, "Folder");
    ISearchMark searchMark = fFactory.createSearchMark(null, folder, "Mark");
    ISearchField searchField = fFactory.createSearchField(0, INews.class);
    fFactory.createSearchCondition(null, searchMark, searchField, SearchSpecifier.BEGINS_WITH, "Some value", false);
    IFolder savedFolder = fDao.saveFolder(folder);
    ISearchMark savedMark = (ISearchMark) savedFolder.getMarks().get(0);
    fDao.deleteSearchCondition(new SearchConditionReference(savedMark.getSearchConditions().get(0).getId()));
    assertNotNull(fDao.loadSearchMark(savedMark.getId()));
  }

  /**
   * Tests that removing a IPerson doesn't also remove its parent INews.
   *
   * @throws Exception
   */
  @Test
  public void testRemovePersonWithoutNews() throws Exception {
    IFeed feed = createFeed("http://www.rssowl.org");
    feed = fDao.saveFeed(feed);
    INews news = fFactory.createNews(null, feed, new Date());
    fFactory.createPerson(null, news);
    INews savedNews = fDao.saveNews(news);
    fDao.deletePerson(new PersonReference(savedNews.getAuthor().getId()));
    savedNews = fDao.loadNews(savedNews.getId());
    assertNotNull(savedNews);
  }

  /**
   * Tests that removing a IPerson doesn't also remove its parent feed.
   *
   * @throws Exception
   */
  @Test
  public void testRemovePersonWithoutFeed() throws Exception {
    IFeed feed = createFeed("http://www.rssowl.org");
    fFactory.createPerson(null, feed);
    IFeed savedFeed = fDao.saveFeed(feed);
    fDao.deletePerson(new PersonReference(savedFeed.getAuthor().getId()));
    savedFeed = fDao.loadFeed(savedFeed.getId());
    assertNotNull(savedFeed);
  }

  /**
   * Tests that removing a ICategory doesn't also remove its parent news.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveCategoryWithoutNews() throws Exception {
    IFeed feed = createFeed("http://www.rssowl.org");
    feed = fDao.saveFeed(feed);
    INews news = fFactory.createNews(null, feed, new Date());
    fFactory.createCategory(null, news);
    INews savedNews = fDao.saveNews(news);
    fDao.deleteCategory(new CategoryReference(news.getCategories().get(0).getId()));
    savedNews = fDao.loadNews(savedNews.getId());
    assertNotNull(savedNews);
  }

  /**
   * Tests that removing a ICategory doesn't also remove its parent feed.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveCategoryWithoutFeed() throws Exception {
    IFeed feed = createFeed("http://www.rssowl.org");
    fFactory.createCategory(null, feed);
    IFeed savedFeed = fDao.saveFeed(feed);
    fDao.deleteCategory(new CategoryReference(feed.getCategories().get(0).getId()));
    savedFeed = fDao.loadFeed(savedFeed.getId());
    assertNotNull(savedFeed);
  }

  /**
   * Tests that removing a child IFolder doesn't also remove its parent IFolder.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveFolderWithoutParentFolder() throws Exception {
    IFolder folder = fFactory.createFolder(null, null, "Folder");
    fFactory.createFolder(null, folder, "Child folder");
    IFolder savedFolder = fDao.saveFolder(folder);
    IFolder savedChildFolder = savedFolder.getFolders().get(0);
    fDao.deleteFolder(new FolderReference(savedChildFolder.getId()));
    assertNotNull(fDao.loadFolder(savedFolder.getId()));
  }

  /**
   * Tests that removing a ISearchMark doesn't also remove its parent folder.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveSearchMarkWithoutFolder() throws Exception {
    IFolder folder = fFactory.createFolder(null, null, "Folder");
    fFactory.createSearchMark(null, folder, "Mark");
    IFolder savedFolder = fDao.saveFolder(folder);
    ISearchMark savedMark = (ISearchMark) savedFolder.getMarks().get(0);
    fDao.deleteSearchMark(new SearchMarkReference(savedMark.getId()));
    assertNotNull(fDao.loadFolder(savedFolder.getId()));
  }

  /**
   * Tests that removing a ISearchMark causes an update event in the parent
   * folder.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveSearchUpdatesFolder() throws Exception {
    IFolder folder = fFactory.createFolder(null, null, "Folder");
    fFactory.createSearchMark(null, folder, "Mark");
    final IFolder savedFolder = fDao.saveFolder(folder);

    final boolean[] folderUpdatedCalled = new boolean[1];
    FolderListener folderListener = new FolderListener() {
      public void folderAdded(Set<FolderEvent> events) {
        fail("folderAdded should not be called");
      }

      public void folderDeleted(Set<FolderEvent> events) {
        fail("folderDeleted should not be called");
      }
     
      public void folderUpdated(Set<FolderEvent> events) {
        assertEquals(1, events.size());
        assertEquals(true, events.iterator().next().getEntity().equals(savedFolder));
        folderUpdatedCalled[0] = true;
      }
    };
    NewsModel.getDefault().addFolderListener(folderListener);
    try {
      ISearchMark savedMark = (ISearchMark) savedFolder.getMarks().get(0);
      fDao.deleteSearchMark(new SearchMarkReference(savedMark.getId()));
      assertTrue("folderUpdated was not called", folderUpdatedCalled[0]);
    } finally {
      NewsModel.getDefault().removeFolderListener(folderListener);
    }
  }

  /**
   * Tests that removing a IBookMark doesn't also remove its parent folder.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveBookMarkWithoutFolder() throws Exception {
    IFolder folder = fFactory.createFolder(null, null, "Folder");
    IFeed feed = createFeed("http://www.someurl.com");
    feed = fDao.saveFeed(feed);
    fFactory.createBookMark(null, folder, feed.getLink(),
        new FeedReference(feed.getId()), "Mark");
    IFolder savedFolder = fDao.saveFolder(folder);
    IBookMark savedMark = (IBookMark) savedFolder.getMarks().get(0);
    fDao.deleteBookMark(new BookMarkReference(savedMark.getId()));
    assertNotNull(fDao.loadFolder(savedFolder.getId()));
  }

  /**
   * Tests that removing a IAttachment doesn't also remove its parent news.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveAttachmentWithoutNews() throws Exception {
    IFeed feed = createFeed("http://www.rssowl.org");
    feed = fDao.saveFeed(feed);
    INews news = fFactory.createNews(null, feed, new Date());
    fFactory.createAttachment(null, news);
    INews savedNews = fDao.saveNews(news);
    fDao.deleteAttachment(new AttachmentReference(news.getAttachments().get(0).getId()));
    savedNews = fDao.loadNews(savedNews.getId());
    assertNotNull(savedNews);
  }

  /**
   * Test removing a IBookmark when it's the only parent of a IFeed. It should
   * cascade in that case. And test removing it when there are two IBookMarks
   * that reference the same IFeed. It should not cascade in that case.
   *
   * @throws Exception
   */
  @Test
  public void testRemoveBookMarkAndFeed() throws Exception {
    {
      IFolder folder = fFactory.createFolder(null, null, "Folder");
      IFeed feed = createFeed("http://www.someurl.com");
      feed = fDao.saveFeed(feed);
      fFactory.createBookMark(null, folder, feed.getLink(),
          new FeedReference(feed.getId()), "Mark");
      IFolder savedFolder = fDao.saveFolder(folder);
      IBookMark savedMark = (IBookMark) savedFolder.getMarks().get(0);
      IFeed savedFeed = savedMark.getFeedReference().resolve();
      fDao.deleteBookMark(new BookMarkReference(savedMark.getId()));
      assertNull("Feed must also be deleted since no more bookmarks reference it", fDao.loadFeed(savedFeed.getId()));
    }
    {
      IFolder folder = fFactory.createFolder(null, null, "AnotherFolder");
      IFeed feed = createFeed("http://www.anotherurl.com");
      feed = fDao.saveFeed(feed);
      fFactory.createBookMark(null, folder, feed.getLink(),
          new FeedReference(feed.getId()), "Mark1");
      fFactory.createBookMark(null, folder, feed.getLink(),
          new FeedReference(feed.getId()), "Mark2");
      IFolder savedFolder = fDao.saveFolder(folder);
      IBookMark savedMark1 = (IBookMark) savedFolder.getMarks().get(0);
      IBookMark savedMark2 = (IBookMark) savedFolder.getMarks().get(1);
      if (savedMark1.getName().equals("Mark2")) {
        IBookMark tempMark = savedMark1;
        savedMark1 = savedMark2;
        savedMark2 = tempMark;
      }
      IFeed savedFeed = savedMark1.getFeedReference().resolve();
      fDao.deleteBookMark(new BookMarkReference(savedMark1.getId()));
      assertNotNull("Feed must not be deleted since one bookmark references it", fDao.loadFeed(savedFeed.getId()));
      fDao.deleteBookMark(new BookMarkReference(savedMark2.getId()));
      assertNull("Feed must also be deleted since no more bookmarks reference it", fDao.loadFeed(savedFeed.getId()));
    }
  }

  /**
   * Test the equals method in model types.
   *
   * @throws Exception
   */
  @Test
  public void testEquals() throws Exception {

    /* IExtendableType */
    IExtendableType type1 = fFactory.createLabel(null, "name");
    IExtendableType type2 = fFactory.createLabel(null, "name");

    IExtendableType type3 = fFactory.createLabel(Long.valueOf(1), "name");
    IExtendableType type4 = fFactory.createLabel(Long.valueOf(1), "name");

    IExtendableType type5 = fFactory.createLabel(Long.valueOf(1), "name");
    IExtendableType type6 = fFactory.createLabel(Long.valueOf(2), "name");

    assertFalse(type1.equals(type2));
    assertTrue(type3.equals(type4));
    assertFalse(type5.equals(type6));

    /* ISearchField */
    ISearchField fieldLabelName1 = fFactory.createSearchField(ILabel.NAME, ILabel.class);
    ISearchField fieldLabelName2 = fFactory.createSearchField(ILabel.NAME, ILabel.class);
    ISearchField fieldLabelAllFields = fFactory.createSearchField(IExtendableType.ALL_FIELDS, ILabel.class);
    ISearchField fieldNewsTitle = fFactory.createSearchField(INews.TITLE, INews.class);

    assertTrue(fieldLabelName1.equals(fieldLabelName2));
    assertFalse(fieldLabelName1.equals(fieldLabelAllFields));
    assertFalse(fieldLabelName1.equals(fieldNewsTitle));

    /* ISearchValueType */
    SearchValueType valueTypeString1 = new SearchValueType(ISearchValueType.STRING);
    SearchValueType valueTypeString2 = new SearchValueType(ISearchValueType.STRING);
    SearchValueType valueTypeDate = new SearchValueType(ISearchValueType.DATE);

    SearchValueType valueTypeEnum1 = new SearchValueType(new ArrayList<String>(Arrays.asList(new String[] { "Foo", "Bar" })));
    SearchValueType valueTypeEnum2 = new SearchValueType(new ArrayList<String>(Arrays.asList(new String[] { "Foo", "Bar" })));
    SearchValueType valueTypeEnum3 = new SearchValueType(new ArrayList<String>(Arrays.asList(new String[] { "Foo" })));

    assertTrue(valueTypeString1.equals(valueTypeString2));
    assertFalse(valueTypeString1.equals(valueTypeDate));

    assertTrue(valueTypeEnum1.equals(valueTypeEnum2));
    assertFalse(valueTypeEnum1.equals(valueTypeEnum3));
  }

  /**
   * Test the hashCode method in model types.
   *
   * @throws Exception
   */
  @Test
  public void testHashCode() throws Exception {

    /* ExtendableType */
    IExtendableType type1 = fFactory.createLabel(null, "name");
    IExtendableType type2 = fFactory.createLabel(null, "name");

    IExtendableType type3 = fFactory.createLabel(Long.valueOf(1), "name");
    IExtendableType type4 = fFactory.createLabel(Long.valueOf(1), "name");

    IExtendableType type5 = fFactory.createLabel(Long.valueOf(1), "name");
    IExtendableType type6 = fFactory.createLabel(Long.valueOf(2), "name");

    assertFalse(type1.hashCode() == type2.hashCode());
    assertTrue(type3.hashCode() == type4.hashCode());
    assertFalse(type5.hashCode() == type6.hashCode());

    /* ISearchField */
    ISearchField fieldLabelName1 = fFactory.createSearchField(ILabel.NAME, ILabel.class);
    ISearchField fieldLabelName2 = fFactory.createSearchField(ILabel.NAME, ILabel.class);
    ISearchField fieldLabelAllFields = fFactory.createSearchField(IExtendableType.ALL_FIELDS, ILabel.class);
    ISearchField fieldNewsTitle = fFactory.createSearchField(INews.TITLE, INews.class);

    assertTrue(fieldLabelName1.hashCode() == fieldLabelName2.hashCode());
    assertFalse(fieldLabelName1.hashCode() == fieldLabelAllFields.hashCode());
    assertFalse(fieldLabelName1.hashCode() == fieldNewsTitle.hashCode());

    /* ISearchValueType */
    SearchValueType valueTypeString1 = new SearchValueType(ISearchValueType.STRING);
    SearchValueType valueTypeString2 = new SearchValueType(ISearchValueType.STRING);
    SearchValueType valueTypeDate = new SearchValueType(ISearchValueType.DATE);

    SearchValueType valueTypeEnum1 = new SearchValueType(new ArrayList<String>(Arrays.asList(new String[] { "Foo", "Bar" })));
    SearchValueType valueTypeEnum2 = new SearchValueType(new ArrayList<String>(Arrays.asList(new String[] { "Foo", "Bar" })));
    SearchValueType valueTypeEnum3 = new SearchValueType(new ArrayList<String>(Arrays.asList(new String[] { "Foo" })));

    assertTrue(valueTypeString1.hashCode() == valueTypeString2.hashCode());
    assertFalse(valueTypeString1.hashCode() == valueTypeDate.hashCode());

    assertTrue(valueTypeEnum1.hashCode() == valueTypeEnum2.hashCode());
    assertFalse(valueTypeEnum1.hashCode() == valueTypeEnum3.hashCode());
  }

  /**
   * Test getting Events for a Folder being added with sub-folders.
   *
   * @throws Exception
   */
  @Test
  public void testDeepFolderAddedEvents() throws Exception {
    FolderListener folderListener = null;
    BookMarkListener bookMarkListener = null;
    FeedListener feedListener = null;
    SearchMarkListener searchMarkListener = null;
    SearchConditionListener searchConditionListener = null;
    try {

      /* Check Folder Added */
      IFolder root = fFactory.createFolder(null, null, "Root");
      IFolder rootChild = fFactory.createFolder(null, root, "Root Child");
      IFolder rootChildChild1 = fFactory.createFolder(null, rootChild, "Root Child Child #1");
      IFolder rootChildChild2 = fFactory.createFolder(null, rootChild, "Root Child Child #2");
      IFolder rootChildChild1Child = fFactory.createFolder(null, rootChildChild1, "Root Child Child #1 Child");

      final boolean[] folderEventsIssued = new boolean[5];
      final Folder[] folders = new Folder[] { (Folder) root, (Folder) rootChild, (Folder) rootChildChild1, (Folder) rootChildChild2, (Folder) rootChildChild1Child };

      folderListener = new FolderAdapter() {
        @Override
        public void folderAdded(Set<FolderEvent> events) {
          for (FolderEvent event : events) {
            IFolder folder = event.getEntity();

            if ("Root".equals(folder.getName()))
              assertTrue("Expected this Event to be Root Event", event.isRoot());
            else
              assertFalse("Expected this Event to be no Root Event", event.isRoot());

            if ("Root".equals(folder.getName()))
              assertNull(folder.getParent());
            else if ("Root Child".equals(folder.getName()))
              assertEquals("Root", folder.getParent().getName());
            else if ("Root Child Child #1".equals(folder.getName()))
              assertEquals("Root Child", folder.getParent().getName());
            else if ("Root Child Child #2".equals(folder.getName()))
              assertEquals("Root Child", folder.getParent().getName());
            else if ("Root Child Child #1 Child".equals(folder.getName()))
              assertEquals("Root Child Child #1", folder.getParent().getName());

            for (int i = 0; i < folders.length; i++) {
              if (folders[i].getName().equals(folder.getName()))
                folderEventsIssued[i] = true;
            }
          }
        }
      };
      fModel.addFolderListener(folderListener);

      /* Check BookMark Added */
      final IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));

      final boolean feedEventsIssued[] = new boolean[1];
      feedListener = new FeedAdapter() {
        @Override
        public void feedAdded(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertFalse("Already received feed added event!", feedEventsIssued[0]);
            assertTrue("Expected this Event to be Root Event", event.isRoot());

            if (event.getEntity().getLink().toString().equals(feed.getLink().toString()))
              feedEventsIssued[0] = true;
          }
        }
      };
      fModel.addFeedListener(feedListener);

      /* Save Feed since a IBookMark now doesn't contain a feed */
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      final IBookMark bookMark1 = fFactory.createBookMark(null, root, feed.getLink(),
          feedRef, "Root BookMark");
      final IBookMark bookMark2 = fFactory.createBookMark(null, rootChild,
          feed.getLink(), feedRef, "Root Child BookMark");
      final IBookMark bookMark3 = fFactory.createBookMark(null, rootChildChild1Child,
          feed.getLink(), feedRef, "Root Child Child #1 BookMark");

      final boolean bookMarkEventsIssued[] = new boolean[3];

      bookMarkListener = new BookMarkAdapter() {
        @Override
        public void bookMarkAdded(Set<BookMarkEvent> events) {
          for (BookMarkEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IBookMark bookMark = event.getEntity();

            if ("Root BookMark".equals(bookMark.getName()))
              assertEquals("Root", bookMark.getFolder().getName());
            else if ("Root Child BookMark".equals(bookMark.getName()))
              assertEquals("Root Child", bookMark.getFolder().getName());
            else if ("Root Child Child #1 BookMark".equals(bookMark.getName()))
              assertEquals("Root Child Child #1 Child", bookMark.getFolder().getName());

            if (bookMark.getName().equals(bookMark1.getName()))
              bookMarkEventsIssued[0] = true;

            else if (bookMark.getName().equals(bookMark2.getName()))
              bookMarkEventsIssued[1] = true;

            else if (bookMark.getName().equals(bookMark3.getName()))
              bookMarkEventsIssued[2] = true;
          }
        }
      };
      fModel.addBookMarkListener(bookMarkListener);

      /* Check SearchMark Added */
      final ISearchMark searchMark1 = fFactory.createSearchMark(null, root, "Root SearchMark");
      final ISearchMark searchMark2 = fFactory.createSearchMark(null, rootChild, "Root Child SearchMark");
      final ISearchMark searchMark3 = fFactory.createSearchMark(null, rootChildChild1Child, "Root Child Child #1 SearchMark");

      final boolean searchMarkEventsIssued[] = new boolean[3];

      searchMarkListener = new SearchMarkAdapter() {
        @Override
        public void searchMarkAdded(Set<SearchMarkEvent> events) {
          for (SearchMarkEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            ISearchMark searchMark = event.getEntity();

            if ("Root SearchMark".equals(searchMark.getName()))
              assertEquals("Root", searchMark.getFolder().getName());
            else if ("Root Child SearchMark".equals(searchMark.getName()))
              assertEquals("Root Child", searchMark.getFolder().getName());
            else if ("Root Child Child #1 SearchMark".equals(searchMark.getName()))
              assertEquals("Root Child Child #1 Child", searchMark.getFolder().getName());

            if (searchMark.getName().equals(searchMark1.getName()))
              searchMarkEventsIssued[0] = true;

            else if (searchMark.getName().equals(searchMark2.getName()))
              searchMarkEventsIssued[1] = true;

            else if (searchMark.getName().equals(searchMark3.getName()))
              searchMarkEventsIssued[2] = true;
          }
        }
      };
      fModel.addSearchMarkListener(searchMarkListener);

      /* Check SearchCondition Added */
      ISearchField field1 = fFactory.createSearchField(IExtendableType.ALL_FIELDS, INews.class);
      final ISearchCondition condition1 = fFactory.createSearchCondition(null, searchMark1, field1, SearchSpecifier.CONTAINS, "Foo", true);
      final ISearchCondition condition2 = fFactory.createSearchCondition(null, searchMark1, field1, SearchSpecifier.IS, "Bar", true);
      final ISearchCondition condition3 = fFactory.createSearchCondition(null, searchMark2, field1, SearchSpecifier.IS_NOT, "Foo Bar", false);

      final boolean searchConditionEventsIssued[] = new boolean[3];

      searchConditionListener = new SearchConditionAdapter() {
        @Override
        public void searchConditionAdded(Set<SearchConditionEvent> events) {
          for (SearchConditionEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            ISearchCondition searchCondition = event.getEntity();

            if (searchCondition.getValue().equals(condition1.getValue()))
              searchConditionEventsIssued[0] = true;

            else if (searchCondition.getValue().equals(condition2.getValue()))
              searchConditionEventsIssued[1] = true;

            else if (searchCondition.getValue().equals(condition3.getValue()))
              searchConditionEventsIssued[2] = true;

          }
        }
      };
      fModel.addSearchConditionListener(searchConditionListener);

      /* Save Folder */
      fDao.saveFolder(root);

      /* Asserts Follow */
      assertTrue("Expected one feedAdded event", feedEventsIssued[0]);

      for (int i = 0; i < folderEventsIssued.length; i++) {
        if (!folderEventsIssued[i])
          fail("Expected five folderAdded events!");
      }

      for (int i = 0; i < bookMarkEventsIssued.length; i++) {
        if (!bookMarkEventsIssued[i])
          fail("Expected three bookMarkAdded events!");
      }

      for (int i = 0; i < searchMarkEventsIssued.length; i++) {
        if (!searchMarkEventsIssued[i])
          fail("Expected three searchMarkAdded events!");
      }

      for (int i = 0; i < searchConditionEventsIssued.length; i++) {
        if (!searchConditionEventsIssued[i])
          fail("Expected three searchConditionAdded events!");
      }
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (folderListener != null)
        fModel.removeFolderListener(folderListener);
      if (bookMarkListener != null)
        fModel.removeBookMarkListener(bookMarkListener);
      if (searchMarkListener != null)
        fModel.removeSearchMarkListener(searchMarkListener);
      if (feedListener != null)
        fModel.removeFeedListener(feedListener);
      if (searchConditionListener != null)
        fModel.removeSearchConditionListener(searchConditionListener);
    }
  }

  /**
   * Test getting Events for a Feed being added with all possible child-types
   * added.
   * <p>
   * <ul>
   * <li>Image Event not sent out</li>
   * <li>Saving News gives NullPE because getLink is not expected to be NULL</li>
   * <li>Resolving Person gives NULL</li>
   * </ul>
   * </p>
   *
   * @throws Exception
   */
  @Test
  public void testDeepFeedAddedEvents() throws Exception {
    FeedListener feedListener = null;
    NewsListener newsListener = null;
    AttachmentListener attachmentListener = null;
    PersonListener personListener = null;
    CategoryListener categoryListener = null;
    try {

      /* Check Feed Added and News received */
      final IFeed feed = fFactory.createFeed(null, new URL("http://www.foobar.com"));
      final boolean feedAdded[] = new boolean[1];
      final boolean newsReceivedFromFeed[] = new boolean[1];
      feedListener = new FeedAdapter() {
        @Override
        public void feedAdded(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertFalse("Already received feedAdded Event", feedAdded[0]);
            assertTrue("Expected this Event to be Root Event", event.isRoot());

            if (event.getEntity().getLink().toString().equals(feed.getLink().toString()))
              feedAdded[0] = true;
          }
        }
      };

      fModel.addFeedListener(feedListener);

      /* Check News Added */
      final INews news1 = fFactory.createNews(null, feed, new Date());
      news1.setTitle("News1 Title");
      news1.setLink(new URI("http://www.news.com/news1.html"));
      final INews news2 = fFactory.createNews(null, feed, new Date());
      news2.setTitle("News2 Title");
      news2.setLink(new URI("http://www.news.com/news2.html"));
      final INews news3 = fFactory.createNews(null, feed, new Date());
      news3.setTitle("News3 Title");
      news3.setLink(new URI("http://www.news.com/news3.html"));
      final boolean newsReceived[] = new boolean[3];
      newsListener = new NewsAdapter() {
        @Override
        public void newsAdded(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            INews news = event.getEntity();

            if (news.getTitle().equals(news1.getTitle()))
              newsReceived[0] = true;

            else if (news.getTitle().equals(news2.getTitle()))
              newsReceived[1] = true;

            else if (news.getTitle().equals(news3.getTitle()))
              newsReceived[2] = true;

            try {
              assertEquals(new URL("http://www.foobar.com").toString(),
                  news.getFeedReference().getLink().toString());
            } catch (MalformedURLException e) {
              fail(e.getMessage());
            }
          }
          if (events.size() == 3)
            newsReceivedFromFeed[0] = true;
        }
      };
      fModel.addNewsListener(newsListener);

      /* Check Attachment Added */
      final IAttachment attachment1 = fFactory.createAttachment(null, news1);
      attachment1.setUrl(new URI("http://www.attachment1.com"));
      final IAttachment attachment2 = fFactory.createAttachment(null, news2);
      attachment2.setUrl(new URI("http://www.attachment2.com"));
      final IAttachment attachment3 = fFactory.createAttachment(null, news3);
      attachment3.setUrl(new URI("http://www.attachment3.com"));
      final boolean attachmentAdded[] = new boolean[3];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentAdded(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IAttachment attachment = event.getEntity();

            if (attachment.getUrl().equals(attachment1.getUrl()))
              attachmentAdded[0] = true;

            else if (attachment.getUrl().equals(attachment2.getUrl()))
              attachmentAdded[1] = true;

            else if (attachment.getUrl().equals(attachment3.getUrl()))
              attachmentAdded[2] = true;

          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);

      /* Check Person Added */
      final IPerson person1 = fFactory.createPerson(null, feed);
      person1.setName("Person1");
      final IPerson person2 = fFactory.createPerson(null, news1);
      person2.setName("Person2");
      final boolean personAdded[] = new boolean[2];
      personListener = new PersonAdapter() {
        @Override
        public void personAdded(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IPerson person = event.getEntity();

            if (person.getName().equals(person1.getName()))
              personAdded[0] = true;

            else if (person.getName().equals(person2.getName()))
              personAdded[1] = true;

          }
        }
      };
      fModel.addPersonListener(personListener);

      /* Check Category Added */
      final ICategory category1 = fFactory.createCategory(null, news1);
      category1.setName("Category1");
      final ICategory category2 = fFactory.createCategory(null, news2);
      category2.setName("Category2");
      final boolean categoryAdded[] = new boolean[2];
      categoryListener = new CategoryAdapter() {
        @Override
        public void categoryAdded(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            ICategory category = event.getEntity();

            if (category.getName().equals(category1.getName()))
              categoryAdded[0] = true;

            else if (category.getName().equals(category2.getName()))
              categoryAdded[1] = true;

          }
        }
      };
      fModel.addCategoryListener(categoryListener);

      /* Save Feed */
      fDao.saveFeed(feed);

      /* Asserts Follow */
      assertTrue("Missed feedAdded Event in FeedListener!", feedAdded[0]);
      assertTrue("Missed newsReceived Event in FeedListener!", newsReceivedFromFeed[0]);

      for (int i = 0; i < newsReceived.length; i++) {
        if (!newsReceived[i])
          fail("Missed newsReceived Event in NewsListener!");
      }

      for (int i = 0; i < attachmentAdded.length; i++) {
        if (!attachmentAdded[i])
          fail("Missed attachmentAdded Event in AttachmentListener!");
      }

      for (int i = 0; i < personAdded.length; i++) {
        if (!personAdded[i])
          fail("Missed personAdded Event in PersonListener!");
      }

      for (int i = 0; i < categoryAdded.length; i++) {
        if (!categoryAdded[i])
          fail("Missed categoryAdded Event in CategoryListener!");
      }
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (feedListener != null)
        fModel.removeFeedListener(feedListener);
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
      if (personListener != null)
        fModel.removePersonListener(personListener);
      if (categoryListener != null)
        fModel.removeCategoryListener(categoryListener);
    }
  }

  /**
   * Test getting Events for a News being added with Attachments and other
   * types.
   *
   * @throws Exception
   */
  @Test
  public void testDeepNewsAddedEvents() throws Exception {
    NewsListener newsListener = null;
    AttachmentListener attachmentListener = null;
    PersonListener personListener = null;
    CategoryListener categoryListener = null;
    try {
      IFeed feed = fFactory.createFeed(null, new URL("http://www.foobar.com"));
      FeedReference feedReference = new FeedReference(fDao.saveFeed(feed).getId());

      /* Check News Added */
      final INews news = fFactory.createNews(null, feedReference.resolve(), new Date());
      news.setTitle("News Title");
      final boolean newsAdded[] = new boolean[1];
      newsListener = new NewsAdapter() {
        @Override
        public void newsAdded(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (event.getEntity().getTitle().equals(news.getTitle()))
              newsAdded[0] = true;

            try {
              assertEquals(new URL("http://www.foobar.com").toString(),
                  event.getEntity().getFeedReference().getLink().toString());
            } catch (MalformedURLException e) {
              fail(e.getMessage());
            }
          }
        }
      };
      fModel.addNewsListener(newsListener);

      /* Check Author Added */
      final IPerson person = fFactory.createPerson(null, news);
      person.setName("Person Name");
      final boolean personAdded[] = new boolean[1];
      personListener = new PersonAdapter() {
        @Override
        public void personAdded(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());
            if (event.getEntity().getName().equals(person.getName()))
              personAdded[0] = true;
          }
        }
      };
      fModel.addPersonListener(personListener);

      /* Check Attachments Added */
      final IAttachment attachment1 = fFactory.createAttachment(null, news);
      attachment1.setUrl(new URI("http://www.attachment1.com"));
      final IAttachment attachment2 = fFactory.createAttachment(null, news);
      attachment2.setUrl(new URI("http://www.attachment2.com"));
      final IAttachment attachment3 = fFactory.createAttachment(null, news);
      attachment3.setUrl(new URI("http://www.attachment3.com"));
      final boolean attachmentAdded[] = new boolean[3];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentAdded(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IAttachment attachment = event.getEntity();

            if (attachment.getUrl().equals(attachment1.getUrl()))
              attachmentAdded[0] = true;
            else if (attachment.getUrl().equals(attachment2.getUrl()))
              attachmentAdded[1] = true;
            else if (attachment.getUrl().equals(attachment3.getUrl()))
              attachmentAdded[2] = true;
          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);

      /* Check Category Added */
      final ICategory category = fFactory.createCategory(null, news);
      category.setName("Category 1");
      final boolean categoryAdded[] = new boolean[1];
      categoryListener = new CategoryAdapter() {
        @Override
        public void categoryAdded(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());
            if (event.getEntity().getName().equals(category.getName()))
              categoryAdded[0] = true;
          }
        }
      };
      fModel.addCategoryListener(categoryListener);

      fDao.saveNews(news);

      /* Asserts Follow */
      assertTrue("Missed newsReceived Event in NewsListener!", newsAdded[0]);
      assertTrue("Missed personAdded Event in PersonListener!", personAdded[0]);
      assertTrue("Missed categoryAdded Event in CategoryListener!", categoryAdded[0]);

      for (int i = 0; i < attachmentAdded.length; i++) {
        if (!attachmentAdded[i])
          fail("Missed attachmentAdded Event in AttachmentListener!");
      }
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
      if (personListener != null)
        fModel.removePersonListener(personListener);
      if (categoryListener != null)
        fModel.removeCategoryListener(categoryListener);
    }
  }
 
  /**
   * This test checks that deleting a feed, also deletes the news
   * an attachments associated with it.
   * @throws Exception
   */
  @Test
  public void testDeleteFeedNewsAndAttachment() throws Exception   {
    NewsListener newsListener = null;
    AttachmentListener attachmentListener = null;
    try {
      IFeed feed = fFactory.createFeed(null, new URL("http://www.foobar.com"));
      FeedReference feedReference = new FeedReference(fDao.saveFeed(feed).getId());

      /* Check News Added */
      final INews news = fFactory.createNews(null, feedReference.resolve(), new Date());
      news.setTitle("News Title");
      final IAttachment attachment0 = fFactory.createAttachment(null, news);
      attachment0.setUrl(new URI("http://www.attachment1.com"));
      final IAttachment attachment1 = fFactory.createAttachment(null, news);
      attachment1.setUrl(new URI("http://www.attachment1.com"));
      fDao.saveFeed(feed);
      NewsReference newsRef = new NewsReference(feed.getNews().get(0).getId());
      AttachmentReference attachmentRef0 = new AttachmentReference(news.getAttachments().get(0).getId());
      AttachmentReference attachmentRef1 = new AttachmentReference(news.getAttachments().get(1).getId());
     
      final boolean[] newsDeleted = new boolean[1];
      newsListener = new NewsListener() {
        public void newsDeleted(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());
            if (event.getEntity().getTitle().equals(news.getTitle()))
              newsDeleted[0] = true;

            try {
              assertEquals(new URL("http://www.foobar.com").toString(),
                  event.getEntity().getFeedReference().getLink().toString());
            } catch (MalformedURLException e) {
              fail(e.getMessage());
            }
          }
        }
        public void newsUpdated(Set<NewsEvent> events) {
          fail("Unexpected event");
        }
        public void newsAdded(Set<NewsEvent> events) {
          fail("Unexpected event");
        }
      };
      fModel.addNewsListener(newsListener);

      /* Check Attachments Added */
      final boolean attachmentDeleted[] = new boolean[2];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentDeleted(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IAttachment attachment = event.getEntity();

            if (attachment.getUrl().equals(attachment0.getUrl()))
              attachmentDeleted[0] = true;
            if (attachment.getUrl().equals(attachment1.getUrl()))
              attachmentDeleted[1] = true;
          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);

      fDao.deleteFeed(new FeedReference(feed.getId()));
      assertNull(fDao.loadFeed(feed.getId()));
      assertNull(fDao.loadNews(newsRef.getId()));
      assertNull(fDao.loadAttachment(attachmentRef0.getId()));
      assertNull(fDao.loadAttachment(attachmentRef1.getId()));
     
      assertTrue("Missed newsDeleted Event in NewsListener!", newsDeleted[0]);
      assertTrue("Missed attachmentDeleted Event in PersonListener!", attachmentDeleted[0]);
      assertTrue("Missed attachmentDeleted Event in PersonListener!", attachmentDeleted[1]);
    } finally {
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
    }
  }
 
  /**
   *
   * @throws Exception
   */
  @Test
  public void testDeleteAttachmentFiresNewsUpdatedEvent() throws Exception {
    NewsListener newsListener = null;
    AttachmentListener attachmentListener = null;
    try {
      IFeed feed = fFactory.createFeed(null, new URL("http://www.foobar.com"));
      FeedReference feedReference = new FeedReference(fDao.saveFeed(feed).getId());

      /* Check News Added */
      final INews news = fFactory.createNews(null, feedReference.resolve(), new Date());
      news.setTitle("News Title");
      final IAttachment attachment1 = fFactory.createAttachment(null, news);
      attachment1.setUrl(new URI("http://www.attachment1.com"));
      fDao.saveNews(news);

      final boolean[] newsUpdated = new boolean[1];
      newsListener = new NewsAdapter() {
        @Override
        public void newsUpdated(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());
            if (event.getEntity().getTitle().equals(news.getTitle()))
              newsUpdated[0] = true;

            try {
              assertEquals(new URL("http://www.foobar.com").toString(),
                  event.getEntity().getFeedReference().getLink().toString());
            } catch (MalformedURLException e) {
              fail(e.getMessage());
            }
          }
        }
      };
      fModel.addNewsListener(newsListener);

      /* Check Attachments Added */
      final boolean attachmentDeleted[] = new boolean[1];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentDeleted(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());

            IAttachment attachment = event.getEntity();

            if (attachment.getUrl().equals(attachment1.getUrl()))
              attachmentDeleted[0] = true;
          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);

      AttachmentReference attachmentRef = new AttachmentReference(news.getAttachments().get(0).getId());
      fDao.deleteAttachment(attachmentRef);

      assertNull(fDao.loadAttachment(attachmentRef.getId()));
      /* Asserts Follow */
      assertTrue("Missed newsUpdated Event in NewsListener!", newsUpdated[0]);
      assertTrue("Missed attachmentDeleted Event in PersonListener!", attachmentDeleted[0]);
    } finally {
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
    }
  }

  /**
   * Test getting Events for a Folder being deleted with sub-folders and some
   * BookMarks and SearchMarks as Childs.
   *
   * @throws Exception
   */
  @Test
  public void testDeepFolderDeletedEvents() throws Exception {
    FolderListener folderListener = null;
    BookMarkListener bookMarkListener = null;
    FeedListener feedListener = null;
    SearchMarkListener searchMarkListener = null;
    SearchConditionListener searchConditionListener = null;
    try {
      /* Check Folder Deleted */
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);
      final FolderReference rootRef = new FolderReference(root.getId());
      fFactory.createFolder(null, root, "Root Child");
      root = fDao.saveFolder(root);
      IFolder rootChild = root.getFolders().get(0);
      fFactory.createFolder(null, rootChild, "Root Child Child #1");
      rootChild = fDao.saveFolder(rootChild);
      FolderReference rootChildRef = new FolderReference(rootChild.getId());
      IFolder rootChildChild1 = rootChild.getFolders().get(0);
      FolderReference rootChildChild1Ref = new FolderReference(rootChildChild1.getId());
      fFactory.createFolder(null, rootChild, "Root Child Child #2");
      rootChild = fDao.saveFolder(rootChild);
      IFolder rootChildChild2 = rootChild.getFolders().get(1);
      FolderReference rootChildChild2Ref = new FolderReference(rootChildChild2.getId());
      fFactory.createFolder(null, rootChildChild1, "Root Child Child #1 Child");
      rootChildChild1 = fDao.saveFolder(rootChildChild1);
      IFolder rootChildChild1Child = rootChildChild1.getFolders().get(0);
      FolderReference rootChildChild1ChildRef = new FolderReference(rootChildChild1Child.getId());

      final boolean[] folderEventsIssued = new boolean[5];
      final FolderReference[] folderReferences = new FolderReference[] { rootRef, rootChildRef, rootChildChild1Ref, rootChildChild2Ref, rootChildChild1ChildRef };

      folderListener = new FolderAdapter() {
        @Override
        public void folderDeleted(Set<FolderEvent> events) {
          for (FolderEvent event : events) {
            IFolder folder = event.getEntity();

            if (rootRef.references(folder))
              assertEquals(true, event.isRoot());

            for (int i = 0; i < folderReferences.length; i++)
              if (folderReferences[i].references(folder))
                folderEventsIssued[i] = true;
          }
        }
      };
      fModel.addFolderListener(folderListener);

      /* Check BookMark Deleted */
      final IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      final FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      IBookMark bookMark1 = fFactory.createBookMark(null, rootRef.resolve(),
          feed.getLink(), feedRef, "Root BookMark");
      root = fDao.saveFolder(bookMark1.getFolder());
      final BookMarkReference bookMarkRef1 = new BookMarkReference(root.getMarks().get(0).getId());

      IBookMark bookMark2 = fFactory.createBookMark(null, rootChildRef.resolve(),
          feed.getLink(), feedRef, "Root Child BookMark");
      rootChild = fDao.saveFolder(bookMark2.getFolder());
      final BookMarkReference bookMarkRef2 = new BookMarkReference(rootChild.getMarks().get(0).getId());

      IBookMark bookMark3 = fFactory.createBookMark(null, rootChildChild1Ref.resolve(),
          feed.getLink(), feedRef, "Root Child Child #1 BookMark");
      rootChildChild1 = fDao.saveFolder(bookMark3.getFolder());
      final BookMarkReference bookMarkRef3 = new BookMarkReference(rootChildChild1.getMarks().get(0).getId());

      final boolean bookMarkEventsIssued[] = new boolean[3];
      final boolean feedEventsIssued[] = new boolean[1];

      bookMarkListener = new BookMarkAdapter() {
        @Override
        public void bookMarkDeleted(Set<BookMarkEvent> events) {
          for (BookMarkEvent event : events) {
            IBookMark bookMark = event.getEntity();

            if (bookMarkRef1.references(bookMark))
              bookMarkEventsIssued[0] = true;

            else if (bookMarkRef2.references(bookMark))
              bookMarkEventsIssued[1] = true;

            else if (bookMarkRef3.references(bookMark))
              bookMarkEventsIssued[2] = true;
          }
        }
      };
      fModel.addBookMarkListener(bookMarkListener);

      feedListener = new FeedAdapter() {
        @Override
        public void feedDeleted(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertFalse("Already received feed deleted event!", feedEventsIssued[0]);
            if (feedRef.references(event.getEntity()))
              feedEventsIssued[0] = true;
          }
        }
      };
      fModel.addFeedListener(feedListener);

      /* Check SearchMark Deleted */
      ISearchMark searchMark1 = fFactory.createSearchMark(null, rootRef.resolve(), "Root SearchMark");
      root = fDao.saveFolder(searchMark1.getFolder());
      searchMark1 = (ISearchMark) root.getMarks().get(root.getMarks().size() - 1);
      final SearchMarkReference searchMarkRef1 = new SearchMarkReference(searchMark1.getId());

      ISearchMark searchMark2 = fFactory.createSearchMark(null, rootChildRef.resolve(), "Root Child SearchMark");
      rootChild = fDao.saveFolder(searchMark2.getFolder());
      searchMark2 = (ISearchMark) rootChild.getMarks().get(rootChild.getMarks().size() - 1);
      final SearchMarkReference searchMarkRef2 = new SearchMarkReference(searchMark2.getId());

      ISearchMark searchMark3 = fFactory.createSearchMark(null, rootChildChild1ChildRef.resolve(), "Root Child Child #1 Child SearchMark");
      rootChildChild1Child = fDao.saveFolder(searchMark3.getFolder());
      searchMark3 = (ISearchMark) rootChildChild1Child.getMarks().get(rootChildChild1Child.getMarks().size() - 1);
      final SearchMarkReference searchMarkRef3 = new SearchMarkReference(searchMark3.getId());

      final boolean searchMarkEventsIssued[] = new boolean[3];

      searchMarkListener = new SearchMarkAdapter() {
        @Override
        public void searchMarkDeleted(Set<SearchMarkEvent> events) {
          for (SearchMarkEvent event : events) {
            ISearchMark searchMark = event.getEntity();

            if (searchMarkRef1.references(searchMark))
              searchMarkEventsIssued[0] = true;

            else if (searchMarkRef2.references(searchMark))
              searchMarkEventsIssued[1] = true;

            else if (searchMarkRef3.references(searchMark))
              searchMarkEventsIssued[2] = true;
          }
        }
      };
      fModel.addSearchMarkListener(searchMarkListener);

      /* Check SearchCondition Deleted */
      ISearchField field1 = fFactory.createSearchField(IExtendableType.ALL_FIELDS, INews.class);

      fFactory.createSearchCondition(null, searchMark1, field1, SearchSpecifier.CONTAINS, "Foo", true);
      searchMark1 = fDao.saveSearchMark(searchMark1);
      final SearchConditionReference conditionRef1 = new SearchConditionReference(searchMark1.getSearchConditions().get(0).getId());

      fFactory.createSearchCondition(null, searchMark2, field1, SearchSpecifier.IS, "Bar", true);
      searchMark2 = fDao.saveSearchMark(searchMark2);
      final SearchConditionReference conditionRef2 = new SearchConditionReference(searchMark2.getSearchConditions().get(0).getId());

      fFactory.createSearchCondition(null, searchMark3, field1, SearchSpecifier.IS_NOT, "Foo Bar", false);
      searchMark3 = fDao.saveSearchMark(searchMark3);
      final SearchConditionReference conditionRef3 = new SearchConditionReference(searchMark3.getSearchConditions().get(0).getId());

      final boolean searchConditionEventsIssued[] = new boolean[3];

      searchConditionListener = new SearchConditionAdapter() {
        @Override
        public void searchConditionDeleted(Set<SearchConditionEvent> events) {
          for (SearchConditionEvent event : events) {
            ISearchCondition searchCondition = event.getEntity();

            if (conditionRef1.references(searchCondition))
              searchConditionEventsIssued[0] = true;

            else if (conditionRef2.references(searchCondition))
              searchConditionEventsIssued[1] = true;

            else if (conditionRef3.references(searchCondition))
              searchConditionEventsIssued[2] = true;
          }
        }
      };
      fModel.addSearchConditionListener(searchConditionListener);

      /* Delete Root Folder */
      fDao.deleteFolder(rootRef);

      /* Asserts Follow */
      assertEquals(0, fAppLayer.loadRootFolders().size());
      assertEquals(0, fAppLayer.loadAllBookMarks(false).size());
      assertTrue("Expected one feedDeleted event", feedEventsIssued[0]);
      for (int i = 0; i < folderEventsIssued.length; i++) {
        if (!folderEventsIssued[i])
          fail("Expected five folderDeleted events!");
      }

      for (int i = 0; i < bookMarkEventsIssued.length; i++) {
        if (!bookMarkEventsIssued[i])
          fail("Expected three bookMarkDeleted events!");
      }

      for (int i = 0; i < searchMarkEventsIssued.length; i++) {
        if (!searchMarkEventsIssued[i])
          fail("Expected three searchMarkDeleted events!");
      }

      for (int i = 0; i < searchConditionEventsIssued.length; i++) {
        if (!searchConditionEventsIssued[i])
          fail("Expected three searchConditionDeleted events!");
      }
    } finally {
      if (folderListener != null)
        fModel.removeFolderListener(folderListener);
      if (bookMarkListener != null)
        fModel.removeBookMarkListener(bookMarkListener);
      if (searchMarkListener != null)
        fModel.removeSearchMarkListener(searchMarkListener);
      if (feedListener != null)
        fModel.removeFeedListener(feedListener);
      if (searchConditionListener != null)
        fModel.removeSearchConditionListener(searchConditionListener);
    }
  }

  /**
   * Test getting Events for a Feed being deleted with News and Attachments and
   * other types.
   *
   * @throws Exception
   */
  @Test
  public void testDeepFeedDeletedEvents() throws Exception {
    FeedListener feedListener = null;
    NewsListener newsListener = null;
    AttachmentListener attachmentListener = null;
    PersonListener personListener = null;
    CategoryListener categoryListener = null;
    NewsAdapter newsAdapter = null;
    try {

      /* Check Feed Deleted and News Deleted */
      final IFeed feed = fFactory.createFeed(null, new URL("http://www.foobar.com"));
      final IPerson person1 = fFactory.createPerson(null, feed);
      person1.setName("Person1");
      final FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());
      final PersonReference personRef1 = new PersonReference(feedRef.resolve().getAuthor().getId());
      final boolean feedDeleted[] = new boolean[1];
      final boolean newsDeletedFromFeed[] = new boolean[1];
      feedListener = new FeedAdapter() {
        @Override
        public void feedDeleted(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertFalse("Already received feedDeleted Event", feedDeleted[0]);
            assertTrue("Expected this Event to be Root Event", event.isRoot());

            if (feedRef.references(event.getEntity()))
              feedDeleted[0] = true;
          }
        }
      };
      fModel.addFeedListener(feedListener);

      /* Check News Deleted */
      final INews news1 = fFactory.createNews(null, feedRef.resolve(), new Date());
      news1.setTitle("News1 Title");
      news1.setLink(new URI("http://www.news.com/news1.html"));
      final IPerson person2 = fFactory.createPerson(null, news1);
      person2.setName("Person2");
      fFactory.createSource(news1).setUrl(new URI("http://www.source1.com"));
      fFactory.createGuid(news1, "Guid1");

      final NewsReference[] newsRef = new NewsReference[1];
      newsAdapter = new NewsAdapter() {
        @Override
        public void newsAdded(Set<NewsEvent> events) {
          assertEquals(1, events.size());
          newsRef[0] = new NewsReference(events.iterator().next().getEntity().getId());
        }
      };
      fModel.addNewsListener(newsAdapter);
      /* Must save parent because it gets changed during creation of news */
      fDao.saveFeed(feedRef.resolve());
      final NewsReference newsRef1 = newsRef[0];
      final PersonReference personRef2 = new PersonReference(newsRef1.resolve().getAuthor().getId());

      final INews news2 = fFactory.createNews(null, feedRef.resolve(), new Date());
      news2.setTitle("News2 Title");
      news2.setLink(new URI("http://www.news.com/news2.html"));
      fFactory.createSource(news2).setUrl(new URI("http://www.source2.com"));
      fFactory.createGuid(news2, "Guid2");
      /* Must save parent because it gets changed during creation of news */
      fDao.saveFeed(feedRef.resolve());
      final NewsReference newsRef2 = newsRef[0];

      final INews news3 = fFactory.createNews(null, feedRef.resolve(), new Date());
      news3.setTitle("News3 Title");
      news3.setLink(new URI("http://www.news.com/news3.html"));
      fFactory.createSource(news3).setUrl(new URI("http://www.source3.com"));
      fFactory.createGuid(news3, "Guid3");
      /* Must save parent because it gets changed during creation of news */
      fDao.saveFeed(feedRef.resolve());
      final NewsReference newsRef3 = newsRef[0];

      final boolean newsDeleted[] = new boolean[3];
      newsListener = new NewsAdapter() {
        @Override
        public void newsDeleted(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            NewsReference newsRef = new NewsReference(event.getEntity().getId());

            if (newsRef.equals(newsRef1))
              newsDeleted[0] = true;

            else if (newsRef.equals(newsRef2))
              newsDeleted[1] = true;

            else if (newsRef.equals(newsRef3))
              newsDeleted[2] = true;
          }

          if (events.size() == 3)
            newsDeletedFromFeed[0] = true;
        }
      };
      fModel.addNewsListener(newsListener);

      /* Check Attachment Deleted */
      final IAttachment attachment1 = fFactory.createAttachment(null, newsRef1.resolve());
      final AttachmentReference attachmentRef1 = new AttachmentReference(fDao.saveAttachment(attachment1).getId());
      final IAttachment attachment2 = fFactory.createAttachment(null, newsRef2.resolve());
      final AttachmentReference attachmentRef2 = new AttachmentReference(fDao.saveAttachment(attachment2).getId());
      final IAttachment attachment3 = fFactory.createAttachment(null, newsRef3.resolve());
      final AttachmentReference attachmentRef3 = new AttachmentReference(fDao.saveAttachment(attachment3).getId());

      final boolean attachmentDeleted[] = new boolean[3];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentDeleted(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IAttachment attachment = event.getEntity();

            if (attachmentRef1.references(attachment))
              attachmentDeleted[0] = true;

            else if (attachmentRef2.references(attachment))
              attachmentDeleted[1] = true;

            else if (attachmentRef3.references(attachment))
              attachmentDeleted[2] = true;
          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);

      /* Check Person Deleted */
      final boolean personDeleted[] = new boolean[2];
      personListener = new PersonAdapter() {
        @Override
        public void personDeleted(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IPerson person = event.getEntity();
            if (personRef1.references(person))
              personDeleted[0] = true;

            else if (personRef2.references(person))
              personDeleted[1] = true;
          }
        }
      };
      fModel.addPersonListener(personListener);

      /* Check Category Deleted */
      final ICategory category1 = fFactory.createCategory(null, newsRef1.resolve());
      final CategoryReference categoryRef1 = new CategoryReference(fDao.saveCategory(category1).getId());
      final ICategory category2 = fFactory.createCategory(null, newsRef2.resolve());
      final CategoryReference categoryRef2 = new CategoryReference(fDao.saveCategory(category2).getId());

      final boolean categoryDeleted[] = new boolean[2];
      categoryListener = new CategoryAdapter() {
        @Override
        public void categoryDeleted(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            ICategory category = event.getEntity();

            if (categoryRef1.references(category))
              categoryDeleted[0] = true;

            else if (categoryRef2.references(category))
              categoryDeleted[1] = true;
          }
        }
      };
      fModel.addCategoryListener(categoryListener);

      /* Delete Feed */
      fDao.deleteFeed(feedRef);

      /* Asserts Follow */
      assertTrue("Missed feedDeleted Event in FeedListener!", feedDeleted[0]);
      assertTrue("Set<NewsEvent> in feedDeleted of FeedListener did not contain 3 News-References!", newsDeletedFromFeed[0]);

      for (int i = 0; i < newsDeleted.length; i++) {
        if (!newsDeleted[i])
          fail("Missed newsDeleted Event in NewsListener!");
      }

      for (int i = 0; i < attachmentDeleted.length; i++) {
        if (!attachmentDeleted[i])
          fail("Missed attachmentDeleted Event in AttachmentListener!");
      }

      for (int i = 0; i < personDeleted.length; i++) {
        if (!personDeleted[i])
          fail("Missed personDeleted Event in PersonListener!");
      }

      for (int i = 0; i < categoryDeleted.length; i++) {
        if (!categoryDeleted[i])
          fail("Missed categoryDeleted Event in CategoryListener!");
      }
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (newsAdapter != null)
        fModel.removeNewsListener(newsAdapter);
      if (feedListener != null)
        fModel.removeFeedListener(feedListener);
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
      if (personListener != null)
        fModel.removePersonListener(personListener);
      if (categoryListener != null)
        fModel.removeCategoryListener(categoryListener);
    }
  }

  /**
   * Test getting Events for a News being deleted with Attachments and other
   * types.
   *
   * @throws Exception
   */
  @Test
  public void testDeepNewsDeletedEvents() throws Exception {
    NewsListener newsListener = null;
    AttachmentListener attachmentListener = null;
    PersonListener personListener = null;
    CategoryListener categoryListener = null;
    try {

      /* Store a Feed */
      IFeed feed = fFactory.createFeed(null, new URL("http://www.foobar.com"));
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      /* Create a News */
      final INews news = fFactory.createNews(null, feedRef.resolve(), new Date());
      news.setTitle("News Title");
      final IPerson person = fFactory.createPerson(null, news);
      person.setName("Person Name");
      fFactory.createCategory(null, news);
      fFactory.createSource(news);
      fFactory.createGuid(news, "Guid Value");
      fFactory.createAttachment(null, news);
      fFactory.createAttachment(null, news);
      fFactory.createAttachment(null, news);

      /* Save News */
      final NewsReference newsRef = new NewsReference(fDao.saveNews(news).getId());

      final PersonReference personRef = new PersonReference(fDao.loadNews(newsRef.getId()).getAuthor().getId());
      final AttachmentReference attachmentRef1 = new AttachmentReference(fDao.loadNews(newsRef.getId()).getAttachments().get(0).getId());
      final AttachmentReference attachmentRef2 = new AttachmentReference(fDao.loadNews(newsRef.getId()).getAttachments().get(1).getId());
      final AttachmentReference attachmentRef3 = new AttachmentReference(fDao.loadNews(newsRef.getId()).getAttachments().get(2).getId());
      final CategoryReference categoryRef = new CategoryReference(fDao.loadNews(newsRef.getId()).getCategories().get(0).getId());

      /* Check News Deleted */
      final boolean newsDeleted[] = new boolean[1];
      newsListener = new NewsAdapter() {
        @Override
        public void newsDeleted(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (newsRef.references(event.getEntity()))
              newsDeleted[0] = true;

            try {
              assertEquals(new URL("http://www.foobar.com").toString(),
                  event.getEntity().getFeedReference().getLink().toString());
            } catch (MalformedURLException e) {
              fail(e.getMessage());
            }
          }
        }
      };
      fModel.addNewsListener(newsListener);

      /* Check Author Deleted */
      final boolean personDeleted[] = new boolean[1];
      personListener = new PersonAdapter() {
        @Override
        public void personDeleted(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());
            if (personRef.references(event.getEntity()))
              personDeleted[0] = true;
          }
        }
      };
      fModel.addPersonListener(personListener);

      /* Check Attachments Deleted */
      final boolean attachmentDeleted[] = new boolean[3];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentDeleted(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());

            IAttachment attachment = event.getEntity();

            if (attachmentRef1.references(attachment))
              attachmentDeleted[0] = true;
            else if (attachmentRef2.references(attachment))
              attachmentDeleted[1] = true;
            else if (attachmentRef3.references(attachment))
              attachmentDeleted[2] = true;
          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);

      /* Check Category Deleted */
      final boolean categoryDeleted[] = new boolean[1];
      categoryListener = new CategoryAdapter() {
        @Override
        public void categoryDeleted(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertFalse("Expected this Event to be no Root Event", event.isRoot());
            if (categoryRef.references(event.getEntity()))
              categoryDeleted[0] = true;
          }
        }
      };
      fModel.addCategoryListener(categoryListener);

      /* Delete News */
      fDao.deleteNews(newsRef);

      /* Asserts Follow */
      assertTrue("Missed newsDeleted Event in NewsListener!", newsDeleted[0]);
      assertTrue("Missed personDeleted Event in PersonListener!", personDeleted[0]);
      assertTrue("Missed categoryDeleted Event in CategoryListener!", categoryDeleted[0]);

      for (int i = 0; i < attachmentDeleted.length; i++) {
        if (!attachmentDeleted[i])
          fail("Missed attachmentDeleted Event in AttachmentListener!");
      }
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
      if (personListener != null)
        fModel.removePersonListener(personListener);
      if (categoryListener != null)
        fModel.removeCategoryListener(categoryListener);
    }
  }

  private IFeed createFeed(String url) throws MalformedURLException {
    return fFactory.createFeed(null, new URL(url));
  }

  /**
   * Tests {@link INews#merge(INews)}. Particularly: - If the state of both
   * news is different, it's changed to the new state with one exception: if the
   * second News is in the NEW state, the state of the first news won't be
   * changed.
   *
   * @throws Exception
   */
  @Test
  public void testNewsStateMerge() throws Exception {
    /* Initial Add News */
    String url = "http://www.feed-case1.com";
    IFeed feed = createFeed(url);
    final String newsTitle = "News Title Case_1";
    fFactory.createNews(null, feed, new Date()).setTitle(newsTitle);
    FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

    /*
     * Recreate the feed because the existing one got changed when it was saved
     */
    feed = createFeed(url);

    /* a) Different publish date and news to be merged is in NEW state */
    INews news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());

    IFeed mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

    /* b) Different publish date and news to be merged is in DELETED state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.DELETED);

    mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.DELETED, feedRef.resolve().getNews().get(0).getState());

    /* c) Different publish date and news to be merged is in HIDDEN state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.HIDDEN);

    mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.HIDDEN, feedRef.resolve().getNews().get(0).getState());

    /* d) Different publish date and news to be merged is in READ state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.READ);

    mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());

    /* e) Different publish date and news to be merged is in UNREAD state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.UNREAD);

    mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.UNREAD, feedRef.resolve().getNews().get(0).getState());

    /* f) Different publish date and news to be merged is in UPDATED state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.UPDATED);

    mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());

    /*
     * g) Different publish date and news to be merged is in NEW state, but
     * existing News is in UPDATED state
     */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.NEW);

    mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());
  }

  /**
   * Tests {@link INews#merge(INews)}. Particularly: - The state does not
   * change to UPDATED if both news have the same state and it is NEW, DELETED
   * or HIDDEN.
   *
   * @throws Exception
   */
  @Test
  public void testNewsStateMerge2() throws Exception {
    /* Initial Add News */
    String url = "http://www.feed-case1.com";
    IFeed feed = createFeed(url);
    final String newsTitle = "News Title Case_1";
    fFactory.createNews(null, feed, new Date()).setTitle(newsTitle);
    FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

    /*
     * Recreate the feed because the existing one got changed when it was saved
     */
    feed = createFeed(url);

    /* a) Different publish date and both news are in NEW state */
    INews news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());

    IFeed mergedFeed = feedRef.resolve().merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

    /* b) Different publish date and both news are in DELETED state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.DELETED);

    IFeed dbFeed = feedRef.resolve();
    dbFeed.getNews().get(0).setState(State.DELETED);
    mergedFeed = dbFeed.merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.DELETED, feedRef.resolve().getNews().get(0).getState());

    /* c) Different publish date and both news are in HIDDEN state */
    feed = createFeed(url);
    news = fFactory.createNews(null, feed, new Date());
    news.setTitle(newsTitle);
    news.setPublishDate(new Date());
    news.setState(State.HIDDEN);

    dbFeed = feedRef.resolve();
    dbFeed.getNews().get(0).setState(State.HIDDEN);
    mergedFeed = dbFeed.merge(feed);
    feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
    assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
    assertEquals("Existing News State changed unexpectedly!", INews.State.HIDDEN, feedRef.resolve().getNews().get(0).getState());
  }

  /**
   * Test all cases of a News being added to the DB, which is already present.
   * Check all possible combinatios that include description.
   *
   * @throws Exception
   */
  @Test
  public void testNewsAddedUpdatedWithDescription() throws Exception  {
    String description = "Initial description";
    /* News with Title and Description */
    {
      /* Initial Add News */
      long time = System.currentTimeMillis();
      String url = "http://www.feed-case4.com";
      IFeed feed = createFeed(url);
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_4");
      news.setPublishDate(new Date(time));
      news.setDescription(description);
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      /* Mark News Read */
      news = feedRef.resolve().getNews().get(0);
      news.setState(INews.State.READ);
      fDao.saveNews(news);

      /* b) Add the same News with updated Description */
      feed = createFeed(url);
      news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_4");
      news.setPublishDate(new Date(time));
      news.setDescription(description + "updated");
      IFeed mergedFeed = feedRef.resolve().merge(feed);
      feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
      assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
      assertEquals("Existing News State is not READ!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());
    }
   
    /* News with Title, URL and Description */
    {
      /* Initial Add News */
      long time = System.currentTimeMillis();
      String url = "http://www.feed-case5.com";
      IFeed feed = createFeed(url);
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_5");
      news.setLink(new URI("http://www.news-case5.com/index.html"));
      news.setPublishDate(new Date(time));
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      /* Mark News Read */
      news = feedRef.resolve().getNews().get(0);
      news.setState(INews.State.READ);
      fDao.saveNews(news);

      /* c) Add the same News with updated description */
      feed = createFeed(url);
      news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_5 Updated");
      news.setLink(new URI("http://www.news-case5.com/index.html"));
      news.setPublishDate(new Date(time));
      news.setDescription(description + "updated#2");
      IFeed mergedFeed = feedRef.resolve().merge(feed);
      feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
      assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
      assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());
    }

    /* News with Title, Guid and Description */
    {
      /* Initial Add News */
      long time = System.currentTimeMillis();
      String url = "http://www.feed-case6.com";
      IFeed feed = createFeed(url);
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_6");
      news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid"));
      news.setPublishDate(new Date(time));
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      /* Mark News Read */
      news = feedRef.resolve().getNews().get(0);
      news.setState(INews.State.READ);
      fDao.saveNews(news);

      /* d) Add the same News with updated description */
      feed = createFeed(url);
      news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_6");
      news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid"));
      news.setDescription(description + "updated#3");
      IFeed mergedFeed = feedRef.resolve().merge(feed);
      feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
      assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
      assertEquals("Existing News State is not READ!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());
    }
   
    /* News with Title, URL, Guid and Description */
    {
      /* Initial Add News */
      long time = System.currentTimeMillis();
      String url = "http://www.feed-case8.com";
      IFeed feed = createFeed(url);
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_8");
      news.setLink(new URI("http://www.news-case8.com/index.html"));
      news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid"));
      news.setPublishDate(new Date(time));
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      /* Mark News Read */
      news = feedRef.resolve().getNews().get(0);
      news.setState(INews.State.READ);
      fDao.saveNews(news);

      /* d) Add the same News with updated Publish Date */
      feed = createFeed(url);
      news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News Title Case_8");
      news.setLink(new URI("http://www.news-case8.com/index.html"));
      news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid"));
      news.setPublishDate(new Date(System.currentTimeMillis() + 1000));
      news.setDescription(description + "updated#4");
      IFeed mergedFeed = feedRef.resolve().merge(feed);
      feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
      assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
      assertEquals("Existing News State is not READ!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());
    }
  }
 
  /**
   * Test all cases of a News being added to the DB, which is already present.
   * Check all possible combinatios of a News containing Title, URL, Guid,
   * PublishDate.
   *
   * @throws Exception
   */
  @Test
  public void testNewsAddedUpdated() throws Exception {
    try {

      /* Case 1: News with Title */
      {
        /* Initial Add News */
        String url = "http://www.feed-case1.com";
        IFeed feed = createFeed(url);
        fFactory.createNews(null, feed, new Date()).setTitle("News Title Case_1");
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /*
         * Recreate the feed because the existing one got changed when it was
         * saved
         */
        feed = createFeed(url);

        /* a) Add the same News */
        fFactory.createNews(null, feed, new Date()).setTitle("News Title Case_1");
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());
      }

      /* Case 2: News with Title and URL */
      {
        /* Initial Add News */
        String url = "http://www.feed-case2.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_2");
        news.setLink(new URI("http://www.news-case2.com/index.html"));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_2");
        news.setLink(new URI("http://www.news-case2.com/index.html"));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Title */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_2 Updated");
        news.setLink(new URI("http://www.news-case2.com/index.html"));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());
      }

      /* Case 3: News with Title and Guid */
      {
        /* Initial Add News */
        String url = "http://www.feed-case3.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_3");
        news.setGuid(fFactory.createGuid(news, "News_Case_3_Guid"));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_3");
        news.setGuid(fFactory.createGuid(news, "News_Case_3_Guid"));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Title */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_3 Updated");
        news.setGuid(fFactory.createGuid(news, "News_Case_3_Guid"));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());
      }

      /* Case 4: News with Title and Publish Date */
      {
        /* Initial Add News */
        long time = System.currentTimeMillis();
        String url = "http://www.feed-case4.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_4");
        news.setPublishDate(new Date(time));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_4");
        news.setPublishDate(new Date(time));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Publish Date */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_4");
        news.setPublishDate(new Date(System.currentTimeMillis() + 1000));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not READ!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());
      }

      /* Case 5: News with Title, URL and Publish Date */
      {
        /* Initial Add News */
        long time = System.currentTimeMillis();
        String url = "http://www.feed-case5.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_5");
        news.setLink(new URI("http://www.news-case5.com/index.html"));
        news.setPublishDate(new Date(time));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_5");
        news.setLink(new URI("http://www.news-case5.com/index.html"));
        news.setPublishDate(new Date(time));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Title */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_5 Updated");
        news.setLink(new URI("http://www.news-case5.com/index.html"));
        news.setPublishDate(new Date(time));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* c) Add the same News with updated Publish Date */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_5 Updated");
        news.setLink(new URI("http://www.news-case5.com/index.html"));
        news.setPublishDate(new Date(System.currentTimeMillis() + 1000));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not READ!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());
      }

      /* Case 6: News with Title, Guid and Publish Date */
      {
        /* Initial Add News */
        long time = System.currentTimeMillis();
        String url = "http://www.feed-case6.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_6");
        news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid"));
        news.setPublishDate(new Date(time));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_6");
        news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid"));
        news.setPublishDate(new Date(time));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Title */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_6 Updated");
        news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid"));
        news.setPublishDate(new Date(time));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());

        /* c) Add the same News with updated Guid */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_6");
        news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid_Updated"));
        news.setPublishDate(new Date(time));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Expected two News in this Feed!", 2, feedRef.resolve().getNews().size());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* d) Add the same News with updated Publish Date */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_6");
        news.setGuid(fFactory.createGuid(news, "News_Case_6_Guid"));
        news.setPublishDate(new Date(time + 1000));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 2, feedRef.resolve().getNews().size());

        List<INews> newsRefs = feedRef.resolve().getNews();
        for (INews newsRef : newsRefs) {
          if ("News_Case_6_Guid".equals(newsRef.getGuid().getValue()))
            assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, newsRef.getState());
        }
      }

      /* Case 7: News with Title, URL and Guid */
      {
        /* Initial Add News */
        String url = "http://www.feed-case7.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_7");
        news.setGuid(fFactory.createGuid(news, "News_Case_7_Guid"));
        news.setLink(new URI("http://www.news-case7.com/index.html"));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_7");
        news.setGuid(fFactory.createGuid(news, "News_Case_7_Guid"));
        news.setLink(new URI("http://www.news-case7.com/index.html"));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Title */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_7 Updated");
        news.setGuid(fFactory.createGuid(news, "News_Case_7_Guid"));
        news.setLink(new URI("http://www.news-case7.com/index.html"));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* c) Add the same News with updated URL */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_7 Updated");
        news.setGuid(fFactory.createGuid(news, "News_Case_7_Guid"));
        news.setLink(new URI("http://www.news-case7.com/index-updated.html"));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not READ!", INews.State.READ, feedRef.resolve().getNews().get(0).getState());

        /* d) Add the same News with updated Guid */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_7");
        news.setGuid(fFactory.createGuid(news, "News_Case_7_Guid_Updated"));
        news.setLink(new URI("http://www.news-case7.com/index.html"));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Expected two News in this Feed!", 2, feedRef.resolve().getNews().size());
      }

      /* Case 8: News with Title, URL, Guid and Publish Date */
      {
        /* Initial Add News */
        long time = System.currentTimeMillis();
        String url = "http://www.feed-case8.com";
        IFeed feed = createFeed(url);
        INews news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_8");
        news.setLink(new URI("http://www.news-case8.com/index.html"));
        news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid"));
        news.setPublishDate(new Date(time));
        FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

        /* a) Add the same News */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_8");
        news.setLink(new URI("http://www.news-case8.com/index.html"));
        news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid"));
        news.setPublishDate(new Date(time));
        IFeed mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State changed unexpectedly!", INews.State.NEW, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* b) Add the same News with updated Title */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_8 Updated");
        news.setLink(new URI("http://www.news-case8.com/index.html"));
        news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid"));
        news.setPublishDate(new Date(time));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 1, feedRef.resolve().getNews().size());
        assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, feedRef.resolve().getNews().get(0).getState());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* c) Add the same News with updated Guid */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_8");
        news.setLink(new URI("http://www.news-case8.com/index.html"));
        news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid_Updated"));
        news.setPublishDate(new Date(time));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Expected two News in this Feed!", 2, feedRef.resolve().getNews().size());

        /* Mark News Read */
        news = feedRef.resolve().getNews().get(0);
        news.setState(INews.State.READ);
        fDao.saveNews(news);

        /* d) Add the same News with updated Publish Date */
        feed = createFeed(url);
        news = fFactory.createNews(null, feed, new Date());
        news.setTitle("News Title Case_8");
        news.setLink(new URI("http://www.news-case8.com/index.html"));
        news.setGuid(fFactory.createGuid(news, "News_Case_8_Guid"));
        news.setPublishDate(new Date(System.currentTimeMillis() + 1000));
        mergedFeed = feedRef.resolve().merge(feed);
        feedRef = new FeedReference(fDao.saveFeed(mergedFeed).getId());
        assertEquals("Same News was added twice!", 2, feedRef.resolve().getNews().size());

        List<INews> newsRefs = feedRef.resolve().getNews();
        for (INews newsRef : newsRefs) {
          if ("News_Case_8_Guid".equals(newsRef.getGuid().getValue()))
            assertEquals("Existing News State is not UPDATED!", INews.State.UPDATED, newsRef.getState());
        }
      }
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    }
  }

  /**
   * Test setting a News' state to deleted and then check wether the DB is
   * correctly deleting it completly from the DB, if no longer contained in the
   * Feed.
   *
   * @throws Exception
   */
  @Test
  public void testReallyDeleteNews() throws Exception {
    NewsListener newsListener = null;
    try {

      /* Add initial News */
      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      INews news1 = fFactory.createNews(null, feed, new Date());
      news1.setTitle("News1 Title");
      INews news2 = fFactory.createNews(null, feed, new Date());
      news2.setTitle("News2 Title");
      INews news3 = fFactory.createNews(null, feed, new Date());
      news3.setTitle("News3 Title");

      final URI news1Link = new URI("http://www.news1.com/index.html");
      final URI news2Link = new URI("http://www.news2.com/index.html");
      final URI news3Link = new URI("http://www.news3.com/index.html");
      news1.setLink(news1Link);
      news2.setLink(news2Link);
      news3.setLink(news3Link);
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      assertEquals(3, fDao.loadFeed(feedRef.getId()).getNews().size());

      /* Mark 2 News as Deleted and save News */
      news1 = fDao.loadFeed(feedRef.getId()).getNews().get(0);
      news1.setState(INews.State.DELETED);
      news2 = fDao.loadFeed(feedRef.getId()).getNews().get(1);
      news2.setState(INews.State.DELETED);
      news3 = fDao.loadFeed(feedRef.getId()).getNews().get(2);
      news3.setState(INews.State.READ);

      final boolean newsUpdatedEvents[] = new boolean[2];

      newsListener = new NewsAdapter() {
        @Override
        public void newsUpdated(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            INews news = event.getEntity();
            if (news.getLink().equals(news1Link))
              newsUpdatedEvents[0] = true;
            else if (news.getLink().equals(news2Link))
              newsUpdatedEvents[1] = true;
          }
        }
      };
      fModel.addNewsListener(newsListener);

      NewsReference newsReference1 = new NewsReference(fDao.saveNews(news1).getId());
      NewsReference newsReference2 = new NewsReference(fDao.saveNews(news2).getId());
      NewsReference newsReference3 = new NewsReference(fDao.saveNews(news3).getId());

      assertEquals(INews.State.DELETED, fDao.loadFeed(feedRef.getId()).getNews().get(0).getState());
      assertEquals(INews.State.DELETED, fDao.loadFeed(feedRef.getId()).getNews().get(1).getState());
      assertEquals(INews.State.READ, fDao.loadFeed(feedRef.getId()).getNews().get(2).getState());

      /* Check Deleted News now being Deleted from DB */
      feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      List<INews> removedNews = feedRef.resolve().mergeAndCleanUp(feed);
      fAppLayer.saveFeed(feed, removedNews);

      /* Asserts follow */
      assertEquals(1, fDao.loadFeed(feedRef.getId()).getNews().size());
      assertNull(fDao.loadNews(newsReference1.getId()));
      assertNull(fDao.loadNews(newsReference2.getId()));
      assertNotNull(fDao.loadNews(newsReference3.getId()));

      for (int i = 0; i < newsUpdatedEvents.length; i++)
        if (!newsUpdatedEvents[i])
          fail("Missing newsUpdated event in NewsListener!");
    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on Folder persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatFolderEvents() throws Exception {
    FolderListener folderListener = null;
    try {
      /* Add */
      final FolderReference rootFolder = new FolderReference(fDao.saveFolder(fFactory.createFolder(null, null, "Root")).getId());

      IFolder folder = fFactory.createFolder(null, rootFolder.resolve(), "Folder");
      final boolean folderEvents[] = new boolean[3];
      final FolderReference folderReference[] = new FolderReference[1];
      folderListener = new FolderListener() {
        boolean updateEventOccurred = false;

        public void folderAdded(Set<FolderEvent> events) {
          for (FolderEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            folderEvents[0] = true;
          }
        }

        public void folderDeleted(Set<FolderEvent> events) {
          for (FolderEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (folderReference[0].references(event.getEntity()))
              folderEvents[1] = true;
          }
        }

        public void folderUpdated(Set<FolderEvent> events) {
          for (FolderEvent event : events) {
            if (updateEventOccurred)
              return;

            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (folderReference[0].references(event.getEntity()))
              folderEvents[2] = true;

            updateEventOccurred = true;
          }
        }
      };
      fModel.addFolderListener(folderListener);
      folderReference[0] = new FolderReference(fDao.saveFolder(folder).getId());

      /* Update */
      folder = folderReference[0].resolve();
      folder.setName("Folder Updated");
      fDao.saveFolder(folder);

      /* Delete */
      fDao.deleteFolder(folderReference[0]);

      /* Asserts Follow */
      assertTrue("Missing folderAdded Event", folderEvents[0]);
      assertTrue("Missing folderUpdated Event", folderEvents[2]);
      assertTrue("Missing folderDeleted Event", folderEvents[1]);
    } finally {
      /* Cleanup */
      if (folderListener != null)
        fModel.removeFolderListener(folderListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on SearchMark persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatSearchMarkEvents() throws Exception {
    SearchMarkListener searchMarkListener = null;
    try {
      /* Add */
      final FolderReference folderRef = new FolderReference(fDao.saveFolder(fFactory.createFolder(null, null, "Folder")).getId());
      ISearchMark searchMark = fFactory.createSearchMark(null, folderRef.resolve(), "SearchMark");
      final boolean searchMarkEvents[] = new boolean[3];
      final SearchMarkReference searchMarkReference[] = new SearchMarkReference[1];
      searchMarkListener = new SearchMarkListener() {
        public void searchMarkAdded(Set<SearchMarkEvent> events) {
          for (SearchMarkEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(folderRef.getId(), event.getEntity().getFolder().getId());
            searchMarkEvents[0] = true;
          }
        }

        public void searchMarkDeleted(Set<SearchMarkEvent> events) {
          for (SearchMarkEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(folderRef.getId(), event.getEntity().getFolder().getId());
            if (searchMarkReference[0].references(event.getEntity()))
              searchMarkEvents[1] = true;
          }
        }

        public void searchMarkUpdated(Set<SearchMarkEvent> events) {
          for (SearchMarkEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(folderRef.getId(), event.getEntity().getFolder().getId());
            if (searchMarkReference[0].references(event.getEntity()))
              searchMarkEvents[2] = true;
          }
        }
      };
      fModel.addSearchMarkListener(searchMarkListener);
      searchMarkReference[0] = new SearchMarkReference(fDao.saveSearchMark(searchMark).getId());

      /* Update */
      searchMark = searchMarkReference[0].resolve();
      searchMark.setName("SearchMark Updated");
      fDao.saveSearchMark(searchMark);

      /* Delete */
      fDao.deleteSearchMark(searchMarkReference[0]);

      /* Asserts Follow */
      assertTrue("Missing searchMarkAdded Event", searchMarkEvents[0]);
      assertTrue("Missing searchMarkUpdated Event", searchMarkEvents[2]);
      assertTrue("Missing searchMarkDeleted Event", searchMarkEvents[1]);
    } finally {
      /* Cleanup */
      if (searchMarkListener != null)
        fModel.removeSearchMarkListener(searchMarkListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on SearchCondition persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatSearchConditionEvents() throws Exception {
    SearchConditionListener searchConditionListener = null;
    try {
      /* Add */
      FolderReference folderRef = new FolderReference(fDao.saveFolder(fFactory.createFolder(null, null, "Folder")).getId());
      SearchMarkReference searchMarkRef = new SearchMarkReference(fDao.saveSearchMark(fFactory.createSearchMark(null, folderRef.resolve(), "SearchMark")).getId());
      ISearchField field = fFactory.createSearchField(IExtendableType.ALL_FIELDS, INews.class);
      ISearchCondition searchCondition = fFactory.createSearchCondition(null, searchMarkRef.resolve(), field, SearchSpecifier.CONTAINS, "Foo", true);
      final boolean searchConditionEvents[] = new boolean[3];
      final SearchConditionReference searchConditionReference[] = new SearchConditionReference[1];
      searchConditionListener = new SearchConditionListener() {
        public void searchConditionAdded(Set<SearchConditionEvent> events) {
          for (SearchConditionEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            searchConditionEvents[0] = true;
          }
        }

        public void searchConditionDeleted(Set<SearchConditionEvent> events) {
          for (SearchConditionEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (searchConditionReference[0].references(event.getEntity()))
              searchConditionEvents[1] = true;
          }
        }

        public void searchConditionUpdated(Set<SearchConditionEvent> events) {
          for (SearchConditionEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (searchConditionReference[0].references(event.getEntity()))
              searchConditionEvents[2] = true;
          }
        }
      };
      fModel.addSearchConditionListener(searchConditionListener);
      searchConditionReference[0] = new SearchConditionReference(fDao.saveSearchCondition(searchCondition).getId());

      /* Update */
      searchCondition = searchConditionReference[0].resolve();
      searchCondition.setValue("Bar");
      searchCondition.setSpecifier(SearchSpecifier.CONTAINS_NOT);
      fDao.saveSearchCondition(searchCondition);

      /* Delete */
      fDao.deleteSearchCondition(searchConditionReference[0]);

      /* Asserts Follow */
      assertTrue("Missing searchConditionAdded Event", searchConditionEvents[0]);
      assertTrue("Missing searchConditionUpdated Event", searchConditionEvents[2]);
      assertTrue("Missing searchConditionDeleted Event", searchConditionEvents[1]);
     
    } finally {
      fModel.removeSearchConditionListener(searchConditionListener);
      if (searchConditionListener != null)
        fModel.removeSearchConditionListener(searchConditionListener);
    }
   
  }
 
  /**
   * Test added, updated and deleted Events sent on BookMark persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatBookMarkEvents() throws Exception {
    BookMarkListener bookMarkListener = null;
    try {
      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      /* Add */
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());
      final FolderReference folderRef = new FolderReference(fDao.saveFolder(fFactory.createFolder(null, null, "Folder")).getId());
      IBookMark bookMark = fFactory.createBookMark(null, folderRef.resolve(),
          feed.getLink(), feedRef, "BookMark");
      final boolean bookMarkEvents[] = new boolean[3];
      final BookMarkReference bookMarkReference[] = new BookMarkReference[1];
      bookMarkListener = new BookMarkListener() {
        public void bookMarkAdded(Set<BookMarkEvent> events) {
          for (BookMarkEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(folderRef.getId(), event.getEntity().getFolder().getId());
            bookMarkEvents[0] = true;
          }
        }

        public void bookMarkDeleted(Set<BookMarkEvent> events) {
          for (BookMarkEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(folderRef.getId(), event.getEntity().getFolder().getId());
            if (bookMarkReference[0].references(event.getEntity()))
              bookMarkEvents[1] = true;
          }
        }

        public void bookMarkUpdated(Set<BookMarkEvent> events) {
          for (BookMarkEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(folderRef.getId(), event.getEntity().getFolder().getId());
            if (bookMarkReference[0].references(event.getEntity()))
              bookMarkEvents[2] = true;
          }
        }
      };
      fModel.addBookMarkListener(bookMarkListener);
      bookMarkReference[0] = new BookMarkReference(fDao.saveBookMark(bookMark).getId());

      /* Update */
      bookMark = bookMarkReference[0].resolve();
      bookMark.setName("BookMark Updated");
      fDao.saveBookMark(bookMark);

      /* Delete */
      fDao.deleteBookMark(bookMarkReference[0]);

      /* Asserts Follow */
      assertTrue("Missing bookMarkAdded Event", bookMarkEvents[0]);
      assertTrue("Missing bookMarkUpdated Event", bookMarkEvents[2]);
      assertTrue("Missing bookMarkDeleted Event", bookMarkEvents[1]);

    } finally {
      /* Cleanup */
      if (bookMarkListener != null)
        fModel.removeBookMarkListener(bookMarkListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on Feed persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatFeedEvents() throws Exception {
    FeedListener feedListener = null;
    try {
      /* Add */
      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      final boolean feedEvents[] = new boolean[3];
      final FeedReference feedReference[] = new FeedReference[1];
      feedListener = new FeedListener() {
        public void feedAdded(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            feedEvents[0] = true;
          }
        }

        public void feedDeleted(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (feedReference[0].references(event.getEntity()))
              feedEvents[1] = true;
          }
        }

        public void feedUpdated(Set<FeedEvent> events) {
          for (FeedEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (feedReference[0].references(event.getEntity()))
              feedEvents[2] = true;
          }
        }
      };
      fModel.addFeedListener(feedListener);
      feedReference[0] = new FeedReference(fDao.saveFeed(feed).getId());

      /* Update */
      feed = feedReference[0].resolve();
      feed.setTitle("Feed Updated");
      fDao.saveFeed(feed);

      /* Delete */
      fDao.deleteFeed(feedReference[0]);

      /* Asserts Follow */
      assertTrue("Missing feedAdded Event", feedEvents[0]);
      assertTrue("Missing feedUpdated Event", feedEvents[2]);
      assertTrue("Missing feedDeleted Event", feedEvents[1]);

    } finally {
      /* Cleanup */
      if (feedListener != null)
        fModel.removeFeedListener(feedListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on News persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatNewsEvents() throws Exception {
    NewsListener newsListener = null;
    try {
      /* Add */
      final IFeed feed = fDao.saveFeed(fFactory.createFeed(null, new URL("http://www.feed.com")));
      INews news = fFactory.createNews(null, feed, new Date());
      news.setTitle("News");
      final boolean newsEvents[] = new boolean[3];
      final NewsReference newsReference[] = new NewsReference[1];
      newsListener = new NewsListener() {
        public void newsAdded(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(feed.getLink(), event.getEntity().getFeedReference().getLink());
            newsEvents[0] = true;
          }
        }

        public void newsDeleted(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(feed.getLink(), event.getEntity().getFeedReference().getLink());
            if (newsReference[0].references(event.getEntity()))
              newsEvents[1] = true;
          }
        }

        public void newsUpdated(Set<NewsEvent> events) {
          for (NewsEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            assertEquals(feed.getLink(), event.getEntity().getFeedReference().getLink());
            if (newsReference[0].references(event.getEntity()))
              newsEvents[2] = true;
          }
        }
      };
      fModel.addNewsListener(newsListener);
      newsReference[0] = new NewsReference(fDao.saveNews(news).getId());

      /* Update */
      news = newsReference[0].resolve();
      news.setTitle("News Updated");
      fDao.saveNews(news);

      /* Delete */
      fDao.deleteNews(newsReference[0]);

      /* Asserts Follow */
      assertTrue("Missing newsAdded Event", newsEvents[0]);
      assertTrue("Missing newsUpdated Event", newsEvents[2]);
      assertTrue("Missing newsDeleted Event", newsEvents[1]);

    } finally {
      /* Cleanup */
      if (newsListener != null)
        fModel.removeNewsListener(newsListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on Attachment persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatAttachmentEvents() throws Exception {
    AttachmentListener attachmentListener = null;
    try {
      /* Add */
      FeedReference feedRef = new FeedReference(fDao.saveFeed(fFactory.createFeed(null, new URL("http://www.feed1.com"))).getId());
      NewsReference newsRef = new NewsReference(fDao.saveNews(fFactory.createNews(null, feedRef.resolve(), new Date())).getId());
      IAttachment attachment = fFactory.createAttachment(null, newsRef.resolve());
      attachment.setUrl(new URI("http://www.attachment.com"));
      final boolean attachmentEvents[] = new boolean[3];
      final AttachmentReference attachmentReference[] = new AttachmentReference[1];
      attachmentListener = new AttachmentAdapter() {
        @Override
        public void attachmentAdded(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            attachmentEvents[0] = true;
          }
        }

        @Override
        public void attachmentDeleted(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (attachmentReference[0].references(event.getEntity()))
              attachmentEvents[1] = true;
          }
        }

        @Override
        public void attachmentUpdated(Set<AttachmentEvent> events) {
          for (AttachmentEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (attachmentReference[0].references(event.getEntity()))
              attachmentEvents[2] = true;
          }
        }
      };
      fModel.addAttachmentListener(attachmentListener);
      attachmentReference[0] = new AttachmentReference(fDao.saveAttachment(attachment).getId());

      /* Update */
      attachment = attachmentReference[0].resolve();
      attachment.setType("MP3");
      fDao.saveAttachment(attachment);

      /* Delete */
      fDao.deleteAttachment(attachmentReference[0]);

      /* Asserts Follow */
      assertTrue("Missing attachmentAdded Event", attachmentEvents[0]);
      assertTrue("Missing attachmentUpdated Event", attachmentEvents[2]);
      assertTrue("Missing attachmentDeleted Event", attachmentEvents[1]);

    } finally {
      /* Cleanup */
      if (attachmentListener != null)
        fModel.removeAttachmentListener(attachmentListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on Category persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatCategoryEvents() throws Exception {
    CategoryListener categoryListener = null;
    try {
      /* Add */
      FeedReference feedRef = new FeedReference(fDao.saveFeed(fFactory.createFeed(null, new URL("http://www.feed2.com"))).getId());
      NewsReference newsRef = new NewsReference(fDao.saveNews(fFactory.createNews(null, feedRef.resolve(), new Date())).getId());
      ICategory category1 = fFactory.createCategory(null, feedRef.resolve());
      category1.setName("Category");
      ICategory category2 = fFactory.createCategory(null, newsRef.resolve());
      category2.setName("Category");
      final boolean categoryEvents[] = new boolean[6];
      final CategoryReference categoryReference[] = new CategoryReference[2];
      categoryListener = new CategoryListener() {
        public void categoryAdded(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (categoryEvents[0])
              categoryEvents[1] = true;
            categoryEvents[0] = true;
          }
        }

        public void categoryDeleted(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (categoryReference[0].references(event.getEntity()))
              categoryEvents[2] = true;
            else if (categoryReference[1].references(event.getEntity()))
              categoryEvents[3] = true;
          }
        }

        public void categoryUpdated(Set<CategoryEvent> events) {
          for (CategoryEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (categoryReference[0].references(event.getEntity()))
              categoryEvents[4] = true;
            else if (categoryReference[1].references(event.getEntity()))
              categoryEvents[5] = true;
          }
        }
      };
      fModel.addCategoryListener(categoryListener);
      categoryReference[0] = new CategoryReference(fDao.saveCategory(category1).getId());
      categoryReference[1] = new CategoryReference(fDao.saveCategory(category2).getId());

      /* Update */
      category1 = categoryReference[0].resolve();
      category1.setName("Category Updated");
      category2 = categoryReference[1].resolve();
      category2.setName("Category Updated");
      fDao.saveCategory(category1);
      fDao.saveCategory(category2);

      /* Delete */
      fDao.deleteCategory(categoryReference[0]);
      fDao.deleteCategory(categoryReference[1]);

      /* Asserts Follow */
      assertTrue("Missing categoryAdded Event", categoryEvents[0]);
      assertTrue("Missing categoryAdded Event", categoryEvents[1]);
      assertTrue("Missing categoryUpdated Event", categoryEvents[4]);
      assertTrue("Missing categoryUpdated Event", categoryEvents[5]);
      assertTrue("Missing categoryDeleted Event", categoryEvents[2]);
      assertTrue("Missing categoryDeleted Event", categoryEvents[3]);

    } finally {
      /* Cleanup */
      if (categoryListener != null)
        fModel.removeCategoryListener(categoryListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on Person persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatPersonEvents() throws Exception {
    PersonListener personListener = null;
    try {
      /* Add */
      FeedReference feedRef = new FeedReference(fDao.saveFeed(fFactory.createFeed(null, new URL("http://www.feed4.com"))).getId());
      NewsReference newsRef = new NewsReference(fDao.saveNews(fFactory.createNews(null, feedRef.resolve(), new Date())).getId());
      IPerson person1 = fFactory.createPerson(null, feedRef.resolve());
      person1.setName("Person1");
      IPerson person2 = fFactory.createPerson(null, newsRef.resolve());
      person2.setName("Person2");
      final boolean personEvents[] = new boolean[6];
      final PersonReference personReference[] = new PersonReference[2];
      personListener = new PersonListener() {
        public void personAdded(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (personEvents[0])
              personEvents[1] = true;
            personEvents[0] = true;
          }
        }

        public void personDeleted(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (personReference[0].references(event.getEntity()))
              personEvents[2] = true;
            else if (personReference[1].references(event.getEntity()))
              personEvents[3] = true;
          }
        }

        public void personUpdated(Set<PersonEvent> events) {
          for (PersonEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (personReference[0].references(event.getEntity()))
              personEvents[4] = true;
            else if (personReference[1].references(event.getEntity()))
              personEvents[5] = true;
          }
        }
      };
      fModel.addPersonListener(personListener);
      personReference[0] = new PersonReference(fDao.savePerson(person1).getId());
      personReference[1] = new PersonReference(fDao.savePerson(person2).getId());

      /* Update */
      person1 = personReference[0].resolve();
      person1.setName("Person Updated");
      person2 = personReference[1].resolve();
      person2.setName("Person Updated");
      fDao.savePerson(person1);
      fDao.savePerson(person2);

      /* Delete */
      fDao.deletePerson(personReference[0]);
      fDao.deletePerson(personReference[1]);

      /* Asserts Follow */
      assertTrue("Missing personAdded Event", personEvents[0]);
      assertTrue("Missing personAdded Event", personEvents[1]);
      assertTrue("Missing personUpdated Event", personEvents[4]);
      assertTrue("Missing personUpdated Event", personEvents[5]);
      assertTrue("Missing personDeleted Event", personEvents[2]);
      assertTrue("Missing personDeleted Event", personEvents[3]);

    } finally {
      /* Cleanup */
      if (personListener != null)
        fModel.removePersonListener(personListener);
    }
  }
 
  /**
   * Test added, updated and deleted Events sent on Label persistence
   * operations
   *
   * @throws Exception
   */
  @Test
  public void testFlatLabelEvents() throws Exception {
    LabelListener labelListener = null;
    try {
      /* Add */
      ILabel label = fFactory.createLabel(null, "Label Name");
      final boolean labelEvents[] = new boolean[3];
      final LabelReference labelReference[] = new LabelReference[1];
      labelListener = new LabelListener() {
        public void labelAdded(Set<LabelEvent> events) {
          for (LabelEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            labelEvents[0] = true;
          }
        }

        public void labelDeleted(Set<LabelEvent> events) {
          for (LabelEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (labelReference[0].references(event.getEntity()))
              labelEvents[1] = true;
          }
        }

        public void labelUpdated(Set<LabelEvent> events) {
          for (LabelEvent event : events) {
            assertTrue("Expected this Event to be Root Event", event.isRoot());
            if (labelReference[0].references(event.getEntity()))
              labelEvents[2] = true;
          }
        }
      };
      fModel.addLabelListener(labelListener);
      labelReference[0] = new LabelReference(fDao.saveLabel(label).getId());

      /* Update */
      label = labelReference[0].resolve();
      label.setColor("255,255,128");
      fDao.saveLabel(label);

      /* Delete */
      fDao.deleteLabel(labelReference[0]);

      /* Asserts Follow */
      assertTrue("Missing labelAdded Event", labelEvents[0]);
      assertTrue("Missing labelUpdated Event", labelEvents[2]);
      assertTrue("Missing labelDeleted Event", labelEvents[1]);

    } finally {
      /* Cleanup */
      if (labelListener != null)
        fModel.removeLabelListener(labelListener);
    }
  }

  /**
   * Test adding Properties to Types.
   *
   * @throws Exception
   */
  @Test
  public void testTypeProperties() throws Exception {
    try {

      /* Add Properties to a Folder */
      IFolder folder = fFactory.createFolder(null, null, "Folder");
      folder.setProperty("String", "Foo");
      folder.setProperty("Integer", 1);
      folder.setProperty("Boolean", true);
      folder.setProperty("Double", 2.2D);
      folder.setProperty("Float", 3.3F);
      FolderReference folderRef = new FolderReference(fDao.saveFolder(folder).getId());
      folder = folderRef.resolve();
      assertEquals("Foo", folder.getProperty("String"));
      assertEquals(1, folder.getProperty("Integer"));
      assertEquals(true, folder.getProperty("Boolean"));
      assertEquals(2.2D, folder.getProperty("Double"));
      assertEquals(3.3F, folder.getProperty("Float"));

      /* Add Properties to a Feed */
      IFeed feed = fFactory.createFeed(null, new URL("http://www.myfeed.com"));
      feed.setProperty("String", "Foo");
      feed.setProperty("Integer", 1);
      feed.setProperty("Boolean", true);
      feed.setProperty("Double", 2.2D);
      feed.setProperty("Float", 3.3F);
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());
      feed = feedRef.resolve();
      assertEquals("Foo", feed.getProperty("String"));
      assertEquals(1, feed.getProperty("Integer"));
      assertEquals(true, feed.getProperty("Boolean"));
      assertEquals(2.2D, feed.getProperty("Double"));
      assertEquals(3.3F, feed.getProperty("Float"));

      /* Add Properties to a BookMark */
      IBookMark bookMark = fFactory.createBookMark(null, folderRef.resolve(),
          feed.getLink(), feedRef, "BookMark");
      bookMark.setProperty("String", "Foo");
      bookMark.setProperty("Integer", 1);
      bookMark.setProperty("Boolean", true);
      bookMark.setProperty("Double", 2.2D);
      bookMark.setProperty("Float", 3.3F);
      BookMarkReference bookMarkRef = new BookMarkReference(fDao.saveBookMark(bookMark).getId());
      bookMark = bookMarkRef.resolve();
      assertEquals("Foo", bookMark.getProperty("String"));
      assertEquals(1, bookMark.getProperty("Integer"));
      assertEquals(true, bookMark.getProperty("Boolean"));
      assertEquals(2.2D, bookMark.getProperty("Double"));
      assertEquals(3.3F, bookMark.getProperty("Float"));

      /* Add Properties to a News */
      INews news = fFactory.createNews(null, feedRef.resolve(), new Date());
      news.setProperty("String", "Foo");
      news.setProperty("Integer", 1);
      news.setProperty("Boolean", true);
      news.setProperty("Double", 2.2D);
      news.setProperty("Float", 3.3F);
      NewsReference newsRef = new NewsReference(fDao.saveNews(news).getId());
      news = newsRef.resolve();
      assertEquals("Foo", news.getProperty("String"));
      assertEquals(1, news.getProperty("Integer"));
      assertEquals(true, news.getProperty("Boolean"));
      assertEquals(2.2D, news.getProperty("Double"));
      assertEquals(3.3F, news.getProperty("Float"));

      /* Add Properties to an Attachment */
      IAttachment attachment = fFactory.createAttachment(null, newsRef.resolve());
      attachment.setUrl(new URI("http://www.attachment.com"));
      attachment.setProperty("String", "Foo");
      attachment.setProperty("Integer", 1);
      attachment.setProperty("Boolean", true);
      attachment.setProperty("Double", 2.2D);
      attachment.setProperty("Float", 3.3F);
      AttachmentReference attachmentRef = new AttachmentReference(fDao.saveAttachment(attachment).getId());
      attachment = attachmentRef.resolve();
      assertEquals("Foo", attachment.getProperty("String"));
      assertEquals(1, attachment.getProperty("Integer"));
      assertEquals(true, attachment.getProperty("Boolean"));
      assertEquals(2.2D, attachment.getProperty("Double"));
      assertEquals(3.3F, attachment.getProperty("Float"));

    } catch (PersistenceException e) {
      TestUtils.fail(e);
    }
  }

  /**
   * Test Adding, Deleting a Feed with no News.
   *
   * @throws Exception
   */
  @Test
  public void testAddDeleteFeedWithNoNews() throws Exception {
    NewsListener feedListener = null;
    try {
      IFeed feed = NewsModel.getDefault().getTypesFactory().createFeed(null, new URL("http://www.feed.com"));
      final boolean addedEvent[] = new boolean[1];
      final boolean deletedEvent[] = new boolean[1];

      feedListener = new NewsAdapter() {
        @Override
        public void newsAdded(Set<NewsEvent> events) {
          addedEvent[0] = true;
        }

        @Override
        public void newsDeleted(Set<NewsEvent> events) {
          deletedEvent[0] = true;
        }
      };
      NewsModel.getDefault().addNewsListener(feedListener);

      feed = NewsModel.getDefault().getPersistenceLayer().getModelDAO().saveFeed(feed);
      NewsModel.getDefault().getPersistenceLayer().getModelDAO().deleteFeed(new FeedReference(feed.getId()));

      if (addedEvent[0])
        fail("Unexpected newsAdded Event for Feed with 0 News");
      if (deletedEvent[0])
        fail("Unexpected newsDeleted Event for Feed with 0 News");

    } catch (PersistenceException e) {
      TestUtils.fail(e);
    } finally {
      if (feedListener != null)
        NewsModel.getDefault().removeNewsListener(feedListener);
    }
  }

  /**
   * @throws Exception
   */
  @SuppressWarnings("nls")
  @Test
  public void testSetNewsState() throws Exception {
    IFeed feed = fModel.getTypesFactory().createFeed(null, new URL("http://www.feed.com"));

    fModel.getTypesFactory().createNews(null, feed, new Date());
    fModel.getTypesFactory().createNews(null, feed, new Date());
    fModel.getTypesFactory().createNews(null, feed, new Date());

    FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

    NewsReference news1 = new NewsReference(feedRef.resolve().getNews().get(0).getId());
    NewsReference news2 = new NewsReference(feedRef.resolve().getNews().get(1).getId());
    NewsReference news3 = new NewsReference(feedRef.resolve().getNews().get(2).getId());

    List<NewsReference> news = new ArrayList<NewsReference>();
    news.add(news1);
    news.add(news2);

    assertEquals(news1.resolve().getState(), INews.State.NEW);
    assertEquals(news2.resolve().getState(), INews.State.NEW);
    assertEquals(news3.resolve().getState(), INews.State.NEW);

    for (NewsReference reference : news) {
      INews newsitem = reference.resolve();
      newsitem.setState(INews.State.UNREAD);
      fDao.saveNews(newsitem);
    }

    assertEquals(news1.resolve().getState(), INews.State.UNREAD);
    assertEquals(news2.resolve().getState(), INews.State.UNREAD);
    assertEquals(news3.resolve().getState(), INews.State.NEW);

    for (NewsReference reference : news) {
      INews newsitem = reference.resolve();
      newsitem.setState(INews.State.READ);
      fDao.saveNews(newsitem);
    }

    assertEquals(news1.resolve().getState(), INews.State.READ);
    assertEquals(news2.resolve().getState(), INews.State.READ);
    assertEquals(news3.resolve().getState(), INews.State.NEW);

    for (NewsReference reference : news) {
      INews newsitem = reference.resolve();
      newsitem.setState(INews.State.DELETED);
      fDao.saveNews(newsitem);
    }

    assertEquals(news1.resolve().getState(), INews.State.DELETED);
    assertEquals(news2.resolve().getState(), INews.State.DELETED);
    assertEquals(news3.resolve().getState(), INews.State.NEW);
  }

  /**
   * @throws Exception
   */
  @SuppressWarnings("nls")
  @Test
  public void testLoadNewsStates() throws Exception {
    IFeed feed = fModel.getTypesFactory().createFeed(null, new URL("http://www.feed.com"));
    FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

    for (int i = 0; i < 5; i++) {
      INews news = fModel.getTypesFactory().createNews(null, feed, new Date());
      fDao.saveNews(news);
      news.setState(INews.State.NEW);
      fDao.saveNews(news);
    }

    for (int i = 0; i < 4; i++) {
      INews news = fModel.getTypesFactory().createNews(null, feed, new Date());
      fDao.saveNews(news);
      news.setState(INews.State.UPDATED);
      fDao.saveNews(news);
    }

    for (int i = 0; i < 3; i++) {
      INews news = fModel.getTypesFactory().createNews(null, feed, new Date());
      fDao.saveNews(news);
      news.setState(INews.State.UNREAD);
      fDao.saveNews(news);
    }

    for (int i = 0; i < 2; i++) {
      INews news = fModel.getTypesFactory().createNews(null, feed, new Date());
      fDao.saveNews(news);
      news.setState(INews.State.READ);
      fDao.saveNews(news);
    }

    for (int i = 0; i < 1; i++) {
      INews news = fModel.getTypesFactory().createNews(null, feed, new Date());
      fDao.saveNews(news);
      news.setState(INews.State.HIDDEN);
      fDao.saveNews(news);
    }

    int newCount = 0, updatedCount = 0, unreadCount = 0, readCount = 0, hiddenCount = 0;

    List<State> states = new ArrayList<State>();

    feed = feedRef.resolve();
    List<INews> news = feed.getNews();

    for (INews newsitem : news) {
      states.add(newsitem.getState());
    }

    for (State state : states) {
      if (state == INews.State.NEW)
        newCount++;
      else if (state == INews.State.UPDATED)
        updatedCount++;
      else if (state == INews.State.UNREAD)
        unreadCount++;
      else if (state == INews.State.READ)
        readCount++;
      else if (state == INews.State.HIDDEN)
        hiddenCount++;
    }

    assertEquals(newCount, 5);
    assertEquals(updatedCount, 4);
    assertEquals(unreadCount, 3);
    assertEquals(readCount, 2);
    assertEquals(hiddenCount, 1);
  }

  /**
   * @throws Exception
   */
  @SuppressWarnings("nls")
  @Test
  public void testMergeMarks() throws Exception {

    /* Merge BookMarks */
    {
      IFolder folder = fDao.saveFolder(fFactory.createFolder(null, null, "Root"));
      IFeed feed = fDao.saveFeed(fFactory.createFeed(null, new URL("http://www.link.de")));
      IBookMark bookmark = fDao.saveBookMark(fFactory.createBookMark(null, folder,
          feed.getLink(), new FeedReference(feed.getId()), "BookMark"));

      /* Create BookMark to merge into existing BookMark */
      IBookMark updatedBookMark1 = fFactory.createBookMark(bookmark.getId(), folder,
          feed.getLink(), new FeedReference(feed.getId()), "BookMark *updated*");

      IBookMark updatedBookMark2 = fFactory.createBookMark(bookmark.getId(), folder,
          feed.getLink(), new FeedReference(feed.getId()), "BookMark *updated*");
      updatedBookMark2.setCreationDate(new Date(System.currentTimeMillis() + 1000));
      updatedBookMark2.setErrorLoading(true);
      updatedBookMark2.setLastVisitDate(new Date(System.currentTimeMillis()));
      updatedBookMark2.setPopularity(100);
      updatedBookMark2.setProperty("Foo", "Bar");

      IFeed feed2 = fDao.saveFeed(fFactory.createFeed(null, new URL("http://www.link2.de")));
      IBookMark updatedBookMark3 = fFactory.createBookMark(bookmark.getId(), folder,
          feed2.getLink(), new FeedReference(feed2.getId()), "BookMark *updated*");

      /* Merge and Test */
      bookmark.merge(updatedBookMark1);
      assertTrue(((BookMark) bookmark).isIdentical(updatedBookMark1));

      bookmark.merge(updatedBookMark2);
      assertTrue(((BookMark) bookmark).isIdentical(updatedBookMark2));

      bookmark.merge(updatedBookMark3);
      assertTrue(((BookMark) bookmark).isIdentical(updatedBookMark3));
    }

    /* Merge SearchMarks */
    {
      IFolder folder = fDao.saveFolder(fFactory.createFolder(null, null, "Root"));

      ISearchMark searchmark = NewsModel.getDefault().getTypesFactory().createSearchMark(10L, folder, "SearchMark");
      ISearchField field = fFactory.createSearchField(INews.STATE, INews.class);
      fFactory.createSearchCondition(11L, searchmark, field, SearchSpecifier.IS, State.HIDDEN.toString(), false);

      /* Create Searchmarks to merge with existing */
      ISearchMark searchmark1 = NewsModel.getDefault().getTypesFactory().createSearchMark(10L, folder, "SearchMark *updated*");
      field = fFactory.createSearchField(INews.STATE, INews.class);
      fFactory.createSearchCondition(11L, searchmark1, field, SearchSpecifier.IS, State.HIDDEN.toString(), false);

      ISearchMark searchmark2 = NewsModel.getDefault().getTypesFactory().createSearchMark(10L, folder, "SearchMark *updated*");
      field = fFactory.createSearchField(IFeed.AUTHOR, IFeed.class);
      fFactory.createSearchCondition(11L, searchmark2, field, SearchSpecifier.IS, "bpasero", true);

      ISearchMark searchmark3 = NewsModel.getDefault().getTypesFactory().createSearchMark(10L, folder, "SearchMark *updated*");
      field = fFactory.createSearchField(IFeed.TITLE, IFeed.class);
      fFactory.createSearchCondition(11L, searchmark3, field, SearchSpecifier.CONTAINS, "foo", true);
      field = fFactory.createSearchField(IFeed.COPYRIGHT, IFeed.class);
      fFactory.createSearchCondition(12L, searchmark3, field, SearchSpecifier.ENDS_WITH, "(c)", false);

      ISearchMark searchmark4 = NewsModel.getDefault().getTypesFactory().createSearchMark(10L, folder, "SearchMark *updated*");
      field = fFactory.createSearchField(IFeed.TITLE, IFeed.class);
      fFactory.createSearchCondition(11L, searchmark4, field, SearchSpecifier.CONTAINS, "bar", false);
      field = fFactory.createSearchField(INews.STATE, INews.class);
      fFactory.createSearchCondition(12L, searchmark4, field, SearchSpecifier.IS_NOT, INews.State.READ.toString(), false);

      /* Merge and Test */
      searchmark.merge(searchmark1);
      assertTrue(((SearchMark) searchmark).isIdentical(searchmark1));

      searchmark.merge(searchmark2);
      assertTrue(((SearchMark) searchmark).isIdentical(searchmark2));

      searchmark.merge(searchmark3);
      assertTrue(((SearchMark) searchmark).isIdentical(searchmark3));

      searchmark.merge(searchmark4);
      assertTrue(((SearchMark) searchmark).isIdentical(searchmark4));
    }
  }

  /**
   * @throws Exception
   */
  @SuppressWarnings("nls")
  @Test
  public void testDeleteTypeFromDeleteParent() throws Exception {

    /* Folder, BookMark, Feed, News (Folder Deleted) */
    {
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);

      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      IBookMark mark = fFactory.createBookMark(null, root, feed.getLink(), feedRef, "BookMark");
      root = fDao.saveFolder(root);
      mark = (IBookMark) root.getMarks().get(0);

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      NewsReference newsRef = new NewsReference(feedRef.resolve().getNews().get(0).getId());

      fDao.deleteFolder(new FolderReference(root.getId()));

      assertNull("Expected this Entity to be NULL", new FolderReference(root.getId()).resolve());
      assertNull("Expected this Entity to be NULL", new BookMarkReference(mark.getId()).resolve());
      assertNull("Expected this Entity to be NULL", feedRef.resolve());
      assertNull("Expected this Entity to be NULL", newsRef.resolve());
    }

    /* Root Folder, Folder, BookMark, Feed, News (Folder Deleted) */
    {
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);

      IFolder folder = fFactory.createFolder(null, root, "Folder");
      folder = fDao.saveFolder(folder);

      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed2.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      IBookMark mark = fFactory.createBookMark(null, folder, feed.getLink(),
          feedRef, "BookMark");
      folder = fDao.saveFolder(folder);
      mark = (IBookMark) folder.getMarks().get(0);

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      NewsReference newsRef = new NewsReference(feedRef.resolve().getNews().get(0).getId());

      fDao.deleteFolder(new FolderReference(folder.getId()));

      assertNull("Expected this Entity to be NULL", new FolderReference(folder.getId()).resolve());
      assertNull("Expected this Entity to be NULL", new BookMarkReference(mark.getId()).resolve());
      assertNull("Expected this Entity to be NULL", feedRef.resolve());
      assertNull("Expected this Entity to be NULL", newsRef.resolve());
    }

    /* Root Folder, Folder, BookMark, Feed, News (Folder Deleted #2) */
    {
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);

      IFolder folder = fFactory.createFolder(null, root, "Folder");
      folder = fDao.saveFolder(folder);

      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed3.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      IBookMark mark = fFactory.createBookMark(null, folder, feed.getLink(),
          feedRef, "BookMark");
      folder = fDao.saveFolder(folder);
      mark = (IBookMark) folder.getMarks().get(0);

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      NewsReference newsRef = new NewsReference(feedRef.resolve().getNews().get(0).getId());

      /* Delete by calling delete */
      fDao.deleteFolder(new FolderReference(folder.getId()));

      final long rootFolderId = root.getId();
      FolderListener folderListener = new FolderAdapter() {
        @Override
        public void folderUpdated(Set<FolderEvent> events) {
          for (FolderEvent event : events) {
            if (event.getEntity().getId() == rootFolderId)
              assertTrue(event.isRoot());
            else
              assertFalse(event.isRoot());
          }
        }
      };
      NewsModel.getDefault().addFolderListener(folderListener);
      try {
        fDao.saveFolder(root);
      } finally {
        NewsModel.getDefault().removeFolderListener(folderListener);
      }

      assertNull("Expected this Entity to be NULL", new FolderReference(folder.getId()).resolve());
      assertNull("Expected this Entity to be NULL", new BookMarkReference(mark.getId()).resolve());
      assertNull("Expected this Entity to be NULL", feedRef.resolve());
      assertNull("Expected this Entity to be NULL", newsRef.resolve());
    }

    /* Folder, BookMark, Feed, News (BookMark Deleted) */
    {
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);

      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed4.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      IBookMark mark = fFactory.createBookMark(null, root, feed.getLink(), feedRef, "BookMark");
      root = fDao.saveFolder(root);
      mark = (IBookMark) root.getMarks().get(0);

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      NewsReference newsRef = new NewsReference(feedRef.resolve().getNews().get(0).getId());

      fDao.deleteBookMark(new BookMarkReference(mark.getId()));

      assertNull("Expected this Entity to be NULL", new BookMarkReference(mark.getId()).resolve());
      assertNull("Expected this Entity to be NULL", feedRef.resolve());
      assertNull("Expected this Entity to be NULL", newsRef.resolve());
    }

    /* Folder, BookMark, Feed, News (BookMark Deleted #2) */
    {
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);

      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed5.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      IBookMark mark = fFactory.createBookMark(null, root, feed.getLink(), feedRef, "BookMark");
      root = fDao.saveFolder(root);
      mark = (IBookMark) root.getMarks().get(0);

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      NewsReference newsRef = new NewsReference(feedRef.resolve().getNews().get(0).getId());

      /* Delete by calling delete */
      fDao.deleteBookMark(new BookMarkReference(mark.getId()));

      assertNull("Expected this Entity to be NULL", new BookMarkReference(mark.getId()).resolve());
      assertNull("Expected this Entity to be NULL", feedRef.resolve());
      assertNull("Expected this Entity to be NULL", newsRef.resolve());
    }

    /* Feed, News (Feed Deleted) */
    {
      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed6.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      NewsReference newsRef = new NewsReference(feedRef.resolve().getNews().get(0).getId());

      fDao.deleteFeed(feedRef);

      assertNull("Expected this Entity to be NULL", feedRef.resolve());
      assertNull("Expected this Entity to be NULL", newsRef.resolve());
    }
  }

  /**
   * @throws Exception
   */
  @SuppressWarnings("nls")
  @Test
  public void testNoUpdateEventForDeletedChildsOfSavedParent() throws Exception {
    FolderAdapter folderListener= null;

    try {
      IFolder root = fFactory.createFolder(null, null, "Root");
      root = fDao.saveFolder(root);

      IFolder folder1 = fFactory.createFolder(null, root, "Folder #1");
      root = fDao.saveFolder(root);
      folder1 = root.getFolders().get(0);
     
      IFolder folder2 =fFactory.createFolder(null, root, "Folder #2");
      root = fDao.saveFolder(root);
      folder2 = root.getFolders().get(1);

      IFeed feed = fFactory.createFeed(null, new URL("http://www.feed.com"));
      fFactory.createNews(null, feed, new Date());
      FeedReference feedRef = new FeedReference(fDao.saveFeed(feed).getId());

      fFactory.createBookMark(null, folder1, feed.getLink(), feedRef, "BookMark");
      folder1 = fDao.saveFolder(folder1);

      assertEquals(1, new FeedReference(feed.getId()).resolve().getNews().size());

      folderListener= new FolderAdapter() {
        @Override
        public void folderUpdated(Set<FolderEvent> events) {
          for (FolderEvent folderEvent : events) {
            IFolder folder = folderEvent.getEntity();
            if (folder.getName().startsWith("Folder"))
              fail("Unexpected Event");
          }
        }
      };
     
      fModel.addFolderListener(folderListener);
     
      root.removeFolder(folder1);
      root.removeFolder(folder2);
      fDao.saveFolder(root);
    } finally {
      if (folderListener != null)
        fModel.removeFolderListener(folderListener);
    }
  }
}
TOP

Related Classes of org.rssowl.core.tests.model.ModelTest

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.