Package org.rssowl.ui.internal

Source Code of org.rssowl.ui.internal.FolderNewsMark$FolderNewsMarkReference

/*   **********************************************************************  **
**   Copyright notice                                                       **
**                                                                          **
**   (c) 2005-2009 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.ui.internal;

import org.eclipse.core.runtime.IProgressMonitor;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.persist.LongArrayList;
import org.rssowl.core.internal.persist.Mark;
import org.rssowl.core.internal.persist.NewsContainer;
import org.rssowl.core.internal.persist.SearchMark;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IFolder;
import org.rssowl.core.persist.IFolderChild;
import org.rssowl.core.persist.IModelFactory;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INews.State;
import org.rssowl.core.persist.INewsBin;
import org.rssowl.core.persist.INewsMark;
import org.rssowl.core.persist.ISearchCondition;
import org.rssowl.core.persist.ISearchField;
import org.rssowl.core.persist.ISearchMark;
import org.rssowl.core.persist.SearchSpecifier;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.dao.INewsDAO;
import org.rssowl.core.persist.reference.FeedLinkReference;
import org.rssowl.core.persist.reference.ModelReference;
import org.rssowl.core.persist.reference.NewsReference;
import org.rssowl.core.persist.service.IModelSearch;
import org.rssowl.core.persist.service.PersistenceException;
import org.rssowl.core.util.SearchHit;
import org.rssowl.ui.internal.editors.feed.NewsFilter;
import org.rssowl.ui.internal.editors.feed.NewsFilter.Type;
import org.rssowl.ui.internal.util.ModelUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* An internal subclass of {@link Mark} that implements {@link INewsMark} to
* provide the news of all bookmarks, bins and saved searches inside a folder.
* The {@link FolderNewsMark} is created dynamically whenever a folder is opened
* in the feedview and is never persisted to the DB.
*
* @author bpasero
*/
@SuppressWarnings("restriction")
public class FolderNewsMark extends Mark implements INewsMark {
  private final Set<Long> fNewsContainer;
  private final IFolder fFolder;
  private final INewsDAO fNewsDao;
  private final IModelFactory fFactory;
  private final IModelSearch fSearch;

  /**
   * Internal implementation of the <code>ModelReference</code> for the internal
   * Type <code>FolderNewsMark</code>.
   *
   * @author bpasero
   */
  public static final class FolderNewsMarkReference extends ModelReference {

    /**
     * @param id
     */
    public FolderNewsMarkReference(long id) {
      super(id, FolderNewsMark.class);
    }

    @Override
    public IFolder resolve() throws PersistenceException {
      throw new UnsupportedOperationException();
    }
  }

  /**
   * @param folder the {@link IFolder} that makes the contents of this
   * {@link INewsMark}.
   */
  public FolderNewsMark(IFolder folder) {
    super(folder.getId(), folder, folder.getName());
    fFolder = folder;
    fNewsContainer = new HashSet<Long>();
    fNewsDao = DynamicDAO.getDAO(INewsDAO.class);
    fFactory = Owl.getModelFactory();
    fSearch = Owl.getPersistenceService().getModelSearch();
  }

  /**
   * @param news the {@link List} of {@link INews} to add into this news mark.
   */
  public void add(Collection<INews> news) {
    synchronized (this) {
      for (INews item : news) {
        if (item != null && item.getId() != null)
          fNewsContainer.add(item.getId());
      }
    }
  }

  private void addAll(long[] elements) {
    synchronized (this) {
      for (long element : elements) {
        if (element > 0)
          fNewsContainer.add(element);
      }
    }
  }

  private void addAll(List<SearchHit<NewsReference>> results) {
    synchronized (this) {
      for (SearchHit<NewsReference> result : results) {
        fNewsContainer.add(result.getResult().getId());
      }
    }
  }

  /**
   * @param news the {@link List} of {@link INews} to remove from this news
   * mark.
   */
  public void remove(Collection<INews> news) {
    synchronized (this) {
      for (INews item : news) {
        if (item != null && item.getId() != null) {
          fNewsContainer.remove(item.getId());
        }
      }
    }
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#getNewsRefs()
   */
  public List<NewsReference> getNewsRefs() {
    synchronized (this) {
      List<NewsReference> news = new ArrayList<NewsReference>(fNewsContainer.size());
      for (Long id : fNewsContainer) {
        news.add(new NewsReference(id));
      }

      return news;
    }
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#getNewsRefs(java.util.Set)
   */
  public List<NewsReference> getNewsRefs(Set<State> states) {
    return getNewsRefs();
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#getNewsCount(java.util.Set)
   */
  public int getNewsCount(Set<State> states) {
    synchronized (this) {
      return fNewsContainer.size();
    }
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#containsNews(org.rssowl.core.persist.INews)
   */
  public boolean containsNews(INews news) {
    return news.getId() != null && containsNews(news.getId());
  }

  /**
   * @param newsId the identifier of the news.
   * @return <code>true</code> if this news mark contains the news and
   * <code>false</code> otherwise.
   */
  public boolean containsNews(long newsId) {
    synchronized (this) {
      return fNewsContainer.contains(newsId);
    }
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#getNews()
   */
  public List<INews> getNews() {
    synchronized (this) {
      List<INews> news = new ArrayList<INews>(fNewsContainer.size());
      for (Long id : fNewsContainer) {
        INews item = fNewsDao.load(id);
        if (item != null)
          news.add(item);
      }

      return news;
    }
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#getNews(java.util.Set)
   */
  public List<INews> getNews(Set<State> states) {
    return getNews();
  }

  /*
   * @see org.rssowl.core.persist.INewsMark#isGetNewsRefsEfficient()
   */
  public boolean isGetNewsRefsEfficient() {
    return true;
  }

  /*
   * @see org.rssowl.core.persist.IEntity#toReference()
   */
  public ModelReference toReference() {
    return new FolderNewsMarkReference(getId());
  }

  /**
   * @return the {@link IFolder} that serves as input to this {@link INewsMark}.
   */
  public IFolder getFolder() {
    return fFolder;
  }

  /*
   * @see org.rssowl.core.internal.persist.AbstractEntity#setProperty(java.lang.String, java.io.Serializable)
   */
  @Override
  public synchronized void setProperty(String key, Serializable value) {
    fFolder.setProperty(key, value);
  }

  /*
   * @see org.rssowl.core.internal.persist.Mark#getName()
   */
  @Override
  public synchronized String getName() {
    return fFolder.getName();
  }

  /*
   * @see org.rssowl.core.internal.persist.AbstractEntity#getProperties()
   */
  @Override
  public synchronized Map<String, Serializable> getProperties() {
    return fFolder.getProperties();
  }

  /*
   * @see org.rssowl.core.internal.persist.AbstractEntity#getProperty(java.lang.String)
   */
  @Override
  public synchronized Object getProperty(String key) {
    return fFolder.getProperty(key);
  }

  /*
   * @see org.rssowl.core.internal.persist.Mark#getParent()
   */
  /*
   * @see org.rssowl.core.internal.persist.Mark#getParent()
   */
  @Override
  public synchronized IFolder getParent() {
    return fFolder.getParent();
  }

  /**
   * @param mark the {@link INewsMark} to search in this folder.
   * @return <code>true</code> if this folder contains the given
   * {@link INewsMark} and <code>false</code> otherwise.
   */
  public boolean contains(INewsMark mark) {
    return contains(fFolder, mark);
  }

  private boolean contains(IFolder folder, INewsMark mark) {
    List<IFolderChild> children = folder.getChildren();
    for (IFolderChild child : children) {
      if (child instanceof IFolder && contains((IFolder) child, mark))
        return true;

      if (child.equals(mark))
        return true;
    }

    return false;
  }

  /**
   * @param searchMark the search to find out if its contained in this folder.
   * @return <code>true</code> if the given Search is contained in the
   * {@link IFolder} of this news mark and <code>false</code> otherwise.
   */
  public boolean isRelatedTo(ISearchMark searchMark) {
    return isRelatedTo(fFolder, searchMark);
  }

  private boolean isRelatedTo(IFolder folder, ISearchMark searchMark) {
    List<IFolderChild> children = folder.getChildren();

    for (IFolderChild child : children) {

      /* Check contained in Folder */
      if (child instanceof IFolder && isRelatedTo((IFolder) child, searchMark))
        return true;

      /* Check identical to Child */
      else if (child.equals(searchMark))
        return true;
    }

    return false;
  }

  /**
   * @param news the news to check if its related to this news mark.
   * @return <code>true</code> if the given News belongs to any
   * {@link IBookMark} or {@link INewsBin} of the given {@link IFolder}.
   */
  public boolean isRelatedTo(INews news) {

    /* Check News Container first */
    if (containsNews(news))
      return true;

    /* Then check by Feedlink for new news yet unknown to the news mark */
    FeedLinkReference feedRef = news.getFeedReference();
    return isRelatedTo(fFolder, news, feedRef);
  }

  private boolean isRelatedTo(IFolder folder, INews news, FeedLinkReference ref) {
    List<IFolderChild> children = folder.getChildren();
    for (IFolderChild child : children) {

      /* Check contained in Folder */
      if (child instanceof IFolder && isRelatedTo((IFolder) child, news, ref))
        return true;

      /* News could be part of the Feed (but is no copy) */
      else if (news.getParentId() == 0 && child instanceof IBookMark) {
        IBookMark bookmark = (IBookMark) child;
        if (bookmark.getFeedLinkReference().equals(ref))
          return true;
      }

      /* News could be part of Bin (and is a copy) */
      else if (news.getParentId() != 0 && child instanceof INewsBin) {
        INewsBin bin = (INewsBin) child;
        if (bin.getId() == news.getParentId())
          return true;
      }
    }

    return false;
  }

  /**
   * @param type the filter type to scope the resulting news properly.
   * @param monitor a monitor to react to cancellation.
   */
  public void resolve(NewsFilter.Type type, IProgressMonitor monitor) {
    doResolve(type, monitor, false);
  }

  /**
   * @param monitor a monitor to react to cancellation.
   * @return a {@link NewsContainer} providing information on included states
   * and news of this {@link IFolder}.
   */
  public NewsContainer resolveNewsContainer(IProgressMonitor monitor) {
    return doResolve(NewsFilter.Type.SHOW_ALL, monitor, true);
  }

  private NewsContainer doResolve(NewsFilter.Type type, IProgressMonitor monitor, boolean resolveContainer) {
    NewsContainer container = null;
    if (resolveContainer)
      container = new NewsContainer(Collections.<INews.State, Boolean> emptyMap());

    /* Clear caches */
    synchronized (this) {
      fNewsContainer.clear();
    }

    /* Return eary on cancellation */
    if (Controller.getDefault().isShuttingDown() || (monitor != null && monitor.isCanceled()))
      return container;

    /* Retrieve filter condition */
    ISearchCondition filterCondition = ModelUtils.getConditionForFilter(type);

    /* Resolve Bookmarks and Newsbins using Location search */
    {
      List<ISearchCondition> conditions = new ArrayList<ISearchCondition>(2);
      ISearchField field = fFactory.createSearchField(INews.LOCATION, INews.class.getName());
      ISearchCondition locationCondition = fFactory.createSearchCondition(field, SearchSpecifier.IS, ModelUtils.toPrimitive(Collections.singleton((IFolderChild) fFolder)));
      conditions.add(locationCondition);
      if (filterCondition != null)
        conditions.add(filterCondition);

      List<SearchHit<NewsReference>> results = fSearch.searchNews(conditions, conditions.size() == 2);
      addAll(results);

      /* Add to container if necessary */
      if (resolveContainer)
        addAll(container, results);

      /* Return eary on cancellation */
      if (Controller.getDefault().isShuttingDown() || (monitor != null && monitor.isCanceled()))
        return container;
    }

    /* Resolve Searches */
    {
      Set<ISearchMark> searches = new HashSet<ISearchMark>();
      findSearches(fFolder, searches);
      for (ISearchMark search : searches) {

        /* Inject the filter condition into the search if it is not an OR query or only 1 condition specified */
        if ((type == Type.SHOW_RECENT || type == Type.SHOW_LAST_5_DAYS || type == Type.SHOW_STICKY || type == Type.SHOW_LABELED)) {
          List<ISearchCondition> conditions = search.getSearchConditions();
          List<SearchHit<NewsReference>> results = fSearch.searchNews(conditions, filterCondition, search.matchAllConditions());
          addAll(results);

          /* Add to container if necessary */
          if (resolveContainer)
            addAll(container, results);
        }

        /* Otherwise pick up the results from the search directly */
        else {
          LongArrayList[] newsIds = ((SearchMark) search).internalGetNewsContainer().internalGetNewsIds();
          for (int i = 0; i < newsIds.length; i++) {

            /* Ignore hidden/deleted and states that are filtered */
            if (i == INews.State.HIDDEN.ordinal() || i == INews.State.DELETED.ordinal())
              continue;
            else if (type == Type.SHOW_NEW && i != INews.State.NEW.ordinal())
              continue;
            else if (type == Type.SHOW_UNREAD && i == INews.State.READ.ordinal())
              continue;

            addAll(newsIds[i].getElements());
          }

          /* Add to container if necessary */
          if (resolveContainer)
            addAll(container, newsIds);
        }

        /* Return eary on cancellation */
        if (Controller.getDefault().isShuttingDown() || (monitor != null && monitor.isCanceled()))
          return container;
      }
    }

    return container;
  }

  private void addAll(NewsContainer container, LongArrayList[] newsIds) {
    for (int i = 0; i < newsIds.length && i < container.internalGetNewsIds().length; i++) {
      LongArrayList list = container.internalGetNewsIds()[i];
      for (int j = 0; j < newsIds[i].size(); j++)
        list.add(newsIds[i].get(j));
    }
  }

  private void addAll(NewsContainer container, List<SearchHit<NewsReference>> results) {
    LongArrayList[] newsIds = container.internalGetNewsIds();
    for (SearchHit<NewsReference> result : results) {
      INews.State state = (State) result.getData(INews.STATE);
      if (newsIds.length > state.ordinal())
        newsIds[state.ordinal()].add(result.getResult().getId());
    }
  }

  private void findSearches(IFolder folder, Set<ISearchMark> searches) {
    List<IFolderChild> children = folder.getChildren();
    for (IFolderChild child : children) {
      if (child instanceof IFolder)
        findSearches((IFolder) child, searches);
      else if (child instanceof ISearchMark)
        searches.add((ISearchMark) child);
    }
  }
}
TOP

Related Classes of org.rssowl.ui.internal.FolderNewsMark$FolderNewsMarkReference

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.